可以使用 pandas.json_normalize() 将具有公共键的字典列表转换为 pandas.DataFrame。
由于它是一种常用的JSON格式,可以通过Web API获取,所以能够将其转换为pandas.DataFrame是非常方便的。
在此,对以下内容进行说明。
使用 pandas.read_json() 直接读取 JSON 字符串或文件作为 pandas.DataFrame 而不是由字典或列表组成的对象。
请注意,pandas.json_normalize() 是从 pandas 1.0.0 提供的,在以前的版本中是作为 pandas.io.json.json_normalize() 提供的。从 1.2.2 开始,pandas.io.json.json_normalize() 仍然可以使用,但已弃用,并且会出现警告(FutureWarning)。
以下面的字典列表为例:
import pandas as pd
l_simple = [{'name': 'Alice', 'age': 25},
{'name': 'Bob'}]
字典中的key键成为列标签(列名),key不存在时的元素成为缺失值NaN。
print(pd.DataFrame(l_simple))
# name age
# 0 Alice 25.0
# 1 Bob NaN
与 pandas.json_normalize() 的结果相同。
print(pd.json_normalize(l_simple))
# name age
# 0 Alice 25.0
# 1 Bob NaN
以字典为字典值的嵌套字典列表为例。
l_nested = [{'name': 'Alice', 'age': 25, 'id': {'x': 2, 'y': 8}},
{'name': 'Bob', 'id': {'x': 10, 'y': 4}}]
使用 pandas.DataFrame() 时,值字典被转换为元素。
print(pd.DataFrame(l_nested))
# name age id
# 0 Alice 25.0 {'x': 2, 'y': 8}
# 1 Bob NaN {'x': 10, 'y': 4}
使用 pandas.json_normalize() 还将嵌套字典转换为每个键的单独列。
print(pd.json_normalize(l_nested))
# name age id.x id.y
# 0 Alice 25.0 2 8
# 1 Bob NaN 10 4
嵌套部分,.默认为列名。这个分隔符可以用参数 sep 改变。
print(pd.json_normalize(l_nested, sep='_'))
# name age id_x id_y
# 0 Alice 25.0 2 8
# 1 Bob NaN 10 4
如果字典值是如下所示的字典列表。
l_complex = [{'label': 'X',
'info' : {'n': 'nx', 'm': 'mx'},
'data': [{'a': 1, 'b': 2},
{'a': 3, 'b': 4}]},
{'label': 'Y',
'info' : {'n': 'ny', 'm': 'my'},
'data': [{'a': 10, 'b': 20},
{'a': 30, 'b': 40}]}]
默认情况下,字典列表成为一个元素。
print(pd.json_normalize(l_complex))
# label data info.n info.m
# 0 X [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}] nx mx
# 1 Y [{'a': 10, 'b': 20}, {'a': 30, 'b': 40}] ny my
如果在参数 record_path 中指定一个键,则只会转换与该键对应的值。可以使用参数 record_prefix 将前缀添加到列名。
print(pd.json_normalize(l_complex, record_path='data'))
# a b
# 0 1 2
# 1 3 4
# 2 10 20
# 3 30 40
print(pd.json_normalize(l_complex, record_path='data', record_prefix='data_'))
# data_a data_b
# 0 1 2
# 1 3 4
# 2 10 20
# 3 30 40
如果要转换其他键值,请使用参数 meta 指定它。可以使用参数 meta_prefix 将前缀添加到列名。
print(pd.json_normalize(l_complex, record_path='data'))
# a b
# 0 1 2
# 1 3 4
# 2 10 20
# 3 30 40
print(pd.json_normalize(l_complex, record_path='data', record_prefix='data_'))
# data_a data_b
# 0 1 2
# 1 3 4
# 2 10 20
# 3 30 40
如果要转换其他键值,请使用参数 meta 指定它。可以使用参数 meta_prefix 将前缀添加到列名。
print(pd.json_normalize(l_complex, record_path='data',
meta='label'))
# a b label
# 0 1 2 X
# 1 3 4 X
# 2 10 20 Y
# 3 30 40 Y
print(pd.json_normalize(l_complex, record_path='data',
meta='label', meta_prefix='meta_'))
# a b meta_label
# 0 1 2 X
# 1 3 4 X
# 2 10 20 Y
# 3 30 40 Y
如果 meta 指定的键对值是一个字典,则可以在列表 [[, ], …] 中指定子键。默认情况下,列名是 .,但在这里你可以用参数 sep 更改分隔符。
print(pd.json_normalize(l_complex, record_path='data',
meta='info'))
# a b info
# 0 1 2 {'n': 'nx', 'm': 'mx'}
# 1 3 4 {'n': 'nx', 'm': 'mx'}
# 2 10 20 {'n': 'ny', 'm': 'my'}
# 3 30 40 {'n': 'ny', 'm': 'my'}
print(pd.json_normalize(l_complex, record_path='data',
meta=[['info', 'n'], ['info', 'm']]))
# a b info.n info.m
# 0 1 2 nx mx
# 1 3 4 nx mx
# 2 10 20 ny my
# 3 30 40 ny my
print(pd.json_normalize(l_complex, record_path='data',
meta=[['info', 'n'], ['info', 'm']],
sep='_'))
# a b info_n info_m
# 0 1 2 nx mx
# 1 3 4 nx mx
# 2 10 20 ny my
# 3 30 40 ny my
要将此示例中的所有元素转换为 pandas.DataFrame,请设置:
print(pd.json_normalize(l_complex, record_path='data',
meta=['label', ['info', 'n'], ['info', 'm']],
sep='_'))
# a b label info_n info_m
# 0 1 2 X nx mx
# 1 3 4 X nx mx
# 2 10 20 Y ny my
# 3 30 40 Y ny my
请注意,即使单独指定子键,它也必须是像 meta=[[, ]] 这样的列表列表。如果 meta=[, ] 则出错。
print(pd.json_normalize(l_complex, record_path='data',
meta=[['info', 'n']]))
# a b info.n
# 0 1 2 nx
# 1 3 4 nx
# 2 10 20 ny
# 3 30 40 ny
# print(pd.json_normalize(l_complex, record_path='data',
# meta=['info', 'n']))
# KeyError: "Try running with errors='ignore' as key 'n' is not always present"