explode仅支持list/tuple,dict需先转为list或用pd.json_normalize;遇None/NaN会删行,需预填充;多列explode须zip合并防笛卡尔积;索引重复影响groupby/join,应合理控制ignore_index。

explode 只能展开 list 或 tuple,不能直接处理 dict
如果你的列里存的是 dict,直接调用 explode() 会报错:TypeError: explode() missing 1 required positional argument: 'column'(其实是内部判断类型失败),更常见的错误是 AttributeError: 'dict' object has no attribute 'len'。因为 explode 底层只认可迭代且有长度的序列类型,dict 虽可迭代但不满足其校验逻辑。
解决办法是先转成 list:比如把每行 {'a': 1, 'b': 2} 变成 [{'a': 1, 'b': 2}],再 explode;或者提前用 pd.json_normalize() 拆解结构。
- 对纯
list列(如['x', 'y']或[[1,2], [3]]),explode('col_name')直接可用 - 对
dict列,先用df['col'].apply(lambda x: [x] if isinstance(x, dict) else x)统一转 list - 若 dict 内部还有嵌套 list,建议别硬刚
explode,改用pd.json_normalize(df['col'])更稳
处理含 None / NaN 的列时 explode 会丢行
explode 遇到 None、np.nan 或空 list([])时,默认行为是**删除整行**——不是留空,是彻底 drop。这点和直觉相反,容易导致数据量莫名减少。
- 确认缺失值状态:用
df['col'].apply(type).value_counts(dropna=False)查看实际类型 - 想保留空/缺失行,得先填充:比如
df['col'] = df['col'].apply(lambda x: x if isinstance(x, list) else []),再explode - 或用
df.explode('col', ignore_index=False)+ 后续fillna补空,但注意ignore_index控制的是结果索引重排,不改变丢行逻辑
多列同时 explode 容易错位,必须用 merge 而非链式 explode
如果两列都是 list,且想按位置一一对应展开(比如 names=['A','B'] 和 ages=[25,30] 展成两行),不能写 df.explode('names').explode('ages') —— 这会做笛卡尔积,变成 4 行。
正确做法是先用 zip 合并再展开:
df['zipped'] = df.apply(lambda r: list(zip(r['names'], r['ages'])) if isinstance(r['names'], list) else [], axis=1)
df = df.explode('zipped')
df[['names', 'ages']] = pd.DataFrame(df['zipped'].tolist(), index=df.index)
- 注意
zip在任一输入为None时返回空迭代器,需提前处理 - 如果 list 长度不一致,
zip以最短为准,丢尾部元素;要补全得用itertools.zip_longest - 避免在大表上用
apply+zip,性能差;超 10 万行建议改用pd.concat+ 索引对齐
explode 后索引重复问题影响 groupby / join
explode 默认保留原索引,所以展开后会出现重复索引(比如原 index=0 的行炸出 3 行,index 都是 0)。这会导致 groupby 计算异常,或 join 时匹配错行。
- 加
ignore_index=True重置索引最简单,但会丢失原始行关系 - 想保留原始分组线索,可先
df = df.reset_index().explode(...).set_index('index') - 后续要做
merge,推荐用df.explode(..., ignore_index=False)+ 显式reset_index(drop=True)控制节奏
嵌套结构展开从来不是单点操作,真正麻烦的往往是展开后字段对齐、空值语义、索引一致性这些细节——炸开只是第一步,别急着链式调用。










