You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Pandas实现:在3行前序窗口中获取最后一个非零值

Pandas实现:在3行前序窗口中获取最后一个非零值

我来帮你搞定这个需求!先把信息理清楚:

原始数据

我们的初始DataFrame是:

import pandas as pd
df = pd.DataFrame({
    'a': [0, 0, 1, -1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]
})

预期效果

我们需要生成一列b,最终输出如下:

a  b
0   0  0
1   0  0
2   1  0
3  -1  1
4  -1 -1
5   0 -1
6   0 -1
7   0 -1
8   0  0
9   0  0
10 -1  0
11  0 -1
12  0 -1
13  1 -1
14  0  1

核心逻辑

说直白点就是:对每一行,往前看最多3行的范围(窗口),找到这个窗口里最后一个非0的数,如果窗口里全是0,那b就填0。举两个例子:

  • 索引3的行:往前3行是索引0、1、2,最后一个非0值是1,所以b=1
  • 索引4的行:往前3行是索引1、2、3,最后一个非0值是-1,所以b=-1

你当前的尝试

你已经写出了可行的代码,思路是完全正确的:

def last_nonzero(x):
    # x是代表窗口的Series
    nonzero = x[x != 0]
    if not nonzero.empty:
        # 返回窗口里最后一个非0值(最靠近当前行的那个)
        return nonzero.iloc[-1]
    return 0

# 先shift(1)让窗口只看当前行之前的行,窗口大小3,min_periods=1兼容前面的短窗口
df['b'] = df['a'].shift(1).rolling(window=3, min_periods=1).apply(last_nonzero, raw=False).astype(int)

更高效的优化方案

你觉得有更好的方法是对的!上面的rolling.apply在处理大数据集时速度会比较慢,我们可以用Pandas的向量化填充操作来实现,效率提升明显:

import pandas as pd
df = pd.DataFrame({
    'a': [0, 0, 1, -1, -1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]
})

# 1. 把原列的0替换为NaN,方便后续填充
temp = df['a'].replace(0, pd.NA)
# 2. 向前填充非0值,限制最多填充3步(对应窗口大小3)
temp_filled = temp.ffill(limit=3)
# 3. 把填充后仍为NaN的位置(即窗口全0的情况)替换为0,再shift(1)让值对应到当前行的前序窗口
df['b'] = temp_filled.shift(1).fillna(0).astype(int)

运行这段代码后,生成的b列和预期输出完全一致,而且处理大数量级数据时,速度会比rolling.apply快很多哦~

备注:内容来源于stack exchange,提问作者AmirX

火山引擎 最新活动