Python While循环未更新DataFrame列计算问题求助
问题排查:循环迭代中DataFrame列未更新的原因与解决
问题描述
编写了一段Python代码,希望循环执行计算并更新DataFrame的NEST_VAL_PER列:按KEY1分组取该列的75分位数,基于分位数完成系列计算,且每次迭代后分位数应因列值更新而降低。但实际运行时,DataFrame未被有效更新,所有迭代结果均与第一次相同。尝试过删除DataFrame、使用临时DataFrame、复制DataFrame等方法,问题仍未解决。
示例代码:
mbd_out_ub2 = mbd_out_ub1 mbd_out_ub2_len = len(mbd_out_ub2) plt_mbd_c1_all = pd.DataFrame() brd2c2_all = pd.DataFrame() iterc=1 ### plt_mbd_c >> this is the data frame with data before the loop starts plt_mbd_c0 = plt_mbd_c.copy() plt_mbd_c0 = plt_mbd_c0[plt_mbd_c0['UB_OUT']==1] while (iterc < 10): plt_mbd_c1 = plt_mbd_c0.copy() brd2c2 = plt_mbd_c1.groupby('KEY1')['NEST_VAL_PER'].agg([('KEY1_CNT','count'),('PER1c', lambda x: x.quantile(0.75))]).reset_index() brd2c2_all = brd2c2_all.append(brd2c2).reset_index(drop=True) plt_mbd_c1 = pd.merge(plt_mbd_c1,brd2c2[['KEY1','PER1c']],on='KEY1', how='left') del brd2c2, plt_mbd_c0 plt_mbd_c1['NEST_VAL_PER1'] = plt_mbd_c1['PER1c'] * (plt_mbd_c1['EVAL_LP_%'] / 100) plt_mbd_c1['NEST_VAL_PER1'] = np.where((plt_mbd_c1['BRD_OUT_FLAG'] == 0),plt_mbd_c1['NEST_VAL'],plt_mbd_c1['NEST_VAL_PER1'] ) plt_mbd_c1['SALESC'] = plt_mbd_c1['NEST_VAL_PER1']/plt_mbd_c1['PROJR']/plt_mbd_c1['NEWPRICE'] plt_mbd_c1['C_SALES_C'] = np.where(plt_mbd_c1['OUT_FLAG'] == 1,plt_mbd_c1['SALESC'],plt_mbd_c1['SALESUNIT']) plt_mbd_c1['NEST_VAL_PER'] = plt_mbd_c1['C_SALES_C'] * plt_mbd_c1['PROJR'] * plt_mbd_c1['NEWPRICE'] plt_mbd_c1['ITER'] = iterc plt_mbd_c1_all = plt_mbd_c1_all.append(plt_mbd_c1).reset_index(drop=True) plt_mbd_c1.drop(['PER1c'],axis=1,inplace=True) plt_mbd_c0 = plt_mbd_c1.copy() del plt_mbd_c1 print("iter = ",iterc) iterc = iterc + 1
问题根源分析
NEST_VAL_PER更新逻辑未触发有效变化- 当
OUT_FLAG == 0时,C_SALES_C直接取原始的SALESUNIT,导致NEST_VAL_PER = SALESUNIT * PROJR * NEWPRICE,与初始值完全一致,这部分行的数值没有任何更新。 - 当
BRD_OUT_FLAG == 0时,NEST_VAL_PER1取NEST_VAL,若NEST_VAL与初始NEST_VAL_PER值相同,这部分行的数值也不会变化。 - 只有同时满足
OUT_FLAG == 1且BRD_OUT_FLAG != 0的行,NEST_VAL_PER才会基于分位数更新。如果这类行占比极低,或计算后的值与初始值差异极小,会导致下一次迭代的分位数几乎不变,最终所有迭代结果趋同。
- 当
append方法的隐性问题
循环中反复使用append拼接DataFrame,每次都会创建新对象,不仅效率低下,还可能因内存引用问题导致中间数据未被正确传递。冗余变量操作干扰逻辑
代码中多次使用del删除DataFrame变量,这种操作没必要,反而可能干扰Python的变量引用机制,增加调试复杂度。
解决方法与修正代码
关键修改点
- 调整更新逻辑,确保符合条件的行能有效更新
NEST_VAL_PER,让分位数在迭代中逐步降低。 - 用
pd.concat替代append,先通过列表收集迭代结果,最后统一拼接,提升效率并避免数据传递问题。 - 删除冗余的
del语句,简化代码逻辑。
修正后的代码
import pandas as pd import numpy as np # 假设plt_mbd_c为初始数据集 plt_mbd_c0 = plt_mbd_c[plt_mbd_c['UB_OUT'] == 1].copy() plt_mbd_c1_all_list = [] brd2c2_all_list = [] iterc = 1 while iterc < 10: plt_mbd_c1 = plt_mbd_c0.copy() # 按KEY1计算NEST_VAL_PER的75分位数 brd2c2 = plt_mbd_c1.groupby('KEY1')['NEST_VAL_PER'].agg( KEY1_CNT='count', PER1c=lambda x: x.quantile(0.75) ).reset_index() brd2c2_all_list.append(brd2c2) # 合并分位数到主数据集 plt_mbd_c1 = plt_mbd_c1.merge(brd2c2[['KEY1', 'PER1c']], on='KEY1', how='left') # 计算中间列:仅BRD_OUT_FLAG非0时使用分位数计算 plt_mbd_c1['NEST_VAL_PER1'] = np.where( plt_mbd_c1['BRD_OUT_FLAG'] != 0, plt_mbd_c1['PER1c'] * (plt_mbd_c1['EVAL_LP_%'] / 100), plt_mbd_c1['NEST_VAL'] ) # 计算销售相关列:仅OUT_FLAG为1时使用计算值 plt_mbd_c1['SALESC'] = plt_mbd_c1['NEST_VAL_PER1'] / plt_mbd_c1['PROJR'] / plt_mbd_c1['NEWPRICE'] plt_mbd_c1['C_SALES_C'] = np.where( plt_mbd_c1['OUT_FLAG'] == 1, plt_mbd_c1['SALESC'], plt_mbd_c1['SALESUNIT'] ) # 更新NEST_VAL_PER,确保变化传递到下一次迭代 plt_mbd_c1['NEST_VAL_PER'] = plt_mbd_c1['C_SALES_C'] * plt_mbd_c1['PROJR'] * plt_mbd_c1['NEWPRICE'] # 标记迭代次数并收集结果 plt_mbd_c1['ITER'] = iterc plt_mbd_c1_all_list.append(plt_mbd_c1.drop('PER1c', axis=1)) # 将更新后的数据集传递到下一次迭代 plt_mbd_c0 = plt_mbd_c1.drop('PER1c', axis=1).copy() print(f"iter = {iterc}") iterc += 1 # 拼接所有迭代结果 plt_mbd_c1_all = pd.concat(plt_mbd_c1_all_list, ignore_index=True) brd2c2_all = pd.concat(brd2c2_all_list, ignore_index=True)
额外验证步骤
- 检查
OUT_FLAG和BRD_OUT_FLAG的分布,确保有足够多的行满足OUT_FLAG == 1且BRD_OUT_FLAG != 0,这是NEST_VAL_PER能有效更新的前提。 - 在每次迭代后打印分组分位数
brd2c2['PER1c'],确认分位数是否逐步降低,验证迭代逻辑的有效性。
内容的提问来源于stack exchange,提问作者user1412
相关产品推荐
相关产品推荐

