Convertir la liste des dictionnaires en un DataFrame pandas

658

J'ai une liste de dictionnaires comme celui-ci:

[{'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'}]

Et je veux transformer cela en pandas DataFramecomme celui-ci:

      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

Remarque: L'ordre des colonnes n'a pas d'importance.

Comment puis-je transformer la liste des dictionnaires en un DataFrame pandas comme indiqué ci-dessus?

appleLover
la source

Réponses:

951

Supposons que dvotre liste de pronostics soit simple:

pd.DataFrame(d)
joris
la source
3
Comment pourrait-on utiliser l'une des paires clé / valeur comme index (par exemple, le temps)?
CatsLoveJazz
6
@CatsLoveJazz Vous pouvez le faire df = df.set_index('time')après
joris
1
@CatsLoveJazz Non, ce n'est pas possible lors de la conversion à partir d'un dict.
joris
6
À partir de Pandas 0.19.2, il n'y a aucune mention de cela dans la documentation, du moins pas dans les documents pourpandas.DataFrame
Leo Alekseyev
1
N'oubliez pas que pour un dictionnaire imbriqué, '{"":{"...vous utilisez l'approche json_normalize, voir la réponse détaillée de @ cs95
Lorenz
136

Comment convertir une liste de dictionnaires en DataFrame pandas?

Les autres réponses sont correctes, mais peu a été expliqué en termes d'avantages et de limites de ces méthodes. Le but de cet article sera de montrer des exemples de ces méthodes dans différentes situations, de discuter quand utiliser (et quand ne pas utiliser) et suggérer des alternatives.


DataFrame(),, DataFrame.from_records()et.from_dict()

Selon la structure et le format de vos données, il existe des situations où les trois méthodes fonctionnent, ou certaines fonctionnent mieux que d'autres, ou certaines ne fonctionnent pas du tout.

Prenons un exemple très artificiel.

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

Cette liste comprend des "enregistrements" avec toutes les clés présentes. C'est le cas le plus simple que vous puissiez rencontrer.

# 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

Mot sur les orientations du dictionnaire: orient='index'/'columns'

Avant de continuer, il est important de faire la distinction entre les différents types d'orientations de dictionnaire et de soutien aux pandas. Il existe deux types principaux: "colonnes" et "index".

orient='columns'
Les dictionnaires avec l'orientation "colonnes" verront leurs clés correspondre aux colonnes du DataFrame équivalent.

Par exemple, dataci-dessus est dans l'orientation "colonnes".

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

Remarque: Si vous utilisez pd.DataFrame.from_records, l'orientation est supposée être des "colonnes" (vous ne pouvez pas spécifier le contraire) et les dictionnaires seront chargés en conséquence.

orient='index'
Avec cet orient, les clés sont supposées correspondre aux valeurs d'index. Ce type de données est le mieux adapté 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

Ce cas n'est pas pris en compte dans le PO, mais reste utile à connaître.

Définition d'un index personnalisé

Si vous avez besoin d'un index personnalisé sur le DataFrame résultant, vous pouvez le définir à l'aide de l' index=...argument.

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

Ceci n'est pas pris en charge par pd.DataFrame.from_dict .

Gérer les clés / colonnes manquantes

Toutes les méthodes fonctionnent immédiatement lors de la manipulation de dictionnaires avec des clés / valeurs de colonne manquantes. Par exemple,

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

Lecture d'un sous-ensemble de colonnes

"Et si je ne veux pas lire dans chaque colonne"? Vous pouvez facilement spécifier cela en utilisant lecolumns=... paramètre.

Par exemple, à partir de l'exemple de dictionnaire data2ci-dessus, si vous souhaitez lire uniquement les colonnes "A", "D" et "F", vous pouvez le faire en passant une liste:

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

Ceci n'est pas supporté par pd.DataFrame.from_dictavec les "colonnes" orient par défaut.

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

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

Lecture d'un sous-ensemble de lignes

Non pris en charge par aucune de ces méthodes directement . Vous devrez itérer sur vos données et effectuer une suppression inverse sur place au fur et à mesure de votre itération. Par exemple, pour extraire uniquement les 0 ème et 2 ème lignes d'en data2haut, vous pouvez utiliser:

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 panacée: json_normalizepour les données imbriquées

Une alternative forte et robuste aux méthodes décrites ci-dessus est la json_normalizefonction qui fonctionne avec des listes de dictionnaires (enregistrements), et en plus peut également gérer des dictionnaires imbriqués.

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

Encore une fois, gardez à l'esprit que les données transmises json_normalizedoivent être au format liste de dictionnaires (enregistrements).

Comme mentionné, json_normalizepeut également gérer les dictionnaires imbriqués. Voici un exemple tiré de la documentation.

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

Pour plus d'informations sur les arguments metaet record_path, consultez la documentation.


Résumer

Voici un tableau de toutes les méthodes décrites ci-dessus, ainsi que les caractéristiques / fonctionnalités prises en charge.

entrez la description de l'image ici

* Utilisez orient='columns'puis transposez pour obtenir le même effet que orient='index'.

cs95
la source
8
Woah! D'accord, ainsi que la fusion de la publication SO appartiennent à l'API. Vous devriez contribuer à la documentation des pandas si vous ne l'avez pas déjà fait. Ted Petrou vient de publier un article LinkedIn sur la popularité des pandas sur Stack Overflow et mentionne que le manque de bonne documentation contribue au volume de questions ici.
Scott Boston
2
@ScottBoston Vous avez absolument raison, j'ai entendu cela assez de fois maintenant que je sais que c'est quelque chose à quoi je devrais réfléchir plus sérieusement. Je pense que la documentation peut être un excellent moyen d'aider les utilisateurs, plus que de publier des questions qui n'atteindraient qu'une fraction du même public.
cs95
1
c'est une bonne réponse, je pense qu'il est temps pour nous de revenir dans ces questions courantes sous la version la plus récente des pandas :-)
YOBEN_S
3
@ely: ce n'est jamais une raison pour ne pas écrire de réponses ici, de toute façon . Toute réponse peut devenir obsolète, c'est pour cela que nous votons, et différentes perspectives et différents objectifs existent ici, et il est toujours utile d'avoir différentes façons d'expliquer la même chose.
Martijn Pieters
1
@MartijnPieters Je remets en question et suis en désaccord avec votre dernière affirmation mais dans l'ensemble je suis d'accord avec vous. Il n'est pas toujours utile de rassembler différentes réponses à la même question, en particulier si certaines des réponses sont des mises à jour ou des différences conditionnelles basées sur d'autres réponses. Dans le pire des cas, ces réponses peuvent être destructrices de valeur lorsqu'elles sont regroupées (par opposition à l'utilisation de la réponse plus mise à jour pour simplement modifier l'ancienne réponse dans un état plus correct). Mais encore une fois, je suis largement d'accord avec vous.
ely
83

Dans les pandas 16.2, je devais faire pd.DataFrame.from_records(d)pour que cela fonctionne.

szeitlin
la source
1
la bonne chose à propos de cette approche est qu'elle fonctionne également avec deque
MBZ
3
fonctionne bien avec les pandas 0.17.1 avec la solution @joris
Anton Protopopov
2
Usinig 0.14.1 et la solution de @joris n'ont pas fonctionné mais cela n'a pas fonctionné
mchen
13
Dans 0.18.1, il faut utiliser from_recordssi les dictionnaires n'ont pas tous les mêmes clés.
fredcallaway
23

Vous pouvez également utiliser pd.DataFrame.from_dict(d)comme:

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
la source
La question concerne la construction d'un bloc de données à partir d'une liste de dicts, et non à partir d'un seul dictcomme vous l'avez supposé dans votre réponse.
a_guest
@a_guest vérifie la réponse mise à jour. Je ne suppose pas.
shivsn
2

Je sais que quelques personnes rencontreront cela et ne trouveront rien ici qui aide. La façon la plus simple que j'ai trouvée de le faire est la suivante:

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)

J'espère que cela aide quelqu'un!

scottapotamus
la source
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'}]

et appel simple:

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

print(pd)
Günel
la source
0

Pyhton3: La plupart des solutions répertoriées précédemment fonctionnent. Cependant, il existe des cas où le numéro de ligne de la trame de données n'est pas requis et chaque ligne (enregistrement) doit être écrite individuellement.

La méthode suivante est utile dans ce cas.

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
la source
0

Pour convertir une liste de dictionnaires en DataFrame pandas, vous pouvez utiliser "ajouter":

Nous avons un dictionnaire appelé dicet dic a 30 éléments de la liste ( list1, list2, ..., list30)

  1. étape 1: définir une variable pour conserver votre résultat (ex: total_df )
  2. étape 2: initialiser total_dfaveclist1
  3. étape3: utilisez "for loop" pour ajouter toutes les listes à 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
la source
Quel est l'avantage de cette approche sur les approches esquissées par @ CS95 dans leur détaillée ancienne réponse de deux ans en ce qui concerne DataFrame(), DataFrame.from_records()et .from_dict()?
Jeremy Caney
J'ai testé toutes les méthodes ci-dessus pour un dictionnaire qui a 30 listes, je n'ai obtenu la réponse qu'en utilisant la fonction Append.
Armin Ahmadi Nasab