Comment json_normalize une colonne avec des NaN
Sep 13 2020
- Cette question est spécifique aux colonnes de données dans un
pandas.DataFrame - Cette question dépend si les valeurs dans les colonnes sont
str,dictou lelisttype. - Cette question traite des
NaNvaleurs, quanddf.dropna().reset_index(drop=True)n'est pas une option valide.
Cas 1
- Avec une colonne de
strtype, les valeurs de la colonne doivent être converties endicttype, avecast.literal_eval, avant utilisation.json_normalize.
import numpy as np
import pandas as pd
from ast import literal_eval
df = pd.DataFrame({'col_str': ['{"a": "46", "b": "3", "c": "12"}', '{"b": "2", "c": "7"}', '{"c": "11"}', np.NaN]})
col_str
0 {"a": "46", "b": "3", "c": "12"}
1 {"b": "2", "c": "7"}
2 {"c": "11"}
3 NaN
type(df.iloc[0, 0])
[out]: str
df.col_str.apply(literal_eval)
Erreur:
df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan
Cas 2
- Avec une colonne de
dicttype, utilisezpandas.json_normalizepour convertir les clés en en-têtes de colonne et les valeurs en lignes
df = pd.DataFrame({'col_dict': [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}, {"c": "11"}, np.NaN]})
col_dict
0 {'a': '46', 'b': '3', 'c': '12'}
1 {'b': '2', 'c': '7'}
2 {'c': '11'}
3 NaN
type(df.iloc[0, 0])
[out]: dict
pd.json_normalize(df.col_dict)
Erreur:
pd.json_normalize(df.col_dict) results in AttributeError: 'float' object has no attribute 'items'
Cas 3
- Dans une colonne de
strtype, avec l'dictintérieur alist. - Pour normaliser la colonne
- appliquer
literal_eval, car l'explosion ne fonctionne pas sur lestrtype - éclater la colonne pour séparer les
dictslignes séparées - normaliser la colonne
- appliquer
df = pd.DataFrame({'col_str': ['[{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]', '[{"b": "2", "c": "7"}, {"c": "11"}]', np.nan]})
col_str
0 [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]
1 [{"b": "2", "c": "7"}, {"c": "11"}]
2 NaN
type(df.iloc[0, 0])
[out]: str
df.col_str.apply(literal_eval)
Erreur:
df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan
Réponses
2 TrentonMcKinney Sep 13 2020 at 23:59
- Comme indiqué dans un commentaire, il y a toujours la possibilité de:
df = df.dropna().reset_index(drop=True)- C'est parfait pour les données factices ici, ou lorsqu'il s'agit d'une trame de données où les autres colonnes n'ont pas d'importance.
- Ce n'est pas une excellente option pour les dataframes avec des colonnes supplémentaires requises.
Cas 1
- Puisque la colonne contient des
strtypes, remplissez avec'{}'(astr)
import numpy as np
import pandas as pd
from ast import literal_eval
df = pd.DataFrame({'col_str': ['{"a": "46", "b": "3", "c": "12"}', '{"b": "2", "c": "7"}', '{"c": "11"}', np.NaN]})
col_str
0 {"a": "46", "b": "3", "c": "12"}
1 {"b": "2", "c": "7"}
2 {"c": "11"}
3 NaN
type(df.iloc[0, 0])
[out]: str
# fillna
df.col_str = df.col_str.fillna('{}')
# convert the column to dicts
df.col_str = df.col_str.apply(literal_eval)
# use json_normalize
df = df.join(pd.json_normalize(df.col_str)).drop(columns=['col_str'])
# display(df)
a b c
0 46 3 12
1 NaN 2 7
2 NaN NaN 11
3 NaN NaN NaN
Cas 2
- Puisque la colonne contient des
dicttypes, remplissez avec{}(pas unstr) - Cela doit être rempli en utilisant un dict-compréhension, car
fillna({})ne fonctionne pas
df = pd.DataFrame({'col_dict': [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}, {"c": "11"}, np.NaN]})
col_dict
0 {'a': '46', 'b': '3', 'c': '12'}
1 {'b': '2', 'c': '7'}
2 {'c': '11'}
3 NaN
type(df.iloc[0, 0])
[out]: dict
# fillna
df.col_dict = df.col_dict.fillna({i: {} for i in df.index})
# use json_normalize
df = df.join(pd.json_normalize(df.col_dict)).drop(columns=['col_dict'])
# display(df)
a b c
0 46 3 12
1 NaN 2 7
2 NaN NaN 11
3 NaN NaN NaN
Cas 3
- Remplissez le
NaNsavec'[]'(astr) - Maintenant
literal_evalva travailler .explodepeut être utilisé sur la colonne pour séparer lesdictvaleurs en lignes- Maintenant le
NaNsbesoin d'être rempli avec{}(pas unstr) - Ensuite, la colonne peut être normalisée
- Pour le cas où la colonne est
listsdedicts, qui n'est pas destrtype, passez à.explode.
df = pd.DataFrame({'col_str': ['[{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]', '[{"b": "2", "c": "7"}, {"c": "11"}]', np.nan]})
col_str
0 [{"a": "46", "b": "3", "c": "12"}, {"b": "2", "c": "7"}]
1 [{"b": "2", "c": "7"}, {"c": "11"}]
2 NaN
type(df.iloc[0, 0])
[out]: str
# fillna
df.col_str = df.col_str.fillna('[]')
# literal_eval
df.col_str = df.col_str.apply(literal_eval)
# explode
df = df.explode('col_str').reset_index(drop=True)
# fillna again
df.col_str = df.col_str.fillna({i: {} for i in df.index})
# use json_normalize
df = df.join(pd.json_normalize(df.col_str)).drop(columns=['col_str'])
# display(df)
a b c
0 46 3 12
1 NaN 2 7
2 NaN 2 7
3 NaN NaN 11
4 NaN NaN NaN