1

我有一个由字典组成的列表。我希望对列表进行子集化,根据元素值的比较来选择字典(在这种情况下,每个日期只选择一个字典,选择的字典是具有最大值的字典realtime_start)。

一个示例列表是:

obs = [{'date': '2012-10-01',
  'realtime_end': '2013-02-18',
  'realtime_start': '2012-11-15',
  'value': '231.751'},
 {'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '2013-02-18',
  'realtime_start': '2012-12-14',
  'value': '231.025'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '2013-02-18',
  'realtime_start': '2013-01-16',
  'value': '230.979'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-19',
  'value': '231.137'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-19',
  'value': '231.197'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-21',
  'value': '231.198'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-21',
  'value': '231.222'}]

我希望对列表进行子集化,使其每个日期只包含一个字典,并选择具有最大值的字典realtime_start

在这种情况下,在列表被子集化后,它将是:

sub = [ {'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-19',
  'value': '231.197'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-21',
  'value': '231.222'}]

此外,假设我指定了一个最大日期:

maxDate = "2013-02-21"

我将如何进行子集化以使该realtime_start大于 maxDate?在这种情况下,我期望以下子集:

sub2 = [ {'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-19',
  'value': '231.137'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-21',
  'value': '231.198'} ]

我将如何在 Python 2.7.3 中编写这样的子集操作?这在 Python 中可能吗?

谢谢

4

2 回答 2

4

你可以使用itertools.groupby

>>> import itertools
>>> # sort so that the same dates are contiguous
>>> obs.sort(key=lambda x: x['date'])
>>> grouped = itertools.groupby(obs, lambda x: x['date'])
>>> m = [max(g, key=lambda x: x['realtime_start']) for k, g in grouped]
>>> 
>>> import pprint
>>> pprint.pprint(m)
[{'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-19',
  'value': '231.197'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-21',
  'value': '231.222'}]

您也可以添加其他条件:

>>> grouped = itertools.groupby(obs, lambda x: x['date'])
>>> m = [max((w for w in g if w['realtime_start'] <= maxDate),
         key=lambda x: x['realtime_start']) for k, g in grouped]
>>> pprint.pprint(m)
[{'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-19',
  'value': '231.137'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-21',
  'value': '231.198'}]

但我建议查看我最喜欢的 Python 数据操作库pandas:它非常适合用于表格和时间序列数据,并且使用它进行数据操作会比你自己滚动的任何东西更容易(并且在功能上更类似于 R) .

于 2013-03-02T23:51:58.403 回答
1

您基本上希望按date字段对条目进行分组,然后对与每个dates 关联的条目组执行操作。我做这种事情的方式是使用普通的 ol' dict。在这种情况下,我认为 adict是一种特殊的set——一个“装饰集”,如果你愿意的话——它的每个(必要的可散列的)元素都被一些(通常是不可散列的)有效载荷“装饰”(即关联的字典值)。date在您的示例中,此“装饰集”的每个元素都是中所有 dicts 中该字段的可能值之一obs,并且其关联的有效负载是以该obs键作为其date字段的所有 dicts 的列表。

因此,

In [4]: dobs = dict()
In [5]: for o in obs:
   ...:     d = o['date']
   ...:     if d not in dobs:
   ...:         dobs[d] = []
   ...:     dobs[d].append(o)
   ...: 

可以使用更简洁地dict.setdefault编写for-loop 的主体,如下所示:

In [7]: for o in obs:
   ...:     dobs.setdefault(o['date'], []).append(o)
   ...: 

或者可以用空列表预加载字典,然后只附加到它们而不需要检查键是否已经在字典中:

In [9]: dobs = dict([(d, []) for d in set([e['date'] for e in obs])])
In [10]: for o in obs:
   ....:     dobs[o['date']].append(o)
   ....: 

在上述任何一项之后,您最终将得到一个字典,dobs其键是date',其值是其中具有相应键作为值的所有字典的列表obsdate

现在你可以带着这个 dict 去城里,对它的值应用任何类型的函数。例如,要为每个 提取date具有最新的 dict realtime_start,您可以这样做:

In [11]: rts = lambda x: x['realtime_start']
In [12]: [sorted(e, key=rts)[-1] for e in dobs.values() if e]
Out[12]: 
[{'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-21',
  'value': '231.222'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-19',
  'value': '231.197'},
 {'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'}]

if e上面理解末尾的限定符在这里不是必需的,但我以“防御性编程”的名义包含它。没有它,如果其中的任何值dobs恰好为空,上面的代码将失败。我们知道情况并非如此dobs,但在更一般的环境中可能会成为一个问题。下面有更多关于这个的信息。)

realtime_start您还询问如何在设置上限时执行上述选择2013-02-21。对于这个问题,我发现将问题拆分为两个子问题在概念上更清晰:首先,生成dobs满足指定约束的子问题realtime_start;然后,对受限字典执行与之前相同的操作。因此:

In [13]: dobs2 = dict([(k, [d for d in v if d['realtime_start'] <= maxDate])
   ....:               for k, v in dobs.items()])
In [14]: [sorted(e, key=rts)[-1] for e in dobs2.values() if e]
Out[14]: 
[{'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-21',
  'value': '231.198'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-02-19',
  'value': '231.137'},
 {'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'}]

再一次,if e在这种情况下,预选赛不是必需的,但如果maxDate足够低以至于某些组最终是空的,那么它将是必不可少的。(没有它,访问第一个遇到的空列表的最后一个元素的尝试会引发IndexError异常。)

您可能已经注意到,上述结果的顺序与您的不同。这是因为内置的 Pythondict不保留排序。如果原始obs列表的顺序很重要,那么您可以将所有调用替换为dict调用collections.OrderedDict。例如:

In [15]: from collections import OrderedDict
In [16]: dobs = OrderedDict()
In [17]: for o in obs:
   ....:     dobs.setdefault(o['date'], []).append(o)
   ....: 
In [18]: [sorted(e, key=rts)[-1] for e in dobs.values()]
Out[18]: 
[{'date': '2012-10-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2012-12-19',
  'value': '231.623'},
 {'date': '2012-11-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-01-19',
  'value': '231.071'},
 {'date': '2012-12-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-19',
  'value': '231.197'},
 {'date': '2013-01-01',
  'realtime_end': '9999-12-31',
  'realtime_start': '2013-03-21',
  'value': '231.222'}]
于 2013-03-03T02:16:05.850 回答