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

如何用向量化方式为Pandas DataFrame标记空闲时段状态列

Pandas向量化识别空闲时段并标记状态

问题描述

需要分析Pandas DataFrame数据,识别空闲时段(表现为disp值从>520降至~225的平稳区间),并创建state列标记三种状态:

  • idl_strt:空闲时段起始行
  • idle:空闲时段中间行
  • idl_ends:空闲时段结束行

数据片段(含噪声,完整数据27万行)

disp      temp  
148  528.253551  0.908375
149  537.832220  0.944138
150  225.073475  0.890493
151  225.247861  0.892878
152  225.487333  0.895262
153  225.515279  0.926256
154  225.515003  0.909567
155  225.518680  0.901222
156  225.537893  0.927448
157  225.068878  0.865460
158  540.460613  0.873804
159  531.048884  0.865460

期望输出

disp      temp      state
148  528.253551  0.908375       None
149  537.832220  0.944138       None
150  225.073475  0.890493   idl_strt
151  225.247861  0.892878       idle
152  225.487333  0.895262       idle
153  225.515279  0.926256       idle
154  225.515003  0.909567       idle
155  225.518680  0.901222       idle
156  225.537893  0.927448       idle
157  225.068878  0.865460   idl_ends
158  540.460613  0.873804       None
159  531.048884  0.865460       None

现有问题

已实现idl_strt和idl_ends的标记,但依赖移位列+pd.apply,方案不够优雅,且无法填充中间行;希望避免逐行遍历,提升效率和可维护性。

现有代码:

def _idlefinder(self, row):
    """Logic for finding idles start and end"""
    if (row["disp"] + 250.0) < row["nextdisp"]:  # find large drops in disp value
        return "idle_strt"
    elif row["disp"] + 250 < row["lastdisp"]:    # find large increases in disp value
        return "idle_end"

df["lastdisp"] = df["disp"].shift(1)
df["nextdisp"] = df["disp"].shift(-1)
df["status"] = df.apply(_idlefinder, axis=1)  # label idle_start and idle_end
df.drop(columns=["nextdisp", "lastdisp"], inplace=True)

向量化解决方案

完全基于Pandas/Numpy的向量化操作,避免低效的逐行apply和遍历,适合大数据集(27万行)。

步骤1:定义阈值并标记空闲行

先根据disp值标记哪些行属于空闲区间(考虑噪声,设阈值为250,可根据实际数据调整):

import numpy as np
import pandas as pd

# 定义空闲状态的disp阈值(适配噪声)
idle_threshold = 250

# 标记是否为空闲行
df['is_idle'] = df['disp'] < idle_threshold

步骤2:向量化识别idl_strt和idl_ends

利用shift函数生成前后行的状态,结合np.where直接标记起始和结束:

# 标记idl_strt:当前行是空闲,前一行不是
df['state'] = np.where(
    df['is_idle'] & ~df['is_idle'].shift(1, fill_value=False),
    'idl_strt',
    None
)

# 标记idl_ends:当前行是空闲,后一行不是
df['state'] = np.where(
    df['is_idle'] & ~df['is_idle'].shift(-1, fill_value=False),
    'idl_ends',
    df['state']
)

步骤3:填充中间行的idle状态

通过分组标记连续的空闲区间,一次性填充中间行:

# 生成连续空闲区间的分组标签:同一连续空闲区间共享同一标签
df['idle_group'] = df['is_idle'].cumsum() - df['is_idle'].cumsum().where(~df['is_idle']).ffill().fillna(0)

# 对所有空闲且未标记状态的行,设置为idle
df['state'] = np.where(
    df['is_idle'] & df['state'].isna(),
    'idle',
    df['state']
)

步骤4:清理辅助列

df.drop(columns=['is_idle', 'idle_group'], inplace=True)

关键问题解答

  1. 是否存在向量化实现方式?
    是的,上述方案全程采用向量化操作,仅用到shift、cumsum、np.where等高效方法,避免了逐行遍历,处理27万行数据的速度远快于apply或循环。

  2. pd.apply调用的函数能否不依赖移位列访问前一行?
    不能。apply是逐行执行,函数本身无法直接访问前一行数据,必须通过提前生成移位列(如lastdisp)来实现。因此更推荐用向量化方法替代apply,既提升效率,又简化代码结构。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 16:40:57