Какой самый питон c / эффективный способ исправить этот список? - PullRequest
0 голосов
/ 22 апреля 2020

У меня есть следующий список:

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

Объяснение:

  • Первые 2 строки объединены в 1. Обе имеют одинаковый тикер AAPL и одну и ту же сторону Buy.
  • Строка с тикером NFLX остается без изменений. Нет другого ряда с таким же тикером и той же стороной
  • Четвертый ряд с SPY в качестве тикера и Buy в качестве стороны остается без изменений. Последний ряд имеет тот же тикер, но разную сторону
  • 3 строки с MSFT при тикере объединяются в один. Все 3 строки имеют одинаковый тикер MSFT и одну и ту же сторону Sell

Как видно, объединенные строки содержат одинаковый тикер, боковое и последнее поля. Если одна строка имеет signal_1 = 100, а другая строка - signal_1 = 0, объединенная строка приведет к signal_1 = 100. Если обе строки имеют signal_1 = 0, объединенное поле останется без изменений.

Тикер сбоку и последние поля остаются прежними. Изменяются только поля сигнала.

Каков наиболее эффективный способ сделать это?

Ответы [ 3 ]

1 голос
/ 22 апреля 2020

Множество способов сделать это, я уверен. Это один из вариантов использования 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}]
>>> 

Я экспериментировал и имел три версии вышеупомянутой функции - пробовал различные способы настройки алгоритма. Тот, который я отправил, самый быстрый. Ниже приводится версия, с которой я начал, и с которой я закончил.

>>> timeit.timeit("first_impl(data)", globals=globals(), number=1000000)
21.083179871027824
>>> timeit.timeit("last_impl(data)", globals=globals(), number=1000000)
5.915724034013692

Я работаю на виртуальной машине на хосте, которому более десяти лет. Я уверен, что каждый может получить лучшее время, чем это.

Мне интересно, что даже itemgetter() играет роль в сокращении времени выполнения функции. Я проверил каждую строку на наличие альтернатив.

Функция имеет побочный эффект. Некоторые словари в исходном списке будут изменены.

Обновление здесь. Кто-то указал мне, что 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
1 голос
/ 23 апреля 2020

Я просто хотел добавить другое решение этой проблемы, используя Pandas. Удивительно легко сгруппировать строки вместе (хотя не все проще с Pandas, хотя - во многих случаях, если вам нужно выполнить много изменений в полевых данных и структуре данных, это может быть сложно).

>>> 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 с, как в примере вывода:

>>> df.to_dict('records')
1 голос
/ 22 апреля 2020

Сначала создайте 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...