Pandas 2.x中使用zip循环后输出DataFrame为空的解决方法
Pandas 2.x中循环zip操作DataFrame赋值失效问题解决
问题背景
将旧的Pandas 1.x代码升级到2.x后,尝试通过zip()循环将输入DataFrame复制到输出DataFrame时遇到问题:循环结束后输出DataFrame(dfo1、dfo2)仍为空,无法保留循环内的赋值操作。核心疑问是:使用zip()时,如何在循环内正确复制DataFrame,让全局的输出DataFrame在循环后可正常使用?
测试代码
import numpy as np import pandas as pd data=np.random.randn(24).reshape(6,4) df=pd.DataFrame(data,index=['1','2','3','4','5','6'],columns=['A','B','C','D']) data2=np.random.randn(24).reshape(6,4) df2=pd.DataFrame(data,index=['1','2','3','4','5','6'],columns=['A','B','C','D']) dflist=[df,df2] # 输入DataFrame列表 print(f'Address of df is:{hex(id(df))}, and df2 is:{hex(id(df2))}') # 初始化空的输出DataFrame dfo1=pd.DataFrame() dfo2=pd.DataFrame() dfout=[dfo1,dfo2] print(f'Address of dfo1 is:{hex(id(dfo1))}, and dfo2 is:{hex(id(dfo2))}') print('***Begin Loop') for i,(dfo,dfx) in enumerate(zip(dfout,dflist)): print(f'\ti={i}') print(f'\t**Start of loop: Address of dfo is:{hex(id(dfo))}. Address of dfx is:{hex(id(dfx))} ') dfx.iloc[3,3]=0 # 测试修改输入DataFrame # 尝试的赋值方式均未生效 # dfo=dfx.copy() # dfo.append(dfx) # Pandas 1.x可用,2.x已移除 dfo=pd.concat([dfo,dfx]) dfo.iloc[0,0]=99 # 测试修改输出DataFrame print(f'\t**End of loop: Address of dfo is:{hex(id(dfo))}. Address of dfx is:{hex(id(dfx))} ') print(f'\tdfx shape is{dfx.shape}') print(f'\tdfo shape is{dfo.shape}') print(dfo) # 循环后dfo1仍为空 print(f'Address of dfo1 is: {hex(id(dfo1))}') print(f'dfo1 shape after loop is{dfo1.shape}') # 输出(0,4) dfo1
问题根源
循环中dfo是从dfout列表中取出的局部变量,当执行dfo = pd.concat([dfo, dfx])时,并不是修改原来的空DataFrame对象,而是创建了一个新的DataFrame对象,并让局部变量dfo指向这个新对象。原来的dfout列表中的元素(即全局的dfo1、dfo2)并没有被修改,它们仍然指向最初的空DataFrame,所以循环结束后dfo1、dfo2还是空的。
解决方法
方法1:直接修改输出列表dfout的元素
循环中不直接给局部变量dfo赋值,而是通过索引修改dfout列表对应的位置,让其指向新的DataFrame对象:
print('***Begin Loop') for i,(dfo,dfx) in enumerate(zip(dfout,dflist)): print(f'\ti={i}') print(f'\t**Start of loop: Address of dfo is:{hex(id(dfo))}. Address of dfx is:{hex(id(dfx))} ') dfx.iloc[3,3]=0 # 直接修改dfout列表的对应元素 new_dfo = pd.concat([dfo, dfx]) new_dfo.iloc[0,0] = 99 dfout[i] = new_dfo # 关键:更新列表中的元素 print(f'\t**End of loop: Address of new_dfo is:{hex(id(new_dfo))} ') print(f'\tdfx shape is{dfx.shape}') print(f'\tnew_dfo shape is{new_dfo.shape}') print(new_dfo) # 循环后更新dfo1和dfo2 dfo1, dfo2 = dfout print(f'Address of dfo1 is: {hex(id(dfo1))}') print(f'dfo1 shape after loop is{dfo1.shape}') # 输出(6,4) dfo1
方法2:预先不创建空DataFrame,直接生成输出列表
如果不需要保留初始的空DataFrame,可以直接在循环中构建新的输出列表,避免局部变量与全局变量的混淆:
dfout = [] for dfx in dflist: dfx.iloc[3,3] = 0 dfo = pd.concat([pd.DataFrame(), dfx]) dfo.iloc[0,0] = 99 dfout.append(dfo) dfo1, dfo2 = dfout print(f'dfo1 shape after loop is{dfo1.shape}') # 输出(6,4) dfo1
补充说明
- Pandas 2.x中已移除
DataFrame.append()方法,官方推荐使用pd.concat()替代,这部分你的方向是对的,问题出在变量赋值的逻辑上,而非concat()的用法。 - 若需要深拷贝DataFrame(避免后续修改输入DataFrame影响输出),可以在赋值时使用
dfx.copy(deep=True),比如new_dfo = pd.concat([dfo, dfx.copy(deep=True)])。
内容的提问来源于stack exchange,提问作者wpilgri
相关产品推荐
相关产品推荐

