You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题根源分析

  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才会基于分位数更新。如果这类行占比极低,或计算后的值与初始值差异极小,会导致下一次迭代的分位数几乎不变,最终所有迭代结果趋同。
  2. append方法的隐性问题
    循环中反复使用append拼接DataFrame,每次都会创建新对象,不仅效率低下,还可能因内存引用问题导致中间数据未被正确传递。

  3. 冗余变量操作干扰逻辑
    代码中多次使用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)

额外验证步骤

  1. 检查OUT_FLAG和BRD_OUT_FLAG的分布,确保有足够多的行满足OUT_FLAG == 1且BRD_OUT_FLAG != 0,这是NEST_VAL_PER能有效更新的前提。
  2. 在每次迭代后打印分组分位数brd2c2['PER1c'],确认分位数是否逐步降低,验证迭代逻辑的有效性。

内容的提问来源于stack exchange,提问作者user1412

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:31:08