Convierta la lista de diccionarios a un pandas DataFrame

658

Tengo una lista de diccionarios como este:

[{'points': 50, 'time': '5:00', 'year': 2010}, 
{'points': 25, 'time': '6:00', 'month': "february"}, 
{'points':90, 'time': '9:00', 'month': 'january'}, 
{'points_h1':20, 'month': 'june'}]

Y quiero convertir esto en pandas DataFramecomo este:

      month  points  points_h1  time  year
0       NaN      50        NaN  5:00  2010
1  february      25        NaN  6:00   NaN
2   january      90        NaN  9:00   NaN
3      june     NaN         20   NaN   NaN

Nota: El orden de las columnas no importa.

¿Cómo puedo convertir la lista de diccionarios en un DataFrame de pandas como se muestra arriba?

appleLover
fuente

Respuestas:

951

Supongamos que des su lista de dictados, simplemente:

pd.DataFrame(d)
joris
fuente
3
¿Cómo podría uno usar uno de los pares clave / valor como índice (por ejemplo, tiempo)?
CatsLoveJazz
66
@CatsLoveJazz Puedes hacerlo df = df.set_index('time')después
joris
1
@CatsLoveJazz No, eso no es posible al convertir desde un dict.
joris
66
A partir de Pandas 0.19.2, no se menciona esto en la documentación, al menos no en los documentos parapandas.DataFrame
Leo Alekseyev
1
'{"":{"...Tenga en cuenta que para un diccionario anidado utiliza el enfoque json_normalize, vea la respuesta detallada de @ cs95
Lorenz
136

¿Cómo convierto una lista de diccionarios en un DataFrame de pandas?

Las otras respuestas son correctas, pero no se ha explicado mucho en términos de ventajas y limitaciones de estos métodos. El objetivo de esta publicación será mostrar ejemplos de estos métodos en diferentes situaciones, discutir cuándo usar (y cuándo no usar), y sugerir alternativas.


DataFrame()` DataFrame.from_records()` y.from_dict()

Dependiendo de la estructura y el formato de sus datos, hay situaciones en las que los tres métodos funcionan, o algunos funcionan mejor que otros, o algunos no funcionan en absoluto.

Considere un ejemplo muy artificial.

np.random.seed(0)
data = pd.DataFrame(
    np.random.choice(10, (3, 4)), columns=list('ABCD')).to_dict('r')

print(data)
[{'A': 5, 'B': 0, 'C': 3, 'D': 3},
 {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 {'A': 2, 'B': 4, 'C': 7, 'D': 6}]

Esta lista consta de "registros" con cada clave presente. Este es el caso más simple que podrías encontrar.

# The following methods all produce the same output.
pd.DataFrame(data)
pd.DataFrame.from_dict(data)
pd.DataFrame.from_records(data)

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Word en las orientaciones del diccionario: orient='index'/'columns'

Antes de continuar, es importante hacer la distinción entre los diferentes tipos de orientaciones de diccionario y el apoyo con pandas. Hay dos tipos principales: "columnas" e "índice".

orient='columns'
Los diccionarios con la orientación de "columnas" tendrán sus claves correspondientes a las columnas en el DataFrame equivalente.

Por ejemplo, dataarriba está en las "columnas" orientar.

data_c = [
 {'A': 5, 'B': 0, 'C': 3, 'D': 3},
 {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 {'A': 2, 'B': 4, 'C': 7, 'D': 6}]

pd.DataFrame.from_dict(data_c, orient='columns')

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Nota: Si está utilizando pd.DataFrame.from_records, se supone que la orientación es "columnas" (no puede especificar lo contrario), y los diccionarios se cargarán en consecuencia.

orient='index'
Con este oriente, se supone que las claves corresponden a valores de índice. Este tipo de datos es el más adecuado para pd.DataFrame.from_dict.

data_i ={
 0: {'A': 5, 'B': 0, 'C': 3, 'D': 3},
 1: {'A': 7, 'B': 9, 'C': 3, 'D': 5},
 2: {'A': 2, 'B': 4, 'C': 7, 'D': 6}}

pd.DataFrame.from_dict(data_i, orient='index')

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

Este caso no se considera en el OP, pero aún es útil saberlo.

Establecer índice personalizado

Si necesita un índice personalizado en el DataFrame resultante, puede configurarlo utilizando el index=...argumento.

pd.DataFrame(data, index=['a', 'b', 'c'])
# pd.DataFrame.from_records(data, index=['a', 'b', 'c'])

   A  B  C  D
a  5  0  3  3
b  7  9  3  5
c  2  4  7  6

Esto no es compatible con pd.DataFrame.from_dict.

Manejo de llaves / columnas faltantes

Todos los métodos funcionan listos para usar cuando se manejan diccionarios con claves / valores de columna faltantes. Por ejemplo,

data2 = [
     {'A': 5, 'C': 3, 'D': 3},
     {'A': 7, 'B': 9, 'F': 5},
     {'B': 4, 'C': 7, 'E': 6}]

# The methods below all produce the same output.
pd.DataFrame(data2)
pd.DataFrame.from_dict(data2)
pd.DataFrame.from_records(data2)

     A    B    C    D    E    F
0  5.0  NaN  3.0  3.0  NaN  NaN
1  7.0  9.0  NaN  NaN  NaN  5.0
2  NaN  4.0  7.0  NaN  6.0  NaN

Lectura de subconjuntos de columnas

"¿Qué pasa si no quiero leer en cada columna"? Puede especificar esto fácilmente utilizando el columns=...parámetro

Por ejemplo, del diccionario de ejemplo data2anterior, si desea leer solo las columnas "A", "D" y "F", puede hacerlo pasando una lista:

pd.DataFrame(data2, columns=['A', 'D', 'F'])
# pd.DataFrame.from_records(data2, columns=['A', 'D', 'F'])

     A    D    F
0  5.0  3.0  NaN
1  7.0  NaN  5.0
2  NaN  NaN  NaN

Esto no es compatible pd.DataFrame.from_dictcon las "columnas" orientadas por defecto.

pd.DataFrame.from_dict(data2, orient='columns', columns=['A', 'B'])

ValueError: cannot use columns parameter with orient='columns'

Lectura de subconjuntos de filas

No es compatible con ninguno de estos métodos directamente . Tendrá que iterar sobre sus datos y realizar una eliminación inversa en el lugar a medida que itera. Por ejemplo, para extraer sólo el 0 º y 2 º filas de data2arriba, puede utilizar:

rows_to_select = {0, 2}
for i in reversed(range(len(data2))):
    if i not in rows_to_select:
        del data2[i]

pd.DataFrame(data2)
# pd.DataFrame.from_dict(data2)
# pd.DataFrame.from_records(data2)

     A    B  C    D    E
0  5.0  NaN  3  3.0  NaN
1  NaN  4.0  7  NaN  6.0

La panacea: json_normalizepara datos anidados

Una alternativa fuerte y robusta a los métodos descritos anteriormente es la json_normalizefunción que funciona con listas de diccionarios (registros) y, además, también puede manejar diccionarios anidados.

pd.io.json.json_normalize(data)

   A  B  C  D
0  5  0  3  3
1  7  9  3  5
2  2  4  7  6

pd.io.json.json_normalize(data2)

     A    B  C    D    E
0  5.0  NaN  3  3.0  NaN
1  NaN  4.0  7  NaN  6.0

Nuevamente, tenga en cuenta que los datos pasados json_normalizedeben estar en el formato de lista de diccionarios (registros).

Como se mencionó, json_normalizetambién puede manejar diccionarios anidados. Aquí hay un ejemplo tomado de la documentación.

data_nested = [
  {'counties': [{'name': 'Dade', 'population': 12345},
                {'name': 'Broward', 'population': 40000},
                {'name': 'Palm Beach', 'population': 60000}],
   'info': {'governor': 'Rick Scott'},
   'shortname': 'FL',
   'state': 'Florida'},
  {'counties': [{'name': 'Summit', 'population': 1234},
                {'name': 'Cuyahoga', 'population': 1337}],
   'info': {'governor': 'John Kasich'},
   'shortname': 'OH',
   'state': 'Ohio'}
]

pd.io.json.json_normalize(data_nested, 
                          record_path='counties', 
                          meta=['state', 'shortname', ['info', 'governor']])

         name  population    state shortname info.governor
0        Dade       12345  Florida        FL    Rick Scott
1     Broward       40000  Florida        FL    Rick Scott
2  Palm Beach       60000  Florida        FL    Rick Scott
3      Summit        1234     Ohio        OH   John Kasich
4    Cuyahoga        1337     Ohio        OH   John Kasich

Para obtener más información sobre los argumentos metay record_path, consulte la documentación.


Resumiendo

Aquí hay una tabla de todos los métodos discutidos anteriormente, junto con características / funcionalidades compatibles.

ingrese la descripción de la imagen aquí

* Use orient='columns'y luego transponga para obtener el mismo efecto que orient='index'.

cs95
fuente
8
Woah! De acuerdo, esto junto con Merging SO post pertenecen a la API. Debes contribuir a la documentación de los pandas si aún no lo has hecho. Ted Petrou acaba de publicar un artículo de LinkedIn sobre la popularidad de los pandas en Stack Overflow y menciona que la falta de buena documentación contribuye al volumen de preguntas aquí.
Scott Boston
2
@ScottBoston Tienes toda la razón, lo he escuchado suficientes veces ahora que sé que es algo en lo que debería pensar más seriamente. Creo que la documentación puede ser una excelente manera de ayudar a los usuarios, más que publicar en preguntas que solo llegarían a una fracción de la misma audiencia.
cs95
1
es una buena respuesta, creo que es hora de que
volvamos a
3
@ely: de todos modos, esa nunca es razón para no escribir respuestas aquí . Cualquier respuesta puede quedar desactualizada, eso es por lo que votamos, y existen diferentes perspectivas y diferentes objetivos aquí, y siempre es valioso tener diferentes formas de explicar lo mismo.
Martijn Pieters
1
@MartijnPieters Cuestiono y no estoy de acuerdo con tu última afirmación, pero en general estoy de acuerdo contigo. No siempre es un valor aditivo recopilar diferentes respuestas a la misma pregunta, especialmente si algunas de las respuestas son actualizaciones o diferencias condicionales basadas en otras respuestas. En el peor de los casos, esas respuestas pueden ser destructivas cuando se compaginan (en lugar de usar la respuesta más actualizada para simplemente editar la respuesta anterior en un estado más correcto). Pero de nuevo, estoy muy de acuerdo contigo.
ely
83

En pandas 16.2, tuve que hacer pd.DataFrame.from_records(d)para que esto funcionara.

szeitlin
fuente
1
Lo bueno de este enfoque es que también funciona condeque
MBZ
3
funciona bien con pandas 0.17.1con solución @joris
Anton Protopopov
2
Usinig 0.14.1 y la solución de @joris no funcionaron, pero esto funcionó
mchen
13
En 0.18.1, uno debe usar from_recordssi no todos los diccionarios tienen las mismas claves.
fredcallaway
23

También puedes usarlo pd.DataFrame.from_dict(d)como:

In [8]: d = [{'points': 50, 'time': '5:00', 'year': 2010}, 
   ...: {'points': 25, 'time': '6:00', 'month': "february"}, 
   ...: {'points':90, 'time': '9:00', 'month': 'january'}, 
   ...: {'points_h1':20, 'month': 'june'}]

In [12]: pd.DataFrame.from_dict(d)
Out[12]: 
      month  points  points_h1  time    year
0       NaN    50.0        NaN  5:00  2010.0
1  february    25.0        NaN  6:00     NaN
2   january    90.0        NaN  9:00     NaN
3      june     NaN       20.0   NaN     NaN
shivsn
fuente
La pregunta es sobre la construcción de un marco de datos a partir de una lista de dicts, no de una sola, dictcomo supuso en su respuesta.
a_guest
@a_guest verifica la respuesta actualizada. No estoy asumiendo.
shivsn
2

Sé que algunas personas se encontrarán con esto y no encontrarán nada que ayude. La forma más fácil que he encontrado para hacerlo es así:

dict_count = len(dict_list)
df = pd.DataFrame(dict_list[0], index=[0])
for i in range(1,dict_count-1):
    df = df.append(dict_list[i], ignore_index=True)

¡Espero que esto ayude a alguien!

scottapotamus
fuente
1
list=[{'points': 50, 'time': '5:00', 'year': 2010}, 
{'points': 25, 'time': '6:00', 'month': "february"}, 
{'points':90, 'time': '9:00', 'month': 'january'}, 
{'points_h1':20, 'month': 'june'}]

y simple llamada:

pd=DataFrame.from_dict(list, orient='columns', dtype=None)

print(pd)
Günel
fuente
0

Pyhton3: la mayoría de las soluciones enumeradas anteriormente funcionan. Sin embargo, hay casos en los que no se requiere el número de fila del marco de datos y cada fila (registro) debe escribirse individualmente.

El siguiente método es útil en ese caso.

import csv

my file= 'C:\Users\John\Desktop\export_dataframe.csv'

records_to_save = data2 #used as in the thread. 


colnames = list[records_to_save[0].keys()] 
# remember colnames is a list of all keys. All values are written corresponding
# to the keys and "None" is specified in case of missing value 

with open(myfile, 'w', newline="",encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(colnames)
    for d in records_to_save:
        writer.writerow([d.get(r, "None") for r in colnames])
Soum
fuente
0

Para convertir una lista de diccionarios en un DataFrame de pandas, puede usar "append":

Tenemos un diccionario llamado dicy DIC tiene 30 elementos de la lista ( list1, list2, ..., list30)

  1. Paso 1: definir una variable para mantener el resultado (por ejemplo: total_df)
  2. paso2: inicializar total_dfconlist1
  3. Paso 3: use "for loop" para agregar todas las listas a total_df
total_df=list1
nums=Series(np.arange(start=2, stop=31))
for num in nums:
    total_df=total_df.append(dic['list'+str(num)])
Armin Ahmadi Nasab
fuente
¿Cuál es la ventaja de este enfoque sobre el enfoques propuestos por @ CS95 en su detallada respuesta de dos años respecto DataFrame(), DataFrame.from_records()y .from_dict()?
Jeremy Caney
Probé todos los métodos anteriores para un diccionario que tiene 30 listas, solo obtuve la respuesta usando la función Agregar.
Armin Ahmadi Nasab