Itertools groupby 按两个值组织字典列表

问题描述

我正在尝试按出生状态以及他们是否有零钱来组织价值观。 Itertools groupby 函数看起来是最简单的方法,但我正在努力实现它。也可以打开其他选项。

如果我有一个看起来像这样的字典列表

users = [
            {"name": "John","state_of_birth": "CA","money": 0},{"name": "Andrew","money": 300},{"name": "Scott","state_of_birth": "OR","money": 20},{"name": "Travis","state_of_birth": "NY",{"name": "Bill",{"name": "Mike","money": 0}
        ]

我正在尝试获取输出

desired_output = [
            [{"name": "John","money": 0}],[{"name": "Andrew","money": 300}],[{"name": "Scott","money": 20}],[{"name": "Travis","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 函数是一个生成 keyvalue 的生成器。密钥派生自我们传递给 key_functionitertools.groupb_by()。在您的情况下,keys 并不重要,这就是它在 for _,v 中被忽略的原因。

输出:

[{'name': 'John','state_of_birth': 'CA','money': 0},{'name': 'Bill','money': 0}]
[{'name': 'Andrew','money': 300}]
[{'name': 'Travis','state_of_birth': 'NY',{'name': 'Mike','money': 0}]
[{'name': 'Scott','state_of_birth': 'OR','money': 20}]
,

代码:

users = [
            {"name": "John","state_of_birth": "CA","money": 0},{"name": "Andrew","money": 300},{"name": "Scott","state_of_birth": "OR","money": 20},{"name": "Travis","state_of_birth": "NY",{"name": "Bill",{"name": "Mike","money": 0}
        ]

result = {}
for user in users:
    key = (user["state_of_birth"],user["money"])
    if key in result:
        result[key].extend([user])
    else:
        result[key] = [user]
for _,v in result.items():
    print(v)

结果:

[{'name': 'John','money': 300}]
[{'name': 'Scott','money': 20}]
[{'name': 'Travis','money': 0}]
,

如果我理解正确,您的结构是 List[Dict] 并且您想要获得一个 List[List[Dict]],其中内部列表包含具有相同 state_of_birth 和 {{ 1}} 布尔值。

我想说最简单的解决方案实际上是使用 money > 0

pandas

根据问题的上下文,您最好保持数据框/表格格式

,

您需要确保 groupby 函数的输入已排序。您可以使用与分组相同的按键功能:

users = [
            {"name": "John","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','money': 0}],[{'name': 'Andrew','money': 300}],[{'name': 'Travis',[{'name': 'Scott','money': 20}]
]
,

虽然它的名字看起来应该是这样,但 itertools.groupby 不是正确的函数,因为它需要对数据进行预先排序。对于一个应该是 O(n) 的算法,排序会使你的时间复杂度变为 O(n log(n))。

从正确的角度来看,如果您有 100 万条记录要排序,而不是 100 万次迭代,如果您使用 groupby 而不是循环和 dict,您现在有 2000 万次迭代。这是一个非常显着的性能损失。

如果 groupby 编写起来更简洁或没有导入,这可能是合理的,但与使用普通循环和字典的更简单方法相比,它的可读性较差。

Pandas 很好,但真的没有理由使用它,除非你已经这样做了。这就像乘坐航天飞机烤西葫芦一样。

您可以使用 defaultdict 和循环:

from collections import defaultdict
from pprint import pprint

users = [
    {"name": "John",]

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()]

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...