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

如何在Pandas DataFrame中高效统计前N个单元格内符合范围的数量

高效统计Pandas中前N个单元格落在当前值范围内的数量

问题描述

给定如下结构的Pandas DataFrame:

col1
0     10
1      5
2      8
3     12
4     13
5      6
6      9
7     11
8     10
9      3
10    21
11    18
12    14
13    16
14    30
15    45
16    31
17    40
18    38

首先为col1列每个单元格计算数值范围:

df['df_min'] = df.col1 - df.col1 * 0.2  # 等价于 df.col1 * 0.8
df['df_max'] = df.col1 + df.col1 * 0.2  # 等价于 df.col1 * 1.2

需求是为每个单元格统计其之前3个单元格中,col1值落在该单元格对应[df_min, df_max]范围内的数量。当前使用循环实现的方案在大数据量下性能极差,需要无需循环的高效解决方案。目标结果如下:

col1  df_min  df_max  counter
0     10     8.0    12.0       -1
1      5     4.0     6.0       -1
2      8     6.4     9.6       -1
3     12     9.6    14.4        1
4     13    10.4    15.6        1
5      6     4.8     7.2        0
6      9     7.2    10.8        0
7     11     8.8    13.2        2
8     10     8.0    12.0        2
9      3     2.4     3.6        0
10    21    16.8    25.2        0
11    18    14.4    21.6        1
12    14    11.2    16.8        0
13    16    12.8    19.2        2
14    30    24.0    36.0        0
15    45    36.0    54.0        0
16    31    24.8    37.2        1
17    40    32.0    48.0        1
18    38    30.4    45.6        3

原循环实现代码

df = pd.DataFrame({"col1":[10, 5, 8, 12, 13, 6, 9, 11, 10, 3, 21, 18, 14, 16, 30, 45, 31, 40, 38]})

back = 3 # 需要回溯的单元格数量

df['df_min'] = df.col1 - df.col1 * 0.2
df['df_max'] = df.col1 + df.col1 * 0.2

l = []
for window in df.col1.rolling(window=back+1, center=False, closed='right'):
    if window.empty:
        pass
    else:
        a = window.iloc[-1]
        range_min = a - a * 0.2
        range_max = a + a * 0.2
        c = 0
        if len(window) == back+1:
            for b in window:
                if (b >= range_min and b <= range_max):
                    c += 1
        c = c-1 # 减去窗口中包含的当前值(必然符合条件)
        l.append(c)
df1 = pd.DataFrame(l, columns=['counter'])

df = df.join(df1)

print(df)

高效向量化解决方案

利用Numpy的滑动窗口和广播操作,完全避免Python层面的循环,大幅提升性能:

import pandas as pd
import numpy as np

# 初始化数据
df = pd.DataFrame({"col1":[10, 5, 8, 12, 13, 6, 9, 11, 10, 3, 21, 18, 14, 16, 30, 45, 31, 40, 38]})
back = 3

# 计算范围值
df['df_min'] = df['col1'] * 0.8
df['df_max'] = df['col1'] * 1.2

# 初始化counter列,前back行没有足够前置元素,设为-1
df['counter'] = -1

# 生成滑动窗口矩阵:每一行对应当前行之前的back个元素
# 矩阵形状为 (len(df)-back, back)
window_matrix = np.lib.stride_tricks.sliding_window_view(df['col1'].values, window_shape=back)

# 提取需要计算的行对应的min和max值
target_mins = df['df_min'].values[back:]
target_maxs = df['df_max'].values[back:]

# 广播比较:每个窗口元素与对应行的min、max比较,生成布尔矩阵
mask = (window_matrix >= target_mins[:, np.newaxis]) & (window_matrix <= target_maxs[:, np.newaxis])

# 统计每行符合条件的元素数量
counts = mask.sum(axis=1)

# 将结果赋值给counter列
df.loc[back:, 'counter'] = counts

print(df)

方案优势

  • 无Python循环:所有核心计算都在Numpy的C级运算中完成,避免了循环带来的性能损耗。
  • 时间复杂度O(n):滑动窗口生成和广播比较的时间复杂度均为线性,处理百万级甚至更大数据集时性能远超循环方案。
  • 代码简洁:逻辑清晰,易于维护和扩展(只需修改back参数即可调整回溯数量)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:40:38