Подтвердить что ты не робот

Список диктов в/из диктов списков

Я хочу переключаться между словарями списков (одинаковой длины):

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

и список словарей:

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

Я ищу самый чистый способ переключения между двумя формами.

4b9b3361

Ответ 1

Возможно, стоит подумать об использовании 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

Ответ 2

Для тех из вас, кто любит умные/хакерские реплики.

Вот DL - LD:

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 немного хакер, так как вы предполагаете, что ключи одинаковы в каждом dict. Также обратите внимание, что я не одобряю использование такого кода в любой реальной системе.

Ответ 3

Чтобы перейти из списка словарей, это просто:

Вы можете использовать эту форму:

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

Переход на другую сторону проблематичен. Вы должны иметь некоторую информацию о порядке вставки в списке из ключей из словаря. Напомним, что порядок ключей в dict не обязательно совпадает с порядком ввода.

Для хихиканья предположим, что порядок вставки основан на отсортированных клавишах. Затем вы можете сделать это следующим образом:

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

Если ваш вопрос был основан на любопытстве, есть ваш ответ. Если у вас есть реальная проблема, позвольте мне предложить вам пересмотреть свои структуры данных. Ни один из них не является очень масштабируемым решением.

Ответ 4

Если вам разрешено использовать внешние пакеты, Pandas отлично работает для этого:

import pandas as pd
pd.DataFrame(DL).to_dict('list')

Какие выходы:

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

Ответ 5

Вот однолинейные решения (разбросанные по нескольким строкам для удобочитаемости), которые я придумал:

если dl - ваш исходный список списков:

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

Затем здесь, как преобразовать его в список dicts:

ld = [{key:value[index] for key in dl.keys()}
         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]))]

и здесь, как преобразовать это обратно в список списков:

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.

Вы можете упростить это, если предположите, что все dicts в вашем списке будут иметь одинаковые ключи:

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

Ответ 6

cytoolz.dicttoolz.merge_with

Документы

from cytoolz.dicttoolz import merge_with

merge_with(list, *LD)

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

Версия без Cython

Документы

from toolz.dicttoolz import merge_with

merge_with(list, *LD)

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

Ответ 7

Модуль python в pandas может дать вам простое для понимания решение. В дополнение к ответу @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

Здесь мой маленький script:

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) Предположим, что у вас одинаковая длина для списков

Ответ 9

Самый чистый способ я могу думать о летней пятнице. В качестве бонуса он поддерживает списки разной длины (но в этом случае DLtoLD(LDtoDL(l)) больше не идентичен).

  • Из списка в dict

    На самом деле менее чистая, чем версия @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
    
  • От dict до списка

    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
    

Ответ 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, когда итерация по c и исключение StopIteration при оценке a.

С другой стороны, я не могу понять, что вы на самом деле пытаетесь сделать; возможно, было бы более разумно проектировать структуру данных, которая соответствует вашим требованиям, вместо того, чтобы пытаться привязать ее к существующим структурам данных. (Например, список dicts - это плохой способ представления результата запроса к базе данных.)

Ответ 12

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