根据多个值过滤字典列表

时间:2019-08-21 18:32:18

标签: python list dictionary data-structures min

我有一个要基于多个条件过滤的词典列表。列表的缩短版本如下所示:

orders = [{"name": "v", "price": 123, "location": "Mars"}, 
          {"name": "x", "price": 223, "location": "Mars"}, 
          {"name": "x", "price": 124, "location": "Mars"}, 
          {"name": "y", "price": 456, "location": "Mars"}, 
          {"name": "z", "price": 123, "location": "Mars"}, 
          {"name": "z", "price": 5623, "location": "Mars"}]

我希望最终得到一个列表,该列表包含具有相同“名称”键的每本词典价格最低的词典。 例如,上面的内容将变为:

minimums = [{"name": "v", "price": 123, "location": "Mars"},
            {"name": "x", "price": 124, "location": "Mars"},
            {"name": "y", "price": 456, "location": "Mars"},
            {"name": "z", "price": 123, "location": "Mars"}]

我通过嵌套的if语句和for循环实现了这一目标,但是我希望有一种更“ Pythonic”的实现方式。

重用相同列表或创建新列表都是可以的。

谢谢您的帮助。

编辑: 谢谢您的回答,我尝试使用以下代码计时每个人

print("Number of dictionaries in orders: " + str(len(orders)))

t0 = time.time()
sorted_orders = sorted(orders, key=lambda i: i["name"])
t1 = time.time()
sorting_time = (t1 - t0)

t0 = time.time()
listcomp_wikiben = [x for x in orders if all(x["price"] <= y["price"] for y  in orders if x["name"] == y["name"])]
t1 = time.time()
print("listcomp_wikiben: " + str(t1 - t0))

t0 = time.time()
itertools_MrGeek = [min(g[1], key=lambda x: x['price']) for g in groupby(sorted_orders, lambda o: o['name'])]
t1 = time.time()
print("itertools_MrGeek: " + str(t1 - t0 + sorting_time))

t0 = time.time()
itertools_Cory = [min(g, key=lambda j: j["price"]) for k,g in groupby(sorted_orders, key=lambda i: i["name"])]
t1 = time.time()
print("itertools_CoryKramer: " + str(t1 - t0 + sorting_time))

t0 = time.time()
pandas_Trenton = pd.DataFrame(orders)
pandas_Trenton.groupby(['name'])['price'].min()
t1 = time.time()
print("pandas_Trenton_M: " + str(t1 - t0))

结果是:

Number of dictionaries in orders: 20867
listcomp_wikiben:     39.78123s
itertools_MrGeek:      0.01562s
itertools_CoryKramer:  0.01565s
pandas_Trenton_M:      0.29685s

4 个答案:

答案 0 :(得分:3)

如果您首先按"name"对列表进行排序,则可以使用itertools.groupby对它们进行分组,然后使用min和lambda来查找每组中的最小"price"

>>> from itertools import groupby
>>> sorted_orders = sorted(orders, key=lambda i: i["name"])
>>> [min(g, key=lambda j: j["price"]) for k,g in groupby(sorted_orders , key=lambda i: i["name"])]
[{'name': 'v', 'price': 123, 'location': 'Mars'},
 {'name': 'x', 'price': 124, 'location': 'Mars'},
 {'name': 'y', 'price': 456, 'location': 'Mars'},
 {'name': 'z', 'price': 123, 'location': 'Mars'}]

答案 1 :(得分:3)

您可以使用itertools.groupby

from itertools import groupby

print([min(g[1], key = lambda x : x['price']) for g in groupby(orders, lambda o : o['name'])])

输出:

[
  {'name': 'v', 'price': 123, 'location': 'Mars'},
  {'name': 'x', 'price': 124, 'location': 'Mars'},
  {'name': 'y', 'price': 456, 'location': 'Mars'},
  {'name': 'z', 'price': 123, 'location': 'Mars'}
]

答案 2 :(得分:2)

没有itertools的解决方案

[x for x in orders if all(x["price"] <= y["price"] for y in orders if x["name"] == y["name"])]

答案 3 :(得分:1)

使用pandas

orders = [{"name": "v", "price": 123, "location": "Mars"}, 
          {"name": "x", "price": 223, "location": "Mars"}, 
          {"name": "x", "price": 124, "location": "Mars"}, 
          {"name": "y", "price": 456, "location": "Mars"}, 
          {"name": "z", "price": 123, "location": "Pluto"}, 
          {"name": "z", "price": 5623, "location": "Mars"}]

import pandas as pd

df = pd.DataFrame(orders)

enter image description here

df.groupby(['name', 'location'])['price'].min()

enter image description here