我有一个由字典组成的列表。我希望对列表进行子集化,根据元素值的比较选择字典(在这种情况下,每个日期只选择一个字典,选择的字典是具有最大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'}]
我希望对列表进行子集化,使得它只包含每个日期的一个dict,并且选择了具有最大realtime_start
值的dict。
在这种情况下,在列表成为子集之后,它将是:
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中可行吗?
感谢
答案 0 :(得分: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) )比任何你可以自己滚动的东西。
答案 1 :(得分:1)
您基本上希望按date
字段对条目进行分组,然后对与每个date
相关联的条目组执行操作。我这样做的方式是使用普通的'dict
。在这种情况下,我认为dict
是一种特殊的set
- 一个“装饰集”,如果你愿意的话,它的每一个(必然可以清洗的)元素都被“装饰”了一些(在一般不可清除的有效载荷(即相关的字典值)。在您的示例中,此“装饰集”的每个元素是date
中所有dicts中obs
字段的可能值之一,其关联的有效负载是obs
中所有dicts的列表{1}}将该密钥作为其date
字段。
因此,
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
- 循环的主体,如下所示:
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
,其值为列出所有字典在obs
中,其具有相应的键作为date
值。
现在你可以使用这个词典前往城镇,并对其值应用任何类型的功能。例如,要为每个date
提取具有最新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
异常。)
您可能已经注意到,上述结果的排序与您的顺序不同。这是因为内置的Python dict
不会保留排序。如果原始obs
列表的排序很重要,那么您可以通过调用dict
来取代对collections.OrderedDict
的所有来电。 E.g:
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'}]