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

Pandas高效统计当前行前后连续满足特定条件的行数的方案需求

优化Pandas中前序/后续连续满足条件行数的计算

嘿,针对你25k行DataFrame的这个效率问题,我来给你几个更高效的解决方案!原apply方法因为每次都要切片、反转DataFrame,产生大量临时对象,确实在数据量大的时候会很慢。我们可以通过预计算+高效循环,甚至用numba加速来大幅提升速度。

先明确问题核心

我们需要给每一行统计:

  • next count:从当前行的下一行开始,连续满足「该行A > 当前行A 且 该行A < 该行B」的行数,直到第一个不满足的行就停止计数
  • previous count:从当前行的上一行往前倒序,连续满足相同条件的行数,同样到第一个不满足的行停止

原方法的瓶颈在于每次行级apply都要做DataFrame切片和反转,这在25k行时会产生巨大的性能开销。

优化方案1:用Numpy数组替代DataFrame操作

先预计算好基础条件,再用纯数组循环,避免DataFrame的额外开销:

import pandas as pd
import numpy as np

# 你的示例数据
df = pd.DataFrame({'A' : [0,3,2,9,1,0,4,7,3,2], 'B': [9,8,3,5,5,5,5,8,0,4]})

# 预计算每个行是否满足 A < B(这是后续/前序行需要满足的基础条件之一)
mask = (df['A'] < df['B']).values
A_vals = df['A'].values
n = len(df)

# 计算next count
next_count = np.zeros(n, dtype=int)
for i in range(n):
    cnt = 0
    # 从下一行开始遍历,直到不满足条件
    for j in range(i+1, n):
        if mask[j] and A_vals[j] > A_vals[i]:
            cnt += 1
        else:
            break
    next_count[i] = cnt

# 计算previous count
prev_count = np.zeros(n, dtype=int)
for i in range(n):
    cnt = 0
    # 从上一行往前倒序遍历
    for j in range(i-1, -1, -1):
        if mask[j] and A_vals[j] > A_vals[i]:
            cnt += 1
        else:
            break
    prev_count[i] = cnt

# 合并结果到原DataFrame
df['next count'] = next_count
df['previous count'] = prev_count

这个方法比原apply快很多,因为用Numpy数组直接遍历,避免了DataFrame切片带来的内存和时间开销。

优化方案2:用Numba加速循环(性能提升几十倍)

如果25k行还是觉得不够快,用numba对循环进行JIT编译,能把纯Python循环的速度提升到接近C的水平:

from numba import jit

# 用numba编译next count的计算函数
@jit(nopython=True)
def compute_next_count(A_vals, mask):
    n = len(A_vals)
    next_count = np.zeros(n, dtype=np.int32)
    for i in range(n):
        cnt = 0
        for j in range(i+1, n):
            if mask[j] and A_vals[j] > A_vals[i]:
                cnt += 1
            else:
                break
        next_count[i] = cnt
    return next_count

# 用numba编译previous count的计算函数
@jit(nopython=True)
def compute_prev_count(A_vals, mask):
    n = len(A_vals)
    prev_count = np.zeros(n, dtype=np.int32)
    for i in range(n):
        cnt = 0
        for j in range(i-1, -1, -1):
            if mask[j] and A_vals[j] > A_vals[i]:
                cnt += 1
            else:
                break
        prev_count[i] = cnt
    return prev_count

# 转换为numpy数组传入编译后的函数
A_np = df['A'].values
mask_np = (df['A'] < df['B']).values

df['next count'] = compute_next_count(A_np, mask_np)
df['previous count'] = compute_prev_count(A_np, mask_np)

这个方案对于25k行的数据来说,几乎是瞬间就能完成计算,效率提升非常明显。

验证结果

运行上述代码后,得到的结果和你预期的完全一致:

A  B  next count  previous count
0  0  9           2                0
1  3  8           0                0
2  2  3           0                1
3  9  5           0                0
4  1  5           0                0
5  0  5           2                1
6  4  5           1                0
7  7  8           0                0
8  3  0           0                2
9  2  4           0                0

为什么没有完全向量化的方案?

这里需要说明一下:因为每一行的判断阈值是自身的A值,不同行的阈值不一样,没法用统一的向量化广播操作来处理所有行的连续计数。所以循环是最直接的方案,而通过Numpy和Numba可以把循环的效率提升到接近向量化的水平,完全能应对25k行的规模。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:07:49