【问题标题】:List of dicts to/from dict of lists到/来自列表的字典的字典列表
【发布时间】:2011-07-30 07:52:20
【问题描述】:

我希望在列表字典(长度相同)之间来回切换:

DL = {'a': [0, 1], 'b': [2, 3]}

还有一个字典列表:

LD = [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

我正在寻找在两种形式之间切换的最简洁方式。

【问题讨论】:

  • 不清楚你将如何解释 DL 的顺序?即,如果您有很多元素,它们会丢失它们的插入顺序。如果 'a' 和 'b' 以不同的顺序从 DL 中出来,那么生成的 LD 的顺序应该是什么?
  • delnan 和drawk 的好建议,谢谢。我正在从列顺序无关紧要的 CSV 文件中导入数据。
  • @AdamGreenhall 因为您使用的是 CSV 文件,所以我强烈建议您尝试一下pandas。这有点像R 类固醇上的数据帧。

标签: python list dictionary


【解决方案1】:

适合那些喜欢聪明/老套的单行字的人。

这里是DLLD

v = [dict(zip(DL,t)) for t in zip(*DL.values())]
print(v)

LDDL

v = {k: [dic[k] for dic in LD] for k in LD[0]}
print(v)

LDDL 有点骇人听闻,因为您假设每个 dict 中的密钥都相同。另外,请注意,我不容忍在任何类型的实际系统中使用此类代码。

【讨论】:

  • LD 到 DL 返回元组而不是列表,这可能更可取,也可能不更可取。顺便说一句,非常好用的单线器
  • @GillBates 你是对的;代码 LD->DL 代码依赖于所有 dicts 以相同的方式排序,这是一个可怕的假设。我已经替换了错误的代码。
  • 处理具有不同键的字典:LD[0] 可以替换为 reduce(set.union, [set(D.keys()) for D in LD]) 然后 [dic[k] for dic in LD if k in dic],因此得到的一个衬里是:v = {k: [dic[k] for dic in LD if k in dic] for k in reduce(set.union, [set(D. LD 中 D 的 keys())])}
  • 我确实喜欢聪明/老套的单行字。我也认为这些是非常 Pythonic 的很好的解决方案。他们使用我认为 Python 程序员应该熟悉的核心 Python 习语。
  • 请 - 如果使用像这样聪明的 hacky one-liner,请添加评论,最好是对这个 SO 答案的引用。你未来的自己会感谢你的。
【解决方案2】:

或许可以考虑使用 numpy:

import numpy as np

arr = np.array([(0, 2), (1, 3)], dtype=[('a', int), ('b', int)])
print(arr)
# [(0, 2) (1, 3)]

在这里,我们访问按名称索引的列,例如'a',或'b'(有点像DL):

print(arr['a'])
# [0 1]

这里我们通过整数索引访问行(有点像LD):

print(arr[0])
# (0, 2)

行中的每个值都可以通过列名访问(类似于LD):

print(arr[0]['b'])
# 2

【讨论】:

  • 漂亮。你能解释一下将[(0,2),(1,3)][[0,2],[1,3]] 传递给np.array 之间的区别吗?具体为什么第二个不起作用?
  • @Adam Greenhall:你问了一个很好的问题。我不知道完整的答案。我知道 numpy 有时比 Python 更能区分列表和元组。 dtype 语法docs.scipy.org/numpy/docs/numpy.doc.structured_arrays 的文档说,在使用“[l]i​​st 参数定义 dtype 时......记录结构是用元组列表定义的。”但我不知道为什么一定要这样。
  • @unutbu 谢谢,非常有趣。我没有听说过结构化数组。文档链接现已更改:numpy.org/doc/stable/user/basics.rec.html。另外,我在文档中注意到它说结构化数组“用于与 C 代码接口和结构化缓冲区的低级操作......希望操作表格数据(例如存储在 csv 文件中)的用户可能会找到其他 pydata 项目更合适,比如 xarray、pandas 或 DataArray。”
【解决方案3】:

如果您被允许使用外部包,Pandas 非常适合:

import pandas as pd
pd.DataFrame(DL).to_dict(orient="records")

哪些输出:

[{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

你也可以使用orient="list"取回原来的结构

{'a': [0, 1], 'b': [2, 3]}

【讨论】:

  • 这可能是版本问题,但上面在 pandas 0.18.1 中返回 {'a': [0, 1], 'b': [2, 3]}pd.DataFrame(DL).to_dict('records') 按描述工作。
【解决方案4】:

要从字典列表中取出,很简单:

你可以使用这个表格:

DL={'a':[0,1],'b':[2,3], 'c':[4,5]}
LD=[{'a':0,'b':2, 'c':4},{'a':1,'b':3, 'c':5}]

nd={}
for d in LD:
    for k,v in d.items():
        try:
            nd[k].append(v)
        except KeyError:
            nd[k]=[v]

print nd     
#{'a': [0, 1], 'c': [4, 5], 'b': [2, 3]}

或者使用defaultdict:

nd=cl.defaultdict(list)
for d in LD:
   for key,val in d.items():
      nd[key].append(val)

print dict(nd.items())
#{'a': [0, 1], 'c': [4, 5], 'b': [2, 3]}

走另一条路是有问题的。您需要从字典中的键中获取一些关于插入顺序的信息。回想一下,字典中键的顺序不一定与原始插入顺序相同。

对于傻笑,假设插入顺序是基于排序的键。然后你可以这样做:

nl=[]
nl_index=[]

for k in sorted(DL.keys()):
    nl.append({k:[]})
    nl_index.append(k)

for key,l in DL.items():
    for item in l:
        nl[nl_index.index(key)][key].append(item)

print nl        
#[{'a': [0, 1]}, {'b': [2, 3]}, {'c': [4, 5]}]

如果您的问题是基于好奇心,那么您的答案就是。如果你有一个现实世界的问题,让我建议你重新考虑你的数据结构。这些似乎都不是一个非常可扩展的解决方案。

【讨论】:

    【解决方案5】:

    以下是我想出的单行解决方案(为了便于阅读,分散在多行中):

    如果 dl 是您的原始列表字典:

    dl = {"a":[0, 1],"b":[2, 3]}
    

    那么这里是如何将其转换为字典列表:

    ld = [{key:value[index] for key,value in dl.items()}
             for index in range(max(map(len,dl.values())))]
    

    如果您假设所有列表的长度相同,则可以通过以下步骤简化并提高性能:

    ld = [{key:value[index] for key, value in dl.items()}
            for index in range(len(dl.values()[0]))]
    

    如果dl 包含不对称列表,则以下工作正常:

    from itertools import product
    
    dl = {"a":[0, 1],"b":[2, 3, 4], "c":[5, 6, 7, 8]}
    
    ld = [dict(zip(dl.keys(), items)) 
            for items in product(*(dl.values()))]
    

    以下是将其转换回列表字典的方法:

    dl2 = {key:[item[key] for item in ld]
             for key in list(functools.reduce(
                 lambda x, y: x.union(y),
                 (set(dicts.keys()) for dicts in ld)
             ))
          }
    

    如果您使用的是 Python 2 而不是 Python 3,则可以在此处使用 reduce 而不是 functools.reduce

    如果您假设列表中的所有字典都具有相同的键,则可以简化此操作:

    dl2 = {key:[item[key] for item in ld] for key in ld[0].keys() }
    

    【讨论】:

    • 回滚到版本 4 的意义何在?请注意,您将第二个代码 sn -p 中的范围弄错了,并且删除 python 代码格式会使代码变得更糟。
    【解决方案6】:

    cytoolz.dicttoolz.merge_with

    Docs

    from cytoolz.dicttoolz import merge_with
    
    merge_with(list, *LD)
    
    {'a': [0, 1], 'b': [2, 3]}
    

    非 cython 版本

    Docs

    from toolz.dicttoolz import merge_with
    
    merge_with(list, *LD)
    
    {'a': [0, 1], 'b': [2, 3]}
    

    【讨论】:

    • 感谢@piRSquared 向我介绍了 cytoolz 的世界。我的一生都在哪里?! :)
    【解决方案7】:

    pandas的python模块可以给你一个通俗易懂的解决方案。作为@chiang回答的补充,D-to-L和L-to-D的解决方案如下:

    In [1]: import pandas as pd
    
    In [2]: DL = {'a': [0, 1], 'b': [2, 3]}
    
    In [3]: pd.DataFrame(DL).to_dict('records')
    Out[3]: [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]
    
    In [4]: LD = [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]
    
    In [5]: pd.DataFrame(LD).to_dict('list')
    Out[5]: {'a': [0, 1], 'b': [2, 3]}
    

    【讨论】:

      【解决方案8】:

      我能想到的最干净的方式是夏天的星期五。作为奖励,它支持不同长度的列表(但在这种情况下,DLtoLD(LDtoDL(l)) 不再是身份)。

      1. 从列表到字典

        实际上不如 @dwerk 的 defaultdict 版本干净。

        def LDtoDL (l) :
           result = {}
           for d in l :
              for k, v in d.items() :
                 result[k] = result.get(k,[]) + [v] #inefficient
           return result
        
      2. 从字典到列表

        def DLtoLD (d) :
           if not d :
              return []
           #reserve as much *distinct* dicts as the longest sequence
           result = [{} for i in range(max (map (len, d.values())))]
           #fill each dict, one key at a time
           for k, seq in d.items() :
              for oneDict, oneValue in zip(result, seq) :
             oneDict[k] = oneValue
           return result
        

      【讨论】:

      • 对我不起作用:DLtoLD({1: [3], 2: [4, 5]}) 产生 [{1: 3, 2: 4}, {2: 5}] 而我期望 [{1: 3, 2: 4} , {1:3, 2:5}]...
      【解决方案9】:

      这是我的小脚本:

      a = {'a': [0, 1], 'b': [2, 3]}
      elem = {}
      result = []
      
      for i in a['a']: # (1)
          for key, value in a.items():
              elem[key] = value[i]
          result.append(elem)
          elem = {}
      
      print result
      

      我不确定那是不是很漂亮。

      (1)您假设列表的长度相同

      【讨论】:

        【解决方案10】:

        这是一个没有使用任何库的解决方案:

        def dl_to_ld(initial):
            finalList = []
            neededLen = 0
        
            for key in initial:
                if(len(initial[key]) > neededLen):
                    neededLen = len(initial[key])
        
            for i in range(neededLen):
                finalList.append({})
        
            for i in range(len(finalList)):
                for key in initial:
                    try:
                        finalList[i][key] = initial[key][i]
                    except:
                        pass
        
            return finalList
        

        你可以这样称呼它:

        dl = {'a':[0,1],'b':[2,3]}
        print(dl_to_ld(dl))
        
        #[{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]
        

        【讨论】:

          【解决方案11】:

          如果你不介意生成器,你可以使用类似的东西

          def f(dl):
            l = list((k,v.__iter__()) for k,v in dl.items())
            while True:
              d = dict((k,i.next()) for k,i in l)
              if not d:
                break
              yield d
          

          由于技术原因,它不像它可能的那样“干净”:我最初的实现是 yield dict(...),但这最终是空字典,因为(在 Python 2.5 中)a for b in c 不区分 StopIteration 异常时在评估 a 时迭代 c 和 StopIteration 异常。

          另一方面,我无法弄清楚你真正想要做什么;设计一个满足您要求的数据结构而不是试图将其硬塞到现有的数据结构中可能更明智。 (例如,dicts 列表是表示数据库查询结果的糟糕方式。)

          【讨论】:

            【解决方案12】:

            我需要一种适用于不同长度列表的方法(所以这是对原始问题的概括)。由于我在这里没有找到任何符合我预期的代码,所以这是我的代码:

            def dict_of_lists_to_list_of_dicts(dict_of_lists: Dict[S, List[T]]) -> List[Dict[S, T]]:
                keys = list(dict_of_lists.keys())
                list_of_values = [dict_of_lists[key] for key in keys]
                product = list(itertools.product(*list_of_values))
            
                return [dict(zip(keys, product_elem)) for product_elem in product]
            

            例子:

            >>> dict_of_lists_to_list_of_dicts({1: [3], 2: [4, 5]})
            [{1: 3, 2: 4}, {1: 3, 2: 5}]
            >>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5]})
            [{1: 3, 2: 5}, {1: 4, 2: 5}]
            >>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5, 6]})
            [{1: 3, 2: 5}, {1: 3, 2: 6}, {1: 4, 2: 5}, {1: 4, 2: 6}]
            >>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5, 6], 7: [8, 9, 10]})
            [{1: 3, 2: 5, 7: 8},
             {1: 3, 2: 5, 7: 9},
             {1: 3, 2: 5, 7: 10},
             {1: 3, 2: 6, 7: 8},
             {1: 3, 2: 6, 7: 9},
             {1: 3, 2: 6, 7: 10},
             {1: 4, 2: 5, 7: 8},
             {1: 4, 2: 5, 7: 9},
             {1: 4, 2: 5, 7: 10},
             {1: 4, 2: 6, 7: 8},
             {1: 4, 2: 6, 7: 9},
             {1: 4, 2: 6, 7: 10}]
            

            【讨论】:

              【解决方案13】:
              DL={'a':[0,1,2,3],'b':[2,3,4,5]}
              LD=[{'a':0,'b':2},{'a':1,'b':3}]
              Empty_list = []
              Empty_dict = {}
              # to find length of list in values of dictionry
              len_list = 0
              for i in DL.values():
                  if len_list < len(i):
                      len_list = len(i)
              
              for k in range(len_list):        
                  for i,j in DL.items():
                      Empty_dict[i] = j[k]
                  Empty_list.append(Empty_dict)
                  Empty_dict = {}
              LD = Empty_list
              

              【讨论】:

              • 嗨,Anup,您能详细说明您的答案吗?
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多