如何使用 NaN 对列进行 json_normalize
Posted
技术标签:
【中文标题】如何使用 NaN 对列进行 json_normalize【英文标题】:How to json_normalize a column with NaNs 【发布时间】:2020-12-31 17:20:57 【问题描述】: 此问题特定于pandas.DataFrame
中的数据列
这个问题取决于列中的值是str
、dict
还是list
类型。
当df.dropna().reset_index(drop=True)
不是有效选项时,此问题涉及处理NaN
值。
案例一
对于str
类型的列,在使用.json_normalize
之前,必须将该列中的值转换为dict
类型,使用ast.literal_eval
。
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)
错误:
df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan
案例 2
对于dict
类型的列,使用pandas.json_normalize
将键转换为列标题,将值转换为行
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)
错误:
pd.json_normalize(df.col_dict) results in AttributeError: 'float' object has no attribute 'items'
案例 3
在str
类型的列中,dict
在list
内。
标准化列
应用literal_eval
,因为explode 不适用于str
类型
分解列以分隔dicts
以分隔行
规范化列
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)
错误:
df.col_str.apply(literal_eval) results in ValueError: malformed node or string: nan
【问题讨论】:
【参考方案1】: 始终可以选择:df = df.dropna().reset_index(drop=True)
这对于此处的虚拟数据或在处理其他列无关紧要的数据框时很好。
对于需要额外列的数据框来说不是一个很好的选择。
案例一
由于该列包含str
类型,因此用''
填充(一个str
)
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
案例 2
至少从 pandas 1.3.4
开始,pd.json_normalize(df.col_dict)
可以正常工作,至少对于这个简单的示例而言。
由于该列包含
dict
类型,因此使用
填充(不是str
)
这需要使用 dict-comprehension 来填充,因为 fillna()
不起作用
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
案例 3
-
用
'[]'
(str
)填充NaNs
现在literal_eval
可以工作了
可以在列上使用.explode
将dict
值分隔为行
现在NaNs
需要填写
(不是str
)
然后可以对列进行归一化
lists
的dicts
且不是str
类型的情况,请跳至.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
【讨论】:
以上是关于如何使用 NaN 对列进行 json_normalize的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 json_normalize 规范化嵌套的 json
pandas 如何使用 groupby 在标签中按日期对列进行分组?