使用json_normalize处理嵌套列时触发AttributeError问题排查
问题:展开含Decimal类型的嵌套列Pandas DataFrame时触发AttributeError
我有一个Pandas DataFrame,其中2列为包含Decimal类型值的嵌套列,执行df.tail(1).to_dict('list')得到如下数据:
{'nested_col1': [array([{'key1': 'CO', 'key2': Decimal('8.940000000')}], dtype=object)], 'nested_col2': [array([{'key3': 'CO', 'key4': 'P14', 'key5': Decimal('8.940000000'), 'key6': None}], dtype=object)]}
尝试用以下代码展开DataFrame:
df = (df.drop(cols, axis=1) .join(pd.concat( [pd.json_normalize(df[x].explode(), errors='ignore').applymap( lambda x: str(x) if isinstance(x, (int, float)) else x).add_prefix(f'{x}.') for x in cols], axis=1)))
但在部分场景下触发报错:
Traceback (most recent call last): File "data_load.py.py", line 365, in <module> df = prepare_data(data, transaction_id, cohort_no) File "data_load.py.py", line 274, in prepare_data df = flatten_dataframe(cols_to_explode, df) File "data_load.py.py", line 204, in flatten_dataframe df1 = pd.concat([pd.json_normalize(df[c].explode()) for c in cols], File "data_load.py.py", line 204, in <listcomp> df1 = pd.concat([pd.json_normalize(df[c].explode()) for c in cols], File "/project1/venv/lib/python3.6/site-packages/pandas/io/json/_normalize.py", line 270, in _json_normalize if any([isinstance(x, dict) for x in y.values()] for y in data): File "/project1/venv/lib/python3.6/site-packages/pandas/io/json/_normalize.py", line 270, in <genexpr> if any([isinstance(x, dict) for x in y.values()] for y in data): AttributeError: 'float' object has no attribute 'values' failed to run commands: exit status 1
期望输出为带前缀的列名,格式如下:
nested_col1.key1,nested_col1.key2,nested_col2.key3...
请问我是否遗漏了什么?有没有更优的实现方式?
解决方案
错误原因
报错是因为df[c].explode()后出现了float类型的值(大概率是np.nan),而非预期的字典/嵌套结构。旧版本的pd.json_normalize会尝试遍历元素的.values()方法,但float类型没有这个属性,因此触发AttributeError。
优化实现代码
from decimal import Decimal import numpy as np import pandas as pd def flatten_nested_columns(df, target_cols): flattened_dfs = [] for col in target_cols: # 1. 预处理:把非数组/字典的元素转为包含空字典的数组,避免explode后出现单个非字典值 processed = df[col].apply( lambda x: x if isinstance(x, (list, np.ndarray)) else [{}] if pd.isna(x) else [x] ) # 2. 展开嵌套数组 exploded = processed.explode() # 3. 标准化字典:转换Decimal为字符串,确保每个元素都是字典 normalized_df = pd.json_normalize( exploded.apply( lambda x: {k: str(v) if isinstance(v, Decimal) else v for k, v in x.items()} if isinstance(x, dict) else {} ) ).add_prefix(f"{col}.") flattened_dfs.append(normalized_df) # 合并原表(移除原嵌套列)和展开后的列 return df.drop(target_cols, axis=1).join(pd.concat(flattened_dfs, axis=1)) # 调用示例 cols = ["nested_col1", "nested_col2"] df = flatten_nested_columns(df, cols)
关键处理点
- 异常值预处理:提前把NaN或单个非字典值转为包含空字典的数组,确保
explode后所有元素都是可解析的结构 - Decimal类型转换:显式将Decimal转为字符串,避免后续序列化或类型兼容问题
- 容错性保障:强制确保传给
pd.json_normalize的每个元素都是字典,即使是空字典,避免旧版Pandas的解析报错
额外注意
你的环境使用的是Python3.6搭配旧版Pandas,pd.json_normalize的容错性较差,提前做数据清洗是关键。如果存在多层嵌套需求,可以给pd.json_normalize添加max_level参数控制展开深度。
内容的提问来源于stack exchange,提问作者Dcook
相关产品推荐
相关产品推荐

