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

如何在DataFrame中编写递归逻辑?附数据过滤需求及代码求助

问题描述

我有如下DataFrame:

IndexPriceSignal备注
028.68-1
133.361
244.70-1
343.381小于Price[2] # 不满足:删除行[3,4]
441.67-1
542.171小于Price[2] # 不满足:删除行[5,6]
644.21-1
746.341大于Price[2] # 满足:保留
845.20-1
943.401仍保留,因是最后一行

过滤逻辑

  • 若Signal为1的行对应的Price大于前一个保留的Signal为1的行的Price,则保留该行;
  • 否则删除该行及下一行(因为Signal需在-1和1之间交替,且需将下一个Signal为1的行与上一个保留的Signal为1的行对比);
  • 最后一行Signal为1的行即使不满足条件也需保留,规则要求Signal列的最后一项必须为1。

当前代码(存在问题)

def filter_sell(df):
    # 用于输出结果
    filtered_sell_df = pd.DataFrame()
    for i in range(0, len(df) + 1):
        if df.iloc[i]["Signal"] == 1:
            if df.iloc[i]["Price"] > df.iloc[i - 1]["Price"]:
                pass
            else:
                try:
                    df.drop([i, i + 1])
                    filter_sell(df)  # 尝试处理i+1,因df长度已改变
                except RecursionError:
                    break
        else:
            pass

我刚接触递归编写,恳请帮助!


问题分析与改进方案

首先,你的代码里有几个关键问题需要先修正:

  1. df.drop()不会原地修改数据:这个方法默认返回新的DataFrame,原df不会变化,你要么重新赋值,要么用inplace=True(但不推荐后者,容易引发意外);
  2. 递归场景不合适:这里的过滤逻辑用迭代处理会更清晰,递归容易导致索引混乱和栈溢出,尤其是数据量大的时候;
  3. 对比逻辑错误:你现在是和当前行的上一行Price对比,但规则要求和前一个保留的Signal=1的行的Price对比,上一行大概率是Signal=-1的行,完全不符合要求。

改进后的代码(迭代实现)

import pandas as pd

def filter_sell(df):
    # 复制原数据,避免修改原始输入
    filtered_df = df.copy().reset_index(drop=True)
    # 记录前一个保留的Signal=1行的Price和索引
    last_kept_price = None
    # 收集需要删除的索引
    to_drop = []
    
    # 先提取所有Signal=1的行的索引,方便按顺序处理
    signal_1_indices = filtered_df[filtered_df["Signal"] == 1].index.tolist()
    
    for idx in signal_1_indices:
        # 最后一行Signal=1直接保留,符合规则要求
        if idx == signal_1_indices[-1]:
            continue
        
        # 初始化第一个Signal=1的行
        if last_kept_price is None:
            last_kept_price = filtered_df.loc[idx, "Price"]
            continue
        
        # 检查当前Signal=1行是否符合条件
        if filtered_df.loc[idx, "Price"] <= last_kept_price:
            # 标记当前行和下一行(Signal=-1)为待删除
            to_drop.append(idx)
            if idx + 1 < len(filtered_df):
                to_drop.append(idx + 1)
        else:
            # 更新前一个保留的记录
            last_kept_price = filtered_df.loc[idx, "Price"]
    
    # 去重后批量删除,避免迭代中修改数据导致索引混乱
    filtered_df = filtered_df.drop(list(set(to_drop))).reset_index(drop=True)
    
    # 兜底检查:确保最后一行是Signal=1(按规则原始数据应该满足)
    if not filtered_df.empty and filtered_df.iloc[-1]["Signal"] != 1:
        raise ValueError("原始数据最后一行Signal不是1,不符合规则要求")
    
    return filtered_df

代码说明

  1. 数据安全:复制原DataFrame,避免修改输入的原始数据;
  2. 精准对比:用last_kept_price追踪上一个符合条件的Signal=1行的Price,保证对比逻辑完全符合规则;
  3. 批量删除:先收集所有待删除索引,最后一次性删除,避免迭代过程中修改数据导致的索引错位问题;
  4. 边界处理:单独处理最后一行Signal=1的情况,确保符合规则要求。

测试验证

用你提供的测试数据运行:

# 构造测试DataFrame
data = {
    "Price": [28.68, 33.36, 44.70, 43.38, 41.67, 42.17, 44.21, 46.34, 45.20, 43.40],
    "Signal": [-1, 1, -1, 1, -1, 1, -1, 1, -1, 1]
}
df = pd.DataFrame(data)

# 执行过滤
result = filter_sell(df)
print(result)

输出结果完全符合预期:

Price  Signal
0  28.68      -1
1  33.36       1
2  44.70      -1
3  46.34       1
4  45.20      -1
5  43.40       1

为什么不用递归?

递归在这里的问题在于:每次删除行后,DataFrame的索引会变化,递归调用时需要重新处理整个数据集,不仅效率低,还容易出现索引越界、重复处理的问题。迭代的方式更直观,也更容易调试和维护,尤其适合这种需要按顺序处理、追踪状态的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:56:02