【问题标题】:How to merge two lists into dictionary without using nested for loop如何在不使用嵌套 for 循环的情况下将两个列表合并到字典中
【发布时间】:2017-10-31 22:07:29
【问题描述】:

我有两个列表:

a = [0, 0, 0, 1, 1, 1, 1, 1, .... 99999]
b = [24, 53, 88, 32, 45, 24, 88, 53, ...... 1]

我想将这两个列表合并到一个字典中,例如:

{
    0: [24, 53, 88], 
    1: [32, 45, 24, 88, 53], 
    ...... 
    99999: [1]
}

一个解决方案可能是使用for 循环,它看起来不太优雅,例如:

d = {}
unique_a = list(set(list_a))
for i in range(len(list_a)):
    if list_a[i] in d.keys:
        d[list_a[i]].append(list_b[i])
    else:
        d[list_a] = [list_b[i]]

虽然这确实有效,但效率低下,并且当列表非常大时会花费太多时间。我想知道构建这样一个字典的更优雅的方法吗?

提前致谢!

【问题讨论】:

  • 嵌套的 for 循环如何?
  • DYM if list_a[i] in d.keysd[list_a[i]] = [list_b[i]]?请准确地发布您尝试过的代码,最好使用复制+粘贴(如果在您的平台上可用)。
  • 如果提供的答案之一对您有用,请将其标记为已接受。它使将来遇到您的问题的人更容易知道什么有效。
  • @TobySpeight if 表示如果list_a[i] 已经是字典中的键,则将list_b[i] 添加到字典中的键list_a[i] 下,而else 表示如果不是,则添加list_b[i] to the new key list_a[i]` 作为列表。希望对您有所帮助。
  • @BigD,我认为这就是您要写的内容(正如我所建议的那样)。 list_[a] in d.keys 没有意义,d[list_a] = 也没有。我建议你 edit 修复这些错误。

标签: python list dictionary


【解决方案1】:

您可以使用defaultdict

from collections import defaultdict
d = defaultdict(list)
list_a = [0, 0, 0, 1, 1, 1, 1, 1, 9999]
list_b = [24, 53, 88, 32, 45, 24, 88, 53, 1]
for a, b in zip(list_a, list_b):
   d[a].append(b)

print(dict(d))

输出:

{0: [24, 53, 88], 1: [32, 45, 24, 88, 53], 9999: [1]}

【讨论】:

  • 真的,在这里使用defaultdict 是多余的。请参阅this answer,其中dict.setdefault 可以以最小的开销处理同样的事情。
  • @cᴏʟᴅsᴘᴇᴇᴅ d[a].append(b)d.setdefault(x, []).append(y) 干净得多
  • 以额外的进口和更重的结构为代价;-)
【解决方案2】:

没有花哨的结构,只是一本普通的字典。

d = {}
for x, y in zip(a, b):
    d.setdefault(x, []).append(y)

【讨论】:

    【解决方案3】:

    替代itertools.groupby()解决方案:

    import itertools
    
    a = [0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3]
    b = [24, 53, 88, 32, 45, 24, 88, 53, 11, 22, 33, 44, 55, 66, 77]
    
    result = { k: [i[1] for i in g] 
               for k,g in itertools.groupby(sorted(zip(a, b)), key=lambda x:x[0]) }
    print(result)
    

    输出:

    {0: [24, 53, 88], 1: [24, 32, 45, 53, 88], 2: [11, 22, 33, 44, 55, 66], 3: [77]}
    

    【讨论】:

    • 当然,我已经弄清楚了你的代码做了什么,但是用那种风格写的,不是很明显。对于 Python 的新手,我认为他们可能会发现您的代码难以理解,然后因此而忽略(或不理会)您的解决方案。只是一个建议,由你决定
    • 我建议不要在一行中写result?也许将groupby 的结果作为单独的变量提取出来?这条线太长了……
    • 这似乎比另一个答案更糟糕,因为你需要排序,而另一个答案没有排序,所以你的工作要做额外的工作。
    • @Daenyth,你的信息一点都不新鲜。该解决方案一开始就被标记为“替代”方式。
    • 如果list_a 已经被订购,你可以删除n log n 排序,而且lambda 增加了不必要的开销,itemgetter 总是一个更好的选择。 {k: [i for _, i in g] for k, g in groupby(zip(a, b), key=itemgetter(0))}
    【解决方案4】:

    也许我没有抓住重点,但至少我会尽力提供帮助。如果您必须列出并希望将它们放入 dict 中,请执行以下操作

    a = [1, 2, 3, 4]
    b = [5, 6, 7, 8]
    lists = [a, b] # or directly -> lists = [ [1, 2, 3, 4], [5, 6, 7, 8] ]
    new_dict = {}
    for idx, sublist in enumerate([a, b]): # or enumerate(lists)
        new_dict[idx] = sublist
    

    希望对你有帮助

    【讨论】:

    • 这与 OP 想要的相差甚远。 a 包含 b 中值的键(有些键是重复的),根本不使用索引。你的只是创建{ 0: a, 1: b },使用lists中的索引。
    【解决方案5】:

    pandas 解决方案:

    设置:

    import pandas as pd
    
    a = [0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 4]
    
    b = pd.np.random.randint(0, 100, len(a)).tolist()
    
    >>> b
    Out[]: [28, 68, 71, 25, 25, 79, 30, 50, 17, 1, 35, 23, 52, 87, 21]
    
    
    df = pd.DataFrame(columns=['Group', 'Value'], data=list(zip(a, b)))  # Create a dataframe
    
    >>> df
    Out[]:
        Group  Value
    0       0     28
    1       0     68
    2       0     71
    3       1     25
    4       1     25
    5       1     79
    6       1     30
    7       1     50
    8       2     17
    9       2      1
    10      2     35
    11      3     23
    12      4     52
    13      4     87
    14      4     21
    

    解决办法:

    >>> df.groupby('Group').Value.apply(list).to_dict()
    Out[]:
    {0: [28, 68, 71],
     1: [25, 25, 79, 30, 50],
     2: [17, 1, 35],
     3: [23],
     4: [52, 87, 21]}
    

    演练:

    1. 从输入列表中创建一个pd.DataFramea 称为Groupb 称为Value
    2. df.groupby('Group') 基于a 创建组
    3. .Value.apply(list) 获取每个组的值并将其转换为 list
    4. .to_dict() 将生成的 DataFrame 转换为 dict

    时间:

    要了解 100,000 个组中包含 1,000,000 个值的测试集的时间安排:

    a = sorted(np.random.randint(0, 100000, 1000000).tolist())
    b = pd.np.random.randint(0, 100, len(a)).tolist()
    df = pd.DataFrame(columns=['Group', 'Value'], data=list(zip(a, b)))
    
    >>> df.shape
    Out[]: (1000000, 2)
    
    %timeit df.groupby('Group').Value.apply(list).to_dict()
    4.13 s ± 9.29 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    但老实说,它的效率可能不如@RomanPerekhrest 建议的itertools.groupby 或@Ajax1234 建议的defaultdict

    【讨论】:

      【解决方案6】:

      你可以用字典理解来做到这一点:

      list_a = [0, 0, 0, 1, 1, 1, 1, 1]
      list_b = [24, 53, 88, 32, 45, 24, 88, 53]
      my_dict = {key: [] for key in set(a)}  # my_dict = {0: [], 1: []}
      for a, b in zip(list_a, list_b):
          my_dict[a].append(b)
      # {0: [24, 53, 88], 1: [32, 45, 24, 88, 53]}
      

      奇怪的是,您似乎无法使用 dict.fromkeys(set(list_a), []) 完成这项工作,因为这会将所有键的值设置为等于 same 空数组:

      my_dict = dict.fromkeys(set(list_a), [])  # my_dict = {0: [], 1: []}
      my_dict[0].append(1)  # my_dict = {0: [1], 1: [1]}
      

      【讨论】:

      • 列表是可变的,您将一个对象/列表传递给 fromkeys,因此您共享对单个列表/对象的引用,它将与 a = [] 然后 d = {1: a, 2: a, 3: a} 相同。 my_dict = dict.fromkeys(set(list_a), tuple());my_dict[0] += (1,) 会显示 {0: (1,), 1: (), 9999: ()},但会增加使用每个 += 创建新对象/元组的开销。
      【解决方案7】:

      或者事先做字典理解,然后由于所有键都带有空列表的值,遍历两个列表的zip,然后将第二个列表的值添加到字典的键中,命名第一个列表的值,不需要try-except 子句(或 if 语句),查看键是否存在,因为事先的字典理解:

      d={k:[] for k in l}
      for x,y in zip(l,l2):
         d[x].append(y)
      

      现在:

      print(d)
      

      是:

      {0: [24, 53, 88], 1: [32, 45, 24, 88, 53], 9999: [1]}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-25
        • 2021-11-11
        • 1970-01-01
        • 2022-10-01
        • 2020-10-14
        • 1970-01-01
        相关资源
        最近更新 更多