字典列表和列表字典之间的转换

133

我想在一个长度相等的列表字典中来回切换:

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

和一个字典列表:

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

1
不清楚您如何解释DL的顺序?例如,如果您有许多元素,它们会失去插入顺序。如果'a'和'b'以不同的顺序从DL中出来,那么结果LD的顺序应该是什么? - dawg
1
很好的建议,delnan和drewk,谢谢。我正在处理从CSV文件中导入数据的问题,其中列的顺序并不重要。 - Adam Greenhall
1
@AdamGreenhall,既然你正在处理CSV文件,我强烈建议你尝试一下pandas。它有点像是强化版的R数据框架。 - Midnighter
14个回答

161

对于那些喜欢巧妙/狡猾的一行代码的人。

这里是从DLLD的转换:

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

将字典中的 LD 转换为 DL(每个字典中的所有键都是相同的):

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

或者将LD转换为DL(每个字典中的所有键不相同):

common_keys = set.intersection(*map(set, LD))
v = {k: [dic[k] for dic in LD] for k in common_keys}
print(v)

此外,请注意我不赞成在任何真实系统中使用这样的代码。

2
LD转DL返回元组而不是列表,这可能更加理想。顺便说一句,非常好用和方便的一行代码。 - theta
3
@GillBates 你是正确的;LD->DL代码依赖于所有字典以相同的顺序排序,这是一个非常糟糕的假设。我已经替换了有问题的代码。 - Aran-Fey
处理具有不同键的字典: 可以将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.keys())for D in LD ])} - tozCSS
我确实喜欢巧妙/狡猾的一行代码。此外,我认为这些是非常Pythonic的好解决方案。它们使用核心Python习语,我认为Python程序员应该熟悉。 - LudvigH
1
请注意,如果您使用类似于此的巧妙的一行代码,请添加注释,最好引用此SO答案。您未来的自己会感谢您的。 - Contango

19

如果您被允许使用外部包,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]}

7
可能是版本问题,但在 pandas 0.18.1 中上述代码将返回 {'a': [0, 1], 'b': [2, 3]}。使用 pd.DataFrame(DL).to_dict('records') 可以达到预期效果。 - ayhan

17

或许考虑使用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

NiftyгЂ‚дҢ иѓҢи§Әй‡ЉдёЂдё‹е°†[(0,2),(1,3)]е’Њ[[0,2],[1,3]]дә йЂ’з»™np.arrayзљ„еЊғ别吗пәџе…·дҢ“жқӨиҮөпәЊдёғ什么第дғЊдёҒдёҚиµ·дҢњз”Ёпәџ - Adam Greenhall
@Adam Greenhall: 你提出了一个非常好的问题。我不知道完整的答案。我知道在某些情况下,NumPy对列表和元组的区别比Python更为明显。dtype语法的文档http://docs.scipy.org/numpy/docs/numpy.doc.structured_arrays/说,在使用"[l]ist argument"定义dtype时,“记录结构是由一系列元组定义的”。但我不知道为什么必须这样。 - unutbu
@unutbu 谢谢,非常有趣。我之前没有听说过结构化数组。文档链接现在已经更改为:https://numpy.org/doc/stable/user/basics.rec.html。此外,我注意到文档中提到结构化数组“旨在与C代码进行接口,并用于结构化缓冲区的低级操作...希望处理表格数据(例如存储在csv文件中)的用户可能会发现其他pydata项目更适合,例如xarray、pandas或DataArray。” - Bill

14

从字典列表中获取数据很简单:

您可以使用以下格式:

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

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


12

以下是我想出来的一行解决方案(为了易读性而分成多行):

如果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())))]

如果你假设所有列表的长度相同,那么你可以简化操作并提高性能,方法如下:

Which, if you assume that all your lists are the same length, you can simplify and gain a performance increase by going to:

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

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

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

2
回滚到第4个版本的目的是什么?请注意,您在第二个代码片段中的范围设置错误,并且删除Python代码格式会使代码变得更糟。 - Martin Bonner supports Monica

6

Python中的pandas模块可以为您提供易于理解的解决方案。作为对@chiang答案的补充,D-to-L和L-to-D的解决方案如下:

import pandas as pd
DL = {'a': [0, 1], 'b': [2, 3]}
out1 = pd.DataFrame(DL).to_dict('records')

输出:

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

在另一个方向上:
LD = [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]
out2 = pd.DataFrame(LD).to_dict('list')

输出:

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

6

cytoolz.dicttoolz.merge_with

Docs

from cytoolz.dicttoolz import merge_with

merge_with(list, *LD)

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

Non-cython version

Docs

from toolz.dicttoolz import merge_with

merge_with(list, *LD)

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

感谢 @piRSquared 让我认识了 cytoolz 这个工具。它一直在哪里躲着我呢?! :) - Niklas B

2
我能想到的最简洁的夏季星期五方式。作为奖励,它支持不同长度的列表(但在这种情况下,DLtoLD(LDtoDL(l)) 不再是身份)。
  1. From list to dict

    Actually less clean than @dwerk's defaultdict version.

    def LDtoDL (l) :
       result = {}
       for d in l :
          for k, v in d.items() :
             result[k] = result.get(k,[]) + [v] #inefficient
       return result
    
  2. From dict to list

    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}]... - dsteinhoefel

2

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

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

1

这是我的小脚本:

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) 你假设列表具有相同的长度


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