我正在尝试按出生状态以及它们是否有 0 钱来组织值。 Itertools groupby 函数看起来像是最简单的方法,但我正在努力实现它。也对其他选项开放。
如果我有一个看起来像这样的词典列表
users = [
{"name": "John", "state_of_birth": "CA", "money": 0},
{"name": "Andrew", "state_of_birth": "CA", "money": 300},
{"name": "Scott", "state_of_birth": "OR", "money": 20},
{"name": "Travis", "state_of_birth": "NY", "money": 0},
{"name": "Bill", "state_of_birth": "CA", "money": 0},
{"name": "Mike", "state_of_birth": "NY", "money": 0}
]
我正在尝试获取此输出
desired_output = [
[{"name": "John", "state_of_birth": "CA", "money": 0}, {"name": "Bill", "state_of_birth": "CA", "money": 0}],
[{"name": "Andrew", "state_of_birth": "CA", "money": 300}],
[{"name": "Scott", "state_of_birth": "OR", "money": 20}],
[{"name": "Travis", "state_of_birth": "NY", "money": 0},{"name": "Mike", "state_of_birth": "NY", "money": 0}]
]
你可以像这样使用itertools
:
import itertools
def func(x):
return tuple([x['state_of_birth'], x['money'] != 0])
desired_output = list(list(v) for _,v in itertools.groupby(sorted(users, key=func), func))
group_by
函数是产生key
和value
的生成器。密钥派生自我们要传递给itertools.groupb_by()
的key_function
。在您的情况下,keys
并不重要,这就是为什么它在for _, v
中被忽略的原因。
输出:
[{'name': 'John', 'state_of_birth': 'CA', 'money': 0}, {'name': 'Bill', 'state_of_birth': 'CA', 'money': 0}]
[{'name': 'Andrew', 'state_of_birth': 'CA', 'money': 300}]
[{'name': 'Travis', 'state_of_birth': 'NY', 'money': 0}, {'name': 'Mike', 'state_of_birth': 'NY', 'money': 0}]
[{'name': 'Scott', 'state_of_birth': 'OR', 'money': 20}]
如果我理解正确,你有一个List[Dict]
的结构,你想得到一个List[List[Dict]]
,其中内部列表包含具有相同state_of_birth
和money > 0
布尔值的字典。
我会说最简单的解决方案实际上是使用pandas
import pandas as pd
users = [
{"name": "John", "state_of_birth": "CA", "money": 0},
{"name": "Andrew", "state_of_birth": "CA", "money": 300},
{"name": "Scott", "state_of_birth": "OR", "money": 20},
{"name": "Travis", "state_of_birth": "NY", "money": 0},
{"name": "Bill", "state_of_birth": "CA", "money": 0},
{"name": "Mike", "state_of_birth": "NY", "money": 0}
]
df = pd.DataFrame.from_records(users)
# we need a column to indicate if money > 0
df["money_bool"] = df["money"] > 0
# groupby gives you an iterator of Tuple[key, sub-dataframe]
# dfs now holds a list of your grouped dataframes
dfs = [tup[1] for tup in df.groupby(["state_of_birth", "money_bool"])]
# you can now drop the money_bool column if you want
dfs = [df.drop("money_bool", axis=1) for df in dfs]
desired_output = [df.to_dict("records") for df in dfs]
根据问题的上下文,最好保持数据帧/表格格式
您需要确保对groupby
函数的输入进行排序。您可以使用与分组相同的键函数:
users = [
{"name": "John", "state_of_birth": "CA", "money": 0},
{"name": "Andrew", "state_of_birth": "CA", "money": 300},
{"name": "Scott", "state_of_birth": "OR", "money": 20},
{"name": "Travis", "state_of_birth": "NY", "money": 0},
{"name": "Bill", "state_of_birth": "CA", "money": 0},
{"name": "Mike", "state_of_birth": "NY", "money": 0}
]
def selector(item): return (item.get('state_of_birth'), item.get('money') != 0)
sorted_users = sorted(users, key=selector)
result = [list(group) for _, group in groupby(sorted_users, selector) ]
输出:
[
[{'name': 'John', 'state_of_birth': 'CA', 'money': 0}, {'name': 'Bill', 'state_of_birth': 'CA', 'money': 0}],
[{'name': 'Andrew', 'state_of_birth': 'CA', 'money': 300}],
[{'name': 'Travis', 'state_of_birth': 'NY', 'money': 0}, {'name': 'Mike', 'state_of_birth': 'NY', 'money': 0}],
[{'name': 'Scott', 'state_of_birth': 'OR', 'money': 20}]
]
尽管它的名字似乎是要走的路,但itertools.groupby
不是正确的功能,因为它需要对数据进行预排序。排序会使您的时间复杂度达到 O(n log(n)) 的算法,该算法应该是 O(n)。
从这个角度来看,如果你有一百万条记录要排序,而不是一百万次迭代,如果你使用groupby
而不是循环和字典,你现在有 2000 万次迭代。这是一个相当严重的性能损失。
如果groupby
编写起来更干净或没有导入,这可能是合理的,但它不如使用普通循环和字典的更简单方法可读。
熊猫很好,但除非你已经这样做了,否则真的没有理由使用它。这就像带一架航天飞机来烤西葫芦。
您可以使用defaultdict
和循环:
from collections import defaultdict
from pprint import pprint
users = [
{"name": "John", "state_of_birth": "CA", "money": 0},
{"name": "Andrew", "state_of_birth": "CA", "money": 300},
{"name": "Scott", "state_of_birth": "OR", "money": 20},
{"name": "Travis", "state_of_birth": "NY", "money": 0},
{"name": "Bill", "state_of_birth": "CA", "money": 0},
{"name": "Mike", "state_of_birth": "NY", "money": 0},
]
grouped = defaultdict(list)
groupby = "state_of_birth", "money"
for user in users:
grouped[tuple([user[k] for k in groupby])].append(user)
pprint([*grouped.values()])
如果你想要"钱不为零",而不仅仅是"money"
值本身,你可以使用自定义分组函数:
grouped = defaultdict(list)
def group_by(x):
return x["state_of_birth"], x["money"] != 0
for user in users:
grouped[group_by(user)].append(user)
result = [*grouped.values()]
或内联逻辑:
grouped = defaultdict(list)
for user in users:
grouped[user["state_of_birth"], user["money"] != 0].append(user)
result = [*grouped.values()]