J'ai un dataframe df
qui charge les données d'une base de données. La plupart des colonnes sont des chaînes json tandis que certaines sont même une liste de jsons. Par exemple:
id name columnA columnB
1 John {"dist": "600", "time": "0:12.10"} [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "3rd", "value": "200"}, {"pos": "total", "value": "1000"}]
2 Mike {"dist": "600"} [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "total", "value": "800"}]
...
Comme vous pouvez le voir, toutes les lignes n'ont pas le même nombre d'éléments dans les chaînes json pour une colonne.
Ce que je dois faire, c'est garder les colonnes normales comme id
et name
telles quelles et aplatir les colonnes json comme ceci:
id name columnA.dist columnA.time columnB.pos.1st columnB.pos.2nd columnB.pos.3rd columnB.pos.total
1 John 600 0:12.10 500 300 200 1000
2 Mark 600 NaN 500 300 Nan 800
J'ai essayé d'utiliser json_normalize
ainsi:
from pandas.io.json import json_normalize
json_normalize(df)
Mais il semble y avoir quelques problèmes avec keyerror
. Quelle est la bonne façon de procéder?
Voici une solution utilisant json_normalize()
à nouveau en utilisant une fonction personnalisée pour obtenir les données dans le format correct compris par la fonction json_normalize
.
import ast
from pandas.io.json import json_normalize
def only_dict(d):
'''
Convert json string representation of dictionary to a python dict
'''
return ast.literal_eval(d)
def list_of_dicts(ld):
'''
Create a mapping of the tuples formed after
converting json strings of list to a python list
'''
return dict([(list(d.values())[1], list(d.values())[0]) for d in ast.literal_eval(ld)])
A = json_normalize(df['columnA'].apply(only_dict).tolist()).add_prefix('columnA.')
B = json_normalize(df['columnB'].apply(list_of_dicts).tolist()).add_prefix('columnB.pos.')
Enfin, joignez le DFs
sur l'index commun pour obtenir:
df[['id', 'name']].join([A, B])
EDIT: - Selon le commentaire de @MartijnPieters, la façon recommandée de décoder les chaînes json serait d'utiliser json.loads()
qui est beaucoup plus rapide par rapport à l'utilisation ast.literal_eval()
si vous savez que la source de données est JSON.
Le plus rapide semble être:
json_struct = json.loads(df.to_json(orient="records"))
df_flat = pf.io.json.json_normalize(json_struct)