Sèkun blog


Avec la librairie pandas: gérer les listes et les objets au format json comme élément d'un DataFrame

2024-08-22 dev | tags : pandas python

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 = (
    # Charge le fichier json
    pd.read_json("data.json")
    # Crée autant de ligne que d'élément dans la liste d'un
    # élément de `friends`
    .explode("friends")
    # On utilise pip pour récupérer le dataframe via la variable
    # `l` à cet étape
    .pipe(lambda l: l.join(
        # Convertir l'objet en dataframe
        pd.json_normalize(l.friends)
        # Ajoute un prefix aux colonnes du dataframe
          .add_prefix("friends_")
    ))
    # Supprime la variables friends plus nécessaire
    .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")
)
Article publié le 22 août 2024.