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

如何避免按两列分组的三层嵌套FOR循环及SettingWithCopyWarning?

解决方案:Pandas向量化替代嵌套循环+消除SettingWithCopyWarning

问题分析

原代码通过三层嵌套循环按Id1+Id2分组处理数据,核心逻辑是每组按时间顺序遍历,根据Col1和Col3的值设置Col4。但嵌套循环效率低下,且直接修改切片触发SettingWithCopyWarning,适合用Pandas分组向量化操作优化。

核心业务规则(从原循环提炼)

对每个Id1+Id2的分组(按TimeStamp升序):

  • 遍历过程中一旦触发终止条件则停止处理后续行:
    1. 当前行Col1=1且Col3>125 → 终止,后续行Col4保持0
    2. 当前行Col1=3且Col3>=125 → 终止,后续行Col4保持0
  • 非终止场景的处理:
    1. Col1=1且Col3<125 → 跳过,Col4保持0,继续下一行
    2. Col1=3且Col3<125 → 设置Col4=1

优化方案

1. 向量化替代嵌套循环

利用groupby分组后结合自定义逻辑处理,避免显式循环,同时保证逻辑与原代码一致。对于大数据量,还可以通过累积布尔标记实现完全向量化,进一步提升效率。

2. 消除SettingWithCopyWarning

禁止直接修改切片副本,改为直接在原DataFrame上修改,或通过groupby处理后将结果合并回原DataFrame,彻底避免操作副本引发的警告。


完整实现代码

方案一:分组自定义函数(逻辑清晰,易维护)

import pandas as pd

# 原始数据
d = {
    'TimeStamp' : [1, 2, 3, 4, 5, 6, 7, 8, 9], 
    'Id1': [80, 80, 80, 90, 90, 90, 100, 100, 100],
    'Id2': [10, 10, 10, 10, 10, 12, 14, 12, 12], 
    'Col1': [3, 3, 3, 3, 1, 3, 1, 3, 1],
    'Col2': [1, 2, 3, 1, 2, 1, 1, 1, 1],
    'Col3': [110, 120, 130, 110, 120, 130, 110, 120, 130], 
    'Col4': [0, 0, 0, 0, 0, 0, 0, 0, 0]}

df = pd.DataFrame(data=d)
df.sort_values(by='TimeStamp', ascending=True, inplace=True)
time_diff = 125

# 定义分组处理函数
def process_group(group):
    active = True
    for idx in group.index:
        if not active:
            break
        col1 = group.loc[idx, 'Col1']
        col3 = group.loc[idx, 'Col3']
        
        if col1 == 1:
            if col3 > time_diff:
                active = False
            continue
        elif col1 ==3:
            if col3 < time_diff:
                group.loc[idx, 'Col4'] = 1
            else:
                active = False
        else:
            active = False
    return group

# 分组处理并合并结果
df = df.groupby(['Id1', 'Id2'], group_keys=False).apply(process_group)

# 输出结果
print(df.to_markdown(index=False))

方案二:完全向量化实现(大数据量下效率更高)

import pandas as pd

d = {
    'TimeStamp' : [1, 2, 3, 4, 5, 6, 7, 8, 9], 
    'Id1': [80, 80, 80, 90, 90, 90, 100, 100, 100],
    'Id2': [10, 10, 10, 10, 10, 12, 14, 12, 12], 
    'Col1': [3, 3, 3, 3, 1, 3, 1, 3, 1],
    'Col2': [1, 2, 3, 1, 2, 1, 1, 1, 1],
    'Col3': [110, 120, 130, 110, 120, 130, 110, 120, 130], 
    'Col4': [0, 0, 0, 0, 0, 0, 0, 0, 0]}

df = pd.DataFrame(data=d)
df.sort_values(by='TimeStamp', ascending=True, inplace=True)
time_diff = 125

# 1. 标记每组内的终止触发行
df['terminate'] = ((df['Col1'] ==1) & (df['Col3'] > time_diff)) | ((df['Col1'] ==3) & (df['Col3'] >= time_diff))
# 2. 计算每组内从开始到第一个终止位置的累积有效标记(True表示可处理)
df['active'] = ~df.groupby(['Id1', 'Id2'])['terminate'].cummax()
# 3. 设置Col4:满足条件则设为1,否则为0
df['Col4'] = df.apply(lambda x: 1 if x['active'] and x['Col1']==3 and x['Col3']<time_diff else 0, axis=1)
# 4. 清理临时列
df.drop(['terminate', 'active'], axis=1, inplace=True)

print(df.to_markdown(index=False))

输出结果(与原循环完全一致)

|   TimeStamp |   Id1 |   Id2 |   Col1 |   Col2 |   Col3 |   Col4 |
|------------:|------:|------:|-------:|-------:|-------:|-------:|
|           1 |    80 |    10 |      3 |      1 |    110 |      1 |
|           2 |    80 |    10 |      3 |      2 |    120 |      1 |
|           3 |    80 |    10 |      3 |      3 |    130 |      0 |
|           4 |    90 |    10 |      3 |      1 |    110 |      1 |
|           5 |    90 |    10 |      1 |      2 |    120 |      0 |
|           6 |    90 |    12 |      3 |      1 |    130 |      0 |
|           7 |   100 |    14 |      1 |      1 |    110 |      0 |
|           8 |   100 |    12 |      3 |      1 |    120 |      1 |
|           9 |   100 |    12 |      1 |      1 |    130 |      0 |

内容的提问来源于stack exchange,提问作者Automacao Na Pratica

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:42:02