如何在Python中处理字典的多个键?

5

我一直在搜索如何在字典中添加多个值来代替重复的键。

让我们以一个例子来说明:

list_1 = ['4', '6' ,'8', '8']
list_2 = ['a', 'b', 'c', 'd']
new_dict = dict(zip(list_1,list_2))
...output...
{'8': 'd', '4': 'a', '6': 'b'}

期望输出:

预期输出:

{'8': 'c,d', '4': 'a', '6': 'b'}

为了处理上述两个列表并将它们合并为一个字典,我将面临一个挑战,即我们不能在字典的“键”中有两个8,这是一种默认行为,我理解为什么会这样!!
存在以下一些选项来处理这种情况:
1)查找字典中是否已经存在“键”,如果是,则将新值附加到“键”
2)创建可变对象以引用每个键,以此方式可以拥有多个重复键~~不是我的使用情况
那么,如何使用选项#1获得预期输出?

1
为什么不从一开始就使用集合作为值呢? - user1767754
1
为什么要使用逗号分隔的字符串,而不是使用列表更合理呢? - bruno desthuilliers
你需要一个 defaultdict - pylang
3个回答

6

defaultdict/dict.setdefault

让我们开始吧:

  1. 连续迭代项目
  2. 添加属于相同键的字符串值
  3. 完成后,遍历每个键值对并将所有内容连接在一起,以获得最终结果。

from collections import defaultdict

d = defaultdict(list)   
for i, j in zip(list_1, list_2):
    d[i].append(j)
< p > defaultdict 可以使事情变得简单,并且在追加方面效率高。如果你不想使用 defaultdict,可以使用 dict.setdefault 代替(但这样会更加低效): < /p >
d = {}
for i, j in zip(list_1, list_2):
    d.setdefault(i, []).append(j)

new_dict = {k : ','.join(v) for k, v in d.items()})
print(new_dict)
{'4': 'a', '6': 'b', '8': 'c,d'}

使用Pandas的DataFrame.groupby + agg提高性能

如果你需要处理大量数据并获得较高的性能,建议使用Pandas:

import pandas as pd

df = pd.DataFrame({'A' : list_1, 'B' : list_2})
new_dict = df.groupby('A').B.agg(','.join).to_dict()

print(new_dict)
{'4': 'a', '6': 'b', '8': 'c,d'}

这个过程快吗?我有每个列表中大约100万条记录需要处理。 - PanDe
@PetPan 是的,这是最有效的解决方案。 - cs95
字典非常快。 - pylang
1
如果使用选项1,请使用defaultdict。如果仍然太慢,请尝试我的pandas选项。 - cs95
@U8-Forward的解决方案比pandas更快的原因是什么? - PanDe
@PetPan 在一个由5个元素组成的列表上的表现并不具有代表性。Pandas 的设计是为了在处理大数据时快速工作(你有一百万条记录对吧?)。在小规模数据上,使用它的开销并不值得。 - cs95

1
你可以使用一个 for 循环来迭代这两个列表:
list_1 = ['4', '6' ,'8', '8']
list_2 = ['a', 'b', 'c', 'd']

new_dict = {}
for k, v in zip(list_1, list_2):
    if k in new_dict:
        new_dict[k] += ', ' + v
    else:
        new_dict[k] = v

可能存在大型字典的效率问题,但在简单情况下它仍能正常工作。

感谢@Ev. Kounis和@bruno desthuilliers指出了原回答的一些改进之处。


coldspeed的答案比我的更有效率,我保留这个答案因为它仍然是正确的,而且我不觉得删除它有任何意义。


你可以通过使用字符串拼接而不是多次调用join方法来避免浪费(new_dict[k] += ', ' + v)。此外,你可以直接使用if k not in new_dict来避免调用其keys()方法。你的代码中还有一个错别字。 - Ma0
1
@coldspeed 是的,但比多个 join 调用更好。 - Ma0
1
k not in dict.keys() 是O(n)(它是一个顺序查找),而 k in dict 是O(1)的(它是哈希表键查找)且高度优化。 - bruno desthuilliers
简短回答:我没有你那么好。:) 有趣的事实是:对于小列表,这个解决方案比你的更有效率,coldspeed:对于OP的数据,我的代码运行需要1.88+-0.03微秒,而你的则需要2.52+-0.09微秒。但当列表大小为1000时,情况就会改变:我的代码需要525微秒,而你的则需要184微秒。 - Gianluca Micchi

1
尝试使用字典函数setdefault并获取其索引,然后使用try和except检查idx是否存在,我没有每次获取元素的索引,因为有重复项,并且最后格式化输出,使其输出为您想要的结果:
new_dict = {}
list_1 = ['4', '6' ,'8', '8']
list_2 = ['a', 'b', 'c', 'd']
for i in list_1:
   try:
      idx+=1
   except:
      idx = list_1.index(i)
   new_dict.setdefault(i, []).append(list_2[idx])
print({k:', '.join(v) for k,v in new_dict.items()})

输出:

{'4': 'a', '6': 'b', '8': 'c, d'}

我简直不敢相信,你的代码比 Pandas 解决方案跑得快得多。 运行 Pandas 花费了 0.0160000324249 秒 运行其他方案花费了 0.0 秒 - PanDe
2
@PetPan 使用timeit进行准确的计时。此外,您应该在实际数据上测量性能,我可以立即告诉您,由于循环内的.index调用,对于大型列表来说这将非常缓慢。 - cs95
肯定的,在您分享的内容中很有意义,我正在寻找相同的信息。 - PanDe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接