Pandas DataFrame批量新增列如何避免碎片化导致对象为None
根因分析
PerformanceWarning: DataFrame is highly fragmented本身属于性能提示,不会直接导致DataFrame变为None,该异常是高频逐列插入触发的pandas旧版本边界bug:
- 每次通过
df[col_name] = value语法新增列时,pandas都会在底层拆分独立内存块存储新列。当前逻辑需要循环生成39*13=507个新列,逐次插入会产生数百个零散内存块,除了运行速度指数级下降外,在pandas 1.x~2.0早期版本中,当内存块数量超过内部处理阈值时,列赋值操作会出现静默失败,最终导致整个df对象返回None。 - 循环中插入
df = df.copy()无法解决问题:如果copy操作执行在赋值失败的节点之后,拿到的本身就是已经异常的对象;且DataFrame处于高度碎片化状态时,copy操作本身也可能触发相同的内部处理异常。
修复方案
按照警告提示调整实现逻辑,不要逐次往原df插入新列,先批量计算所有待新增列,再一次性拼接即可,同时可以优化原代码中的重复计算逻辑进一步提升效率:
import pandas as pd Bbands_list = [-3,-2.5,-2,-1.5,-1,0,-0.5,0.5,1,1.5,2,2.5,3] SMA_list = [1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,21,22,24,26,28,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100,125,150,175,200] new_columns = {} for m in SMA_list: # 同一滚动窗口的均值、标准差仅计算1次,消除原逻辑中每个b值重复计算滚动统计量的冗余 rolling_mean = df['Close'].rolling(m).mean() rolling_std = df['Close'].rolling(m).std() for b in Bbands_list: col_name = f"M{m}B{b}" new_columns[col_name] = (rolling_mean + rolling_std * b - df['Close']) / df['Close'] # 一次性拼接所有新列,从根源避免逐次插入导致的内存碎片化 df = pd.concat([df, pd.DataFrame(new_columns, index=df.index)], axis=1)
- 上述写法完全规避了逐次插入列的操作,不会触发碎片化警告,运行效率比原循环写法高5~10倍,也不会出现赋值静默失败的问题。
- 若当前使用的pandas版本低于2.0,建议升级到2.0以上稳定版,新版本重构了DataFrame底层存储逻辑,大幅降低了逐列插入的碎片化概率,同时修复了旧版本中赋值操作静默返回None的边界bug。
内容的提问来源于stack exchange,提问作者DataMonkey
相关产品推荐
相关产品推荐

