dict - python list index
¿Cuál es una forma Pythonic de hacer la siguiente transformación en una lista de dictados? (9)
Al igual que esto:
(lambda f:
lambda l, r=[(), ()]: f(f, l, r)
)(lambda g, l, r:
r if len(l) == 0 else g(g, l[1:], [r[0]+(l[0][''name''],), r[1]+(l[0][''values''],)])
)([
{''name'': ''foo'', ''values'': [1, 2, 3, 4]},
{''name'': ''bar'', ''values'': [5, 6, 7, 8]},
{''name'': ''baz'', ''values'': [9, 9, 9, 9]}
])
Resultado:
[(''foo'', ''bar'', ''baz''), ([1, 2, 3, 4], [5, 6, 7, 8], [9, 9, 9, 9])]
Tengo una lista de dictados como este:
l = [{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}]
y me gustaría obtener una salida de este formulario:
>>> [(''foo'', ''bar''), ([1,2,3,4], [5,6,7,8])]
Pero, for
excepción for
-looping y anexión, no veo una solución. ¿Hay una manera más inteligente que hacer esto?
names = []
values = []
for d in l:
names.append(d[''name''])
values.append(d[''values''])
Aquí hay una forma recursiva de hacerlo:
def trans(l):
if l:
res = trans(l[1:])
res[0], res[1] = (l[0][''name''],) + res[0], (l[0][''values''],) + res[1]
return res
return [(),()]
No estoy seguro sobre el rendimiento, pero aquí hay otra toma que usa zip()
y desempaquetar:
list(zip(*[tuple(i.values()) for i in l]))
# [(''foo'', ''bar''), ([1, 2, 3, 4], [5, 6, 7, 8])]
Edición: como señaló @DeepSpace, se puede reducir aún más a:
list(zip(*(i.values() for i in l)))
Aquí hay una respuesta más larga pero más explícita si desea definir las órdenes usted mismo:
list(zip(*(tuple(map(lambda k: i.get(k), (''name'', ''values''))) for i in l)))
# [(''foo'', ''bar''), ([1, 2, 3, 4], [5, 6, 7, 8])]
Primero: su código es correcto, legible y eficiente, lo que me suena Pythonic. Tenga en cuenta que probablemente no quiera una lista de tuplas, sin embargo. Las tuplas son inmutables , por lo que no podrías agregar otro nombre a los names
.
Con un solo dict
Si los names
son únicos, puede convertir su lista de dictados en un dict grande:
>>> l = [{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}]
>>> data = {d[''name'']:d[''values''] for d in l}
>>> data
{''foo'': [1, 2, 3, 4], ''bar'': [5, 6, 7, 8]}
Puede obtener la información deseada directamente:
>>> data.keys()
dict_keys([''foo'', ''bar''])
>>> data.values()
dict_values([[1, 2, 3, 4], [5, 6, 7, 8]])
Si realmente quieres una lista de listas:
>>> [list(data.keys()), list(data.values())]
[[''foo'', ''bar''], [[1, 2, 3, 4], [5, 6, 7, 8]]]
Con pandas
Si está trabajando con una gran lista de dictados, es posible que desee considerar pandas
.
Podría inicializar un DataFrame
directamente:
>>> import pandas as pd
>>> df = pd.DataFrame([{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}])
>>> df
name values
0 foo [1, 2, 3, 4]
1 bar [5, 6, 7, 8]
Si necesita los nombres como un iterable, puede obtener la columna correspondiente:
>>> df[''name'']
0 foo
1 bar
Name: name, dtype: object
Si realmente necesitas una lista de nombres:
>>> list(df[''name''])
[''foo'', ''bar'']
Para obtener los nombres y valores juntos:
>>> df.values.T
array([[''foo'', ''bar''],
[list([1, 2, 3, 4]), list([5, 6, 7, 8])]], dtype=object)
Puede que esto no sea exactamente lo que tenía en mente, pero para datos tabulares como este, encuentro que los pandas
suelen ser la mejor solución a largo plazo:
>>> import pandas as pd
>>> l = [{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}]
>>> df = pd.DataFrame(l)
name values
0 foo [1, 2, 3, 4]
1 bar [5, 6, 7, 8]
Por lo general, usa el marco de datos directamente para cualquier cosa que necesite hacer, pero también puede convertirlo en una estructura de datos basada en listas:
>>> df[''name''].tolist(), df[''values''].tolist()
([''foo'', ''bar''], [[1, 2, 3, 4], [5, 6, 7, 8]])
Puede utilizar operator.itemgetter
para garantizar el orden de los valores:
from operator import itemgetter
fields = (''name'', ''values'')
res = list(zip(*map(itemgetter(*fields), L)))
print(res)
[(''foo'', ''bar''), ([1, 2, 3, 4], [5, 6, 7, 8])]
Si, asumiendo Python 3.6+, no puede garantizar el orden de inserción apropiado de los diccionarios dentro de su lista de entrada, deberá definir explícitamente un orden como se indica anteriormente.
Actuación
Si bien una lista de "comprensiones de tuplas" funciona, se vuelve ilegible e ineficiente al consultar más de un par de campos:
from operator import itemgetter
n = 10**6
L = [{''name'': ''foo'', ''values'': [1,2,3,4], ''name2'': ''zoo'', ''name3'': ''xyz'',
''name4'': ''def''}, {''name'': ''bar'', ''values'': [5,6,7,8], ''name2'': ''bart'',
''name3'': ''abc'', ''name4'': ''ghi''}] * n
%timeit [tuple(k["name"] for k in L), tuple(k["values"] for k in L),/
tuple(k["name2"] for k in L), tuple(k["name3"] for k in L),
tuple(k["name4"] for k in L)]
%timeit fields = (''name'', ''values'', ''name2'', ''name3'' ,''name4'');/
list(zip(*map(itemgetter(*fields), L)))
1 loop, best of 3: 1.25 s per loop
1 loop, best of 3: 1.04 s per loop
Usar la expresión del generador:
l = [{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}]
v = [tuple(k["name"] for k in l), tuple(k["values"] for k in l)]
print(v)
Salida:
[(''foo'', ''bar''), ([1, 2, 3, 4], [5, 6, 7, 8])]
Yo usaría una lista de comprensión (como la de eyllanesc) si estuviera escribiendo este código para el consumo público. Pero solo por diversión, aquí hay una línea que no usa ninguna for
s.
>>> l = [{''name'': ''foo'', ''values'': [1,2,3,4]}, {''name'': ''bar'', ''values'': [5,6,7,8]}]
>>> list(zip(*map(dict.values, l)))
[(''foo'', ''bar''), ([1, 2, 3, 4], [5, 6, 7, 8])]
(Tenga en cuenta que esto solo funciona de manera confiable si los diccionarios conservan el orden de inserción, lo cual no es el caso en todas las versiones de Python. CPython 3.6 lo hace como un detalle de implementación, pero solo tiene un comportamiento garantizado a partir del 3.7).
Desglose rápido del proceso:
- dict.values devuelve un objeto
dict_values
, que es un iterable que contiene todos los valores del dict. -
map
toma cada diccionario enl
y llama a dict.values en él, devolviendo un iterable de dict_values objetos. -
zip(*thing)
es una receta clásica de "transposición", que toma una iteración de iterables y la voltea efectivamente en diagonal. Por ejemplo, [[a, b], [c, d]] se convierte en [[a, c], [b, d]]. Esto pone todos los nombres en una tupla, y todos los valores en otra. -
list
convierte el objeto zip en una lista.
usa el mapa para esto
names = tuple(map(lambda d: d[''name''], l))
values = tuple(map(lambda d: d[''values''], l))
result = [names, values]