[英]Whats the most pythonic / efficient way to fix this list?
我有以下列表:
dummyData =[
{'ticker':'AAPL', 'side':'Buy', 'signal_1':211.12, 'signal_2':0, 'signal_3':0, 'last':200.12},
{'ticker':'AAPL', 'side':'Buy', 'signal_1':0, 'signal_2':0, 'signal_3':211.12, 'last':200.12},
{'ticker':'NFLX', 'side':'Sell', 'signal_1':411.12, 'signal_2':0, 'signal_3':0, 'last':455.02},
{'ticker':'SPY', 'side':'Buy', 'signal_1':0, 'signal_2':211.12, 'signal_3':0, 'last':259.55},
{'ticker':'MSFT', 'side':'Sell', 'signal_1':160.33, 'signal_2':0, 'signal_3':0, 'last':110.14},
{'ticker':'MSFT', 'side':'Sell', 'signal_1':0, 'signal_2':161.71, 'signal_3':0, 'last':110.14},
{'ticker':'MSFT', 'side':'Sell', 'signal_1':0, 'signal_2':0, 'signal_3':170, 'last':110.14},
{'ticker':'SPY', 'side':'Sell', 'signal_1':300, 'signal_2':0, 'signal_3':0, 'last':259.55},
]
目标是合并具有相同ticker
和side
的项目。 结果列表应如下所示:
resultData =[
{'ticker':'AAPL', 'side':'Buy', 'signal_1':211.12, 'signal_2':0, 'signal_3':211.12, 'last':200.12},
{'ticker':'NFLX', 'side':'Sell', 'signal_1':411.12, 'signal_2':0, 'signal_3':0, 'last':455.02},
{'ticker':'SPY', 'side':'Buy', 'signal_1':0, 'signal_2':211.12, 'signal_3':0, 'last':259.55},
{'ticker':'MSFT', 'side':'Sell', 'signal_1':160.33, 'signal_2':161.71, 'signal_3':170, 'last':110.14},
{'ticker':'SPY', 'side':'Sell', 'signal_1':300, 'signal_2':0, 'signal_3':0, 'last':259.55},
]
解释:
AAPL
和相同的方Buy
。NFLX
代码的行保持不变。 没有任何其他行具有相同的代码和相同的一面SPY
为股票代码和Buy
为边的第四行保持原样。 最后一行有相同的代码但不同的一面MSFT
作为代码的 3 行合并为一。 所有 3 行都有相同的股票代码MSFT
和相同的边Sell
正如您所看到的,合并的行包含相同的代码、边和最后一个字段。 如果一行的 signal_1 = 100 而另一行的 signal_1 = 0,则合并的行将导致 signal_1 = 100。如果两者的 signal_1 = 0,则合并字段保持原样。
Ticker、side 和 last 字段保持不变。 只有信号字段被修改。
最有效的方法是什么?
首先,创建一set
代码/边组合。
combis = set((r['ticker'], r['side']) for r in dummyData)
使用列表推导对每个组合的数据进行子集化:
resultdata = []
for ticker, side in combis:
# sub contains all records for this particular combination.
sub = [r for r in dummyData if r['ticker'] == ticker and r['side'] == side]
num = len(sub)
if num == 0:
continue
elif num == 1:
resultdata.append(sub[0])
else:
# TODO: merge the data from sub
我敢肯定,有很多方法可以做到这一点。 这是使用groupby()
的一个选项。 groupby()
返回一个迭代器,因此它的评估是惰性的,并且不会独立地迭代列表。
该算法的更新版本发布在此答案的底部。 新版本不假定键入两个属性的记录是相邻的。 groupby()
需要相邻的键来最大化其分组。 样本数据是相邻组织的,这可能是巧合。
>>> from itertools import groupby
>>> from operator import itemgetter
>>>
>>> def fix_list(inp):
... new_li = []
... for _, group in groupby(inp, key=itemgetter('ticker', 'side')):
... new_d = next(group)
... for d in group:
... # This block is skipped if there's only one
... # item in the group.
... for k, v in d.items():
... new_d[k] = new_d[k] or v
... new_li.append(new_d)
... return new_li
...
>>> fix_list(dummyData)
[{'ticker': 'AAPL', 'side': 'Buy', 'signal_1': 211.12, 'signal_2': 0, 'signal_3': 211.12, 'last': 200.12},
{'ticker': 'NFLX', 'side': 'Sell', 'signal_1': 411.12, 'signal_2': 0, 'signal_3': 0, 'last': 455.02},
{'ticker': 'SPY', 'side': 'Buy', 'signal_1': 0, 'signal_2': 211.12, 'signal_3': 0, 'last': 259.55},
{'ticker': 'MSFT', 'side': 'Sell', 'signal_1': 160.33, 'signal_2': 161.71, 'signal_3': 170, 'last': 110.14},
{'ticker': 'SPY', 'side': 'Sell', 'signal_1': 300, 'signal_2': 0, 'signal_3': 0, 'last': 259.55}]
>>>
我进行了实验并获得了上述 function 的三个版本 - 尝试了各种方法来调整算法。 我发的是最快的。 下面的时间是我开始的版本,也是我结束的版本。
>>> timeit.timeit("first_impl(data)", globals=globals(), number=1000000)
21.083179871027824
>>> timeit.timeit("last_impl(data)", globals=globals(), number=1000000)
5.915724034013692
我在一台已有十多年历史的主机上运行虚拟机。 我相信任何人都可以得到比这更好的时光。
我发现有趣的是,即使itemgetter()
在减少 function 的运行时间方面也发挥了作用。 我针对替代品测试了每一行。
function 有副作用。 原始列表中的一些词典将被修改。
这里有更新。 有人向我指出groupby()
将列表中的相邻键项分组,因此需要一个排序来获得更严格的通用解决方案。 我们用示例中的数据集得到了相同的结果,但是假设其他数据集中的项目可能不相邻,这个版本应该处理它。 排序非常快(timsort 算法),并且只比上面的时间增加了一秒多一点。
>>> def fix_list(inp):
... new_li = []
... getter = itemgetter('ticker', 'side')
... inp.sort(key=getter)
... for _, group in groupby(inp, key=getter):
... new_d = next(group)
... for d in group:
... # This block is skipped if there's only one
... # item in the group.
... for k, v in d.items():
... new_d[k] = new_d[k] or v
... new_li.append(new_d)
... return new_li
我只是想使用 Pandas 为这个问题添加另一个解决方案。 将行分组在一起非常容易......(尽管 Pandas 并不是所有事情都更容易 - 在很多情况下,如果您必须对字段数据和 dataframe 结构执行大量更改,它可能会很复杂)。
>>> df = pd.DataFrame.from_dict(dummyData)
>>> df
ticker side signal_1 signal_2 signal_3 last
0 AAPL Buy 211.12 0.00 0.00 200.12
1 AAPL Buy 0.00 0.00 211.12 200.12
2 NFLX Sell 411.12 0.00 0.00 455.02
3 SPY Buy 0.00 211.12 0.00 259.55
4 MSFT Sell 160.33 0.00 0.00 110.14
5 MSFT Sell 0.00 161.71 0.00 110.14
6 MSFT Sell 0.00 0.00 170.00 110.14
7 SPY Sell 300.00 0.00 0.00 259.55
>>>
>>> df = df.groupby(['ticker', 'side'], as_index=False).max()
>>> df
ticker side signal_1 signal_2 signal_3 last
0 AAPL Buy 211.12 0.00 211.12 200.12
1 MSFT Sell 160.33 161.71 170.00 110.14
2 NFLX Sell 411.12 0.00 0.00 455.02
3 SPY Buy 0.00 211.12 0.00 259.55
4 SPY Sell 300.00 0.00 0.00 259.55
>>>
然后,如果您需要dict
列表形式的数据,如示例 output 中所示:
>>> df.to_dict('records')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.