如何用向量化方式为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)
关键问题解答
是否存在向量化实现方式?
是的,上述方案全程采用向量化操作,仅用到shift、cumsum、np.where等高效方法,避免了逐行遍历,处理27万行数据的速度远快于apply或循环。pd.apply调用的函数能否不依赖移位列访问前一行?
不能。apply是逐行执行,函数本身无法直接访问前一行数据,必须通过提前生成移位列(如lastdisp)来实现。因此更推荐用向量化方法替代apply,既提升效率,又简化代码结构。
内容的提问来源于stack exchange,提问作者Riftwave
相关产品推荐
相关产品推荐

