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

Pandas优化:百万级DataFrame搜索函数性能提升方案

高效优化Pandas滑动窗口统计逻辑(针对480w+行数据集)

问题场景

现有一个包含4,827,996行、55列的Pandas DataFrame,需要为每行新增rbt列,规则如下:

  • 统计当前行IPorig对应的、State为RST、且datetime在当前行时间前300秒内的记录数
  • 若统计数量>10,rbt值为1;否则为0

原实现使用df.apply结合自定义函数,因逐行全表扫描导致运行速度极慢,需优化。

原实现代码

def rbt(saddr, state, stime):
    sub_df = df.loc[(df['IPorig'] == saddr) & (df['State'] == 'RST') & (df['datetime'] < stime) & (df['datetime'] > (stime - 300))]
    count = len(sub_df)
    return 1 if count > 10 else 0

df['rbt'] = df.apply(lambda x: rbt(x['IPorig'], x['State'], x['datetime']), axis=1)

原方案低效原因

df.apply逐行处理,相当于对480w行数据做480w次全表过滤,时间复杂度为O(n²),完全不适合百万级数据集。

优化方案

思路核心

先预处理所有RST记录,通过分组+滑动窗口计数替代逐行扫描,将时间复杂度降至O(n log n)(主要来自排序操作)。

步骤1:提取并预处理RST记录

先筛选出所有State为RST的记录,按IPorig分组后按datetime排序(确保时间有序,为滑动窗口做准备):

import pandas as pd

# 提取RST记录并按IP+时间排序
rst_df = df[df['State'] == 'RST'].sort_values(['IPorig', 'datetime'])

步骤2:分组计算滑动窗口内的RST数量

利用Pandas的滚动窗口功能,对每个IPorig分组统计300秒内的RST记录数(需排除当前行本身):

# 转换datetime为时间类型(若原始数据是时间戳,需指定unit='s')
rst_df['datetime'] = pd.to_datetime(rst_df['datetime'], unit='s')
# 设置datetime为索引,方便使用时间滚动窗口
rst_df = rst_df.set_index('datetime')

# 按IP分组,计算300秒窗口内的记录数
rst_df['rst_count'] = rst_df.groupby('IPorig')['IPorig'].rolling('300s').count().reset_index(level=0, drop=True)
# 减去当前行,得到当前时间之前300秒内的RST数量
rst_df['rst_count'] = rst_df['rst_count'] - 1

# 恢复原索引,方便后续合并
rst_df = rst_df.reset_index()

步骤3:合并结果到原DataFrame

将统计结果合并回原表,填充空值后生成rbt列:

# 合并统计结果到原DataFrame(按id匹配)
df = df.merge(rst_df[['id', 'rst_count']], on='id', how='left')
# 无匹配记录的行填充为0
df['rst_count'] = df['rst_count'].fillna(0)
# 生成rbt列:计数>10则为1,否则为0
df['rbt'] = (df['rst_count'] > 10).astype(int)

进阶加速:Numba优化

如果上述方案仍有性能瓶颈,可使用Numba对分组后的滑动计数进行JIT编译,进一步提升速度:

import numba
import numpy as np

@numba.jit(nopython=True)
def count_rst_in_window(times):
    n = len(times)
    counts = np.zeros(n, dtype=np.int64)
    for i in range(n):
        # 用二分查找快速定位窗口左边界
        left = np.searchsorted(times, times[i] - 300, side='left')
        counts[i] = i - left
    return counts

# 分组处理每个IP的时间序列
def process_group(group):
    times = group['datetime'].values
    group['rst_count'] = count_rst_in_window(times)
    return group

# 重新处理RST记录
rst_df = df[df['State'] == 'RST'].sort_values(['IPorig', 'datetime'])
rst_df = rst_df.groupby('IPorig', group_keys=False).apply(process_group)

# 后续合并步骤同前
df = df.merge(rst_df[['id', 'rst_count']], on='id', how='left')
df['rst_count'] = df['rst_count'].fillna(0)
df['rbt'] = (df['rst_count'] > 10).astype(int)

优化效果

原方案的O(n²)复杂度被降至O(n log n),对于480w行数据,运行速度可提升数十至数百倍,完全满足大数据量处理需求。

内容的提问来源于stack exchange,提问作者Willen Borges Coelho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 20:05:11