La librairie pandas est faite pour traiter des fichiers
plats. Parfois, on récupère des données au format json
, dont des
éléments sont des listes d'objets. Voici un exemple généré via
json-generator.com:
[{"name": "Carole Elliott", "friends": [{"id": 0, "name": "Bonner King"}, {"id": 1, "name": "Cora Cabrera"}, {"id": 2, "name": "Sophie Valdez"}]}, {"name": "Ladonna Leblanc", "friends": [{"id": 0, "name": "Jaclyn Dillard"}, {"id": 1, "name": "Dickson Burks"}, {"id": 2, "name": "Mendez Stanton"}]}, {"name": "Lula Downs", "friends": [{"id": 0, "name": "Marian Scott"}, {"id": 1, "name": "Pitts Rosa"}, {"id": 2, "name": "Harriet Blanchard"}]}, {"name": "Prince Bowen", "friends": [{"id": 0, "name": "Deloris Rojas"}, {"id": 1, "name": "May Good"}, {"id": 2, "name": "Saundra Preston"}]}, {"name": "Love Lang", "friends": [{"id": 0, "name": "Lana Dickson"}, {"id": 1, "name": "Erica Figueroa"}, {"id": 2, "name": "Monroe Gentry"}]}, {"name": "Stuart Mcdonald", "friends": [{"id": 0, "name": "Bass Kline"}, {"id": 1, "name": "Sylvia Sampson"}, {"id": 2, "name": "Hays Conway"}]}, {"name": "Andrea Whitley", "friends": [{"id": 0, "name": "Gibson Pena"}, {"id": 1, "name": "Avila Henry"}, {"id": 2, "name": "Rivers Griffin"}]}]
Le fichier est disponible via: data.json.
En important ce fichier via pandas, on obtient:
>>> import pandas as pd
>>> data = pd.read_json("data.json")
>>> data
name friends
0 Carole Elliott [{'id': 0, 'name': 'Bonner King'}, {'id': 1, '...
1 Ladonna Leblanc [{'id': 0, 'name': 'Jaclyn Dillard'}, {'id': 1...
2 Lula Downs [{'id': 0, 'name': 'Marian Scott'}, {'id': 1, ...
3 Prince Bowen [{'id': 0, 'name': 'Deloris Rojas'}, {'id': 1,...
4 Love Lang [{'id': 0, 'name': 'Lana Dickson'}, {'id': 1, ...
5 Stuart Mcdonald [{'id': 0, 'name': 'Bass Kline'}, {'id': 1, 'n...
6 Andrea Whitley [{'id': 0, 'name': 'Gibson Pena'}, {'id': 1, '...
Chaque élément de la variable friends est une liste. Il est
possible d'aplatir le DataFrame via sa méthode explode
. Chaque
ligne sera dupliquée selon le nombre d'élément de cette variable.
>>> data = data.explode("friends")
>>> data.head()
name friends
0 Carole Elliott {'id': 0, 'name': 'Bonner King'}
0 Carole Elliott {'id': 1, 'name': 'Cora Cabrera'}
0 Carole Elliott {'id': 2, 'name': 'Sophie Valdez'}
1 Ladonna Leblanc {'id': 0, 'name': 'Jaclyn Dillard'}
1 Ladonna Leblanc {'id': 1, 'name': 'Dickson Burks'}
La variable
friends n'est pas encore utilisable en l'état. On peut transformer
chaque clé de l'objet constituant sa valeur en colonne. On utilise
la fonction json_normalize
de pandas, qui renverra un DataFrame,
on appelle ensuite la méthode add_prefix
afin de préfixer le nom des
colonnes, on en aura donc deux: friend_id
et friends_name
.
>>> friends = pd.json_normalize(data.friends).add_prefix("friends_").head()
>>> friends
friends_id friends_name
0 0 Bonner King
1 1 Cora Cabrera
2 2 Sophie Valdez
3 0 Jaclyn Dillard
4 1 Dickson Burks
Comme json_normalize
est une fonction de pandas, et non une
méthode de DataFrame, il faut manuellement venir incorporer le
résultat à la variable data
.
data = data.join(friends)
>>> data.head()
name friends friends_id friends_name
0 Carole Elliott {'id': 0, 'name': 'Bonner King'} 0 Bonner King
0 Carole Elliott {'id': 1, 'name': 'Cora Cabrera'} 0 Bonner King
0 Carole Elliott {'id': 2, 'name': 'Sophie Valdez'} 0 Bonner King
1 Ladonna Leblanc {'id': 0, 'name': 'Jaclyn Dillard'} 1 Cora Cabrera
1 Ladonna Leblanc {'id': 1, 'name': 'Dickson Burks'} 1 Cora Cabrera
On peut finalement supprimer la variable friends
:
>>> data = data.drop(columns="friends")
>>> data.head()
name friends_id friends_name
0 Carole Elliott 0 Bonner King
0 Carole Elliott 0 Bonner King
0 Carole Elliott 0 Bonner King
1 Ladonna Leblanc 1 Cora Cabrera
1 Ladonna Leblanc 1 Cora Cabrera
On obtient un fichier plat.
Au final, il est possible de faire tout ça en une seule suite de
commande, comme suit:
data = (
pd.read_json("data.json")
.explode("friends")
.pipe(lambda l: l.join(
pd.json_normalize(l.friends)
.add_prefix("friends_")
))
.drop(columns="friends")
)
Sans les commentaires:
data = (
pd.read_json("data.json")
.explode("friends")
.pipe(lambda l: l.join(pd.json_normalize(l.friends).add_prefix("friends_")))
.drop(columns="friends")
)