繁体   English   中英

修复此列表的最pythonic /最有效的方法是什么?

[英]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},
        ]

目标是合并具有相同tickerside的项目。 结果列表应如下所示:

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},
        ]

解释:

  • 前 2 行合并为 1。两者都具有相同的代码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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM