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})