Python-基于键/值标识的组/合并字典

Python-基于键/值标识的组/合并字典,python,list,dictionary,merge,key,Python,List,Dictionary,Merge,Key,我有一个列表,其中包含许多键相同但值不同的字典 我想做的是根据一些键的值对字典进行分组/合并。 展示一个例子可能比试图解释更快: [{'zone': 'A', 'weekday': 1, 'hour': 12, 'C1': 3, 'C2': 15}, {'zone': 'B', 'weekday': 2, 'hour': 6, 'C1': 5, 'C2': 27}, {'zone': 'A', 'weekday': 1, 'hour': 12, 'C1': 7, 'C2': 12},

我有一个列表,其中包含许多键相同但值不同的字典

我想做的是根据一些键的值对字典进行分组/合并。 展示一个例子可能比试图解释更快:

[{'zone': 'A', 'weekday': 1, 'hour': 12,  'C1': 3, 'C2': 15},
 {'zone': 'B', 'weekday': 2, 'hour': 6,  'C1': 5, 'C2': 27},
 {'zone': 'A', 'weekday': 1, 'hour': 12,  'C1': 7, 'C2': 12},
 {'zone': 'C', 'weekday': 5, 'hour': 8,  'C1': 2, 'C2': 13}]
因此,我想要实现的是合并第一个和第三个字典,因为它们具有相同的“区域”、“小时”和“工作日”,将C1和C2中的值相加:

[{'zone': 'A', 'weekday': 1, 'hour': 12,  'C1': 10, 'C2': 27},
 {'zone': 'B', 'weekday': 2, 'hour': 6,  'C1': 5, 'C2': 27},
 {'zone': 'C', 'weekday': 5, 'hour': 8,  'C1': 2, 'C2': 13}]
有什么帮助吗?:)我已经为此挣扎了几天,我得到了一个不好的不可扩展的解决方案,但我确信我可以把一些更具pythonic的东西放在适当的位置

谢谢

通过使用,您可以在线性时间内合并它们

from collections import defaultdict

res = defaultdict(lambda : defaultdict(int))

for d in dictionaries:
        res[(d['zone'],d['weekday'],d['hour'])]['C1']+= d['C1']
        res[(d['zone'],d['weekday'],d['hour'])]['C2']+= d['C2']

缺点是需要另一个过程才能获得定义的输出。

我已经编写了一个稍长的解决方案,使用NameTuple作为字典的键:

从集合导入namedtuple
zones=[{'zone':'A','weekday':1,'hour':12,'C1':3,'C2':15},
{'zone':'B','weekday':2,'hour':6,'C1':5,'C2':27},
{'zone':'A','weekday':1,'hour':12,'C1':7,'C2':12},
{'zone':'C','weekday':5,'hour':8,'C1':2,'C2':13}]
ZoneTime=namedtuple(“ZoneTime”、“zone”、“weekday”、“hour”])
结果=dict()
对于分区中的分区:
区域时间=区域时间(区域['zone']、区域['weekday']、区域['hour'])
如果区域_时间结果为:
结果[区域时间]['C1']+=区域['C1']
结果[区域时间]['C2']+=区域['C2']
其他:
结果[zone_time]={'C1':zone['C1'],'C2':zone['C2']}
打印(结果)
这将使用(区域、工作日、小时)的命名元组作为每个字典的键。然后,如果它已经存在于
结果中,则添加到它,或者在字典中创建一个新条目,都是相当简单的

您当然可以将其缩短并“更智能”,但它的可读性可能会降低。

Edit:运行时比较 我最初的答案(见下文)不是一个好答案,但我认为我对其他答案做了一点运行时分析,这是一个有用的贡献,因此我编辑了该部分并将其放在顶部。这里我包括其他三个解决方案,以及生成所需输出所需的转换。为完整起见,我还提供了一个使用
pandas
的版本,该版本假设用户正在使用
数据帧
(从dict列表到数据帧再到数据帧的转换甚至不值得)。根据生成的随机数据,比较时间略有不同,但这些数据具有相当的代表性:

运行计时器(100) 具有100个值的时间 …带defaultdict:0.1496697600000516 …带名称的整数:0.14976404848994122 …与groupby:0.0690777249999428 …有熊猫:3.316571125001095 >>>运行计时器(1000) 具有1000个值的时间 …带defaultdict:1.267153091999944 …名称为双:0.9605341750000207 …与groupby:0.6634409229998255 …有熊猫:3.5146895360001054 >>>运行计时器(10000) 具有10000个值的时间 …带defaultdict:9.194478484000001 …带名称的整数:9.157486262000179 …与groupby:5.18553969300001 …有熊猫:4.704001281000046 >>>运行计时器(100000) 具有100000个值的时间 …带defaultdict:59.644778522000024 …带名称的整数:89.26688319799996 …与groupby:93.351702798999 …有熊猫:14.49520906199958
外卖:

  • 使用pandas数据帧可以为大型数据集带来巨大的收益

    • 注意:我不包括DICT列表和数据帧之间的转换,这绝对是重要的
  • 否则,被接受的解决方案(二战时)在中小型数据集中获胜,但对于非常大的数据集,它可能是最慢的

  • 更改组的大小(例如,通过减少分区的数量)会产生巨大的影响,此处未对此进行研究

下面是我用来生成上述内容的脚本

随机导入
进口大熊猫
从timeit导入timeit
从functools导入部分
从itertools导入groupby
从运算符导入itemgetter
从集合导入namedtuple、defaultdict
带_熊猫(df)的def:
返回df.groupby(['zone','weekday','hour']).agg(sum).reset_index()
def与_groupby(数据):
keys=itemgetter('区域'、'工作日'、'小时')
#数据是你的口述清单
data.sort(key=key)
分组=分组依据(数据、键)
新的_数据=[]
对于(区域、工作日、小时),g分组:
c1,c2=0,0
对于d in g:
c1+=d['c1']
c2+=d['c2']
新增_data.append({'zone':区域,'weekday':weekday),
“小时”:小时,'C1':C1,'C2':C2})
返回新的数据
带有\u名称的def(分区):
ZoneTime=namedtuple(“ZoneTime”、“zone”、“weekday”、“hour”])
结果=dict()
对于分区中的分区:
区域时间=区域时间(区域['zone']、区域['weekday']、区域['hour'])
如果区域_时间结果为:
结果[区域时间]['C1']+=区域['C1']
结果[区域时间]['C2']+=区域['C2']
其他:
结果[zone_time]={'C1':zone['C1'],'C2':zone['C2']}
返回[
{
“区域”:键[0],
“weekday”:键[1],
“小时”:键[2],
**瓦尔
}
对于键,在results.items()中使用val
]
def与_defaultdict(字典):
res=defaultdict(lambda:defaultdict(int))
对于字典中的d:
res[(d['zone',d['weekday',d['hour'])]['C1']+=d['C1']
res[(d['zone',d['weekday',d['hour'])]['C2']+=d['C2']
返回[
{
“区域”:键[0],
“weekday”:键[1],
“小时”:键[2],
**瓦尔
}
对于键,在res.items()中使用val
]
def gen_随机值(数值):
返回[
{
“区域”:随机.choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),
“工作日”:random.ra
import operator
import itertools

keys = operator.itemgetter('zone','weekday','hour')
c1_c2 = operator.itemgetter('C1','C2')

# data is your list of dicts
data.sort(key=keys)
grouped = itertools.groupby(data,keys)

new_data = []
for (zone,weekday,hour),g in grouped:
    c1,c2 = 0,0
    for d in g:
        c1 += d['C1']
        c2 += d['C2']
    new_data.append({'zone':zone,'weekday':weekday,
                     'hour':hour,'C1':c1,'C2':c2})
for (zone,weekday,hour),g in grouped:
    cees = map(c1_c2,g)
    c1,c2 = map(sum,zip(*cees))
    new_data.append({'zone':zone,'weekday':weekday,
                     'hour':hour,'C1':c1,'C2':c2})