Объединение словарей в список на основе двух значений - PullRequest
0 голосов
/ 26 мая 2020

Ищу объединить словари в списке словарей. Мой список dicts выглядит так:

opt = [{'expiry': '2020-06-26', 'strike': 138.5, 'p_bid': 0.4375, 'p_ask': 0.46875}, 
       {'expiry': '2020-06-26', 'strike': 139.0, 'p_bid': 0.6875, 'p_ask': 0.71875}, 
       {'expiry': '2020-07-22', 'strike': 139.0, 'p_bid': 1.015625, 'p_ask': 1.0625}, 
       {'expiry': '2020-06-26', 'strike': 138.5, 'c_bid': 0.6875, 'c_ask': 0.734375}, 
       {'expiry': '2020-06-26', 'strike': 139.0, 'c_bid': 0.4375, 'c_ask': 0.484375}, 
       {'expiry': '2020-07-22', 'strike': 139.0, 'c_bid': 0.28125, 'c_ask': 0.3125}]

Словари нужно объединять попарно, где «истечение срока» и «удар» идентичны. желаемый результат выглядит так:

[{'expiry': '2020-06-26', 'strike': 138.5, 'p_bid': 0.4375, 'p_ask': 0.46875, 'c_bid': 0.6875, 'c_ask': 0.734375}, 
 {'expiry': '2020-06-26', 'strike': 139.0, 'p_bid': 0.6875, 'p_ask': 0.71875, 'c_bid': 0.4375, 'c_ask': 0.484375}, 
 {'expiry': '2020-07-22', 'strike': 139.0, 'p_bid': 1.015625, 'p_ask': 1.0625, 'c_bid': 0.28125, 'c_ask': 0.3125}}]

Ответы [ 2 ]

3 голосов
/ 26 мая 2020

"Наивный" подход:

Добавить словари в новый список результатов. Для каждого нового словаря проверяйте, соответствует ли он словарю, уже находящемуся в списке. Если да, объедините их. Если нет, добавьте его в список:

res = [opt[0]]
for d_new in opt[1:]:
    for d in res:
        if d['expiry'] == d_new['expiry'] and d['strike'] == d_new['strike']:
       #if (d['expiry'], d['strike']) == (d_new['expiry'], d_new['strike']):
            d.update(d_new)
            break
    else:
        res.append(d_new)

Здесь используется конструкция for/else, которая здесь полезна, потому что мы хотим добавить новый dict в список, только если это не так. не совпадает ни с одним другим в списке результатов. Если мы нашли совпадение, мы объединяем их, и break и else не будут выполнены.

Небольшое улучшение:

Приведенный выше подход приводит к временной сложности O(n^2) для зацикливание всех диктовок для каждого дикта (не совсем, но академически это все равно O(n^2)). Чтобы попытаться улучшить это, второй подход может заключаться в том, чтобы сгруппировать вместе словари с похожими expiry и strike в один go (O(n)):

from collections import defaultdict

merged_dicts = defaultdict(dict)
for d in opt:
    merged_dicts[(d['expiry'], d['strike'])].update(d)

res = list(merged_dicts.values())

Здесь используется collections.defaultdict, чтобы легко объединить словари без слишком большого количества if условий. Мы также используем метод dict update, чтобы фактически объединить их.

0 голосов
/ 26 мая 2020

Один довольно простой способ сделать это - использовать pandas:

df = pd.DataFrame(opt)
df = df.drop_duplicates(subset = ["expiry", "strike"])
[ v.dropna().to_dict() for k,v in df.iterrows() ]

Результаты в:

[{'expiry': '2020-06-26', 'strike': 138.5, 'p_bid': 0.4375, 'p_ask': 0.46875},
 {'expiry': '2020-06-26', 'strike': 139.0, 'p_bid': 0.6875, 'p_ask': 0.71875},
 {'expiry': '2020-07-22', 'strike': 139.0, 'p_bid': 1.015625, 'p_ask': 1.0625}]

Обратите внимание, что при удалении дубликатов я сохраняю первый элемент. Можно легко оставить последний элемент, если это то, что вы хотите, но используя:

df = df.drop_duplicates(subset = ["expiry", "strike"], keep="last")

В этом случае результат:

[{'expiry': '2020-06-26', 'strike': 138.5, 'c_bid': 0.6875, 'c_ask': 0.734375},
 {'expiry': '2020-06-26', 'strike': 139.0, 'c_bid': 0.4375, 'c_ask': 0.484375},
 {'expiry': '2020-07-22', 'strike': 139.0, 'c_bid': 0.28125, 'c_ask': 0.3125}]

И еще один способ - использовать словарь для уменьшения «похожих» значений:

reduction_dict = {(x["expiry"], x["strike"]):x for x in opt }
list(reduction_dict.values())
...