Pandas如何按相邻行类型对应不同时间间隔规则分组生成新列
按规则生成new_group分组列实现方案
分组规则
目标DataFrame已按人员、购买时间完成排序,分组逻辑仅针对product=fruit的记录,other类记录不分配组号,同人员下组号独立从1开始计数:
- 若当前fruit行的上一行记录product为
fruit:两条记录时间间隔≤10分钟则归为同一组,间隔超过10分钟则新建分组 - 若当前fruit行的上一行记录product为
other:当前fruit行与上一个fruit记录时间间隔≤2分钟则归为同一组,间隔超过2分钟则新建分组
示例参考数据
person time_bought product new_group abby 2:21 fruit 1 abby 2:25 fruit 1 (2.25与2.21间隔在10分钟内,属于同一组) abby 10:35 fruit 2 abby 10:40 other abby 10:42 fruit 2 (10.42与10.35间隔在2分钟内,属于同一组) abby 10:53 fruit 3 (10.53与10.42间隔超过10分钟,属于新分组) barry 12:00 fruit 1 ...
原有代码问题
- 时间差计算未跳过
other类记录,插入other行后diff计算的是和other行的间隔,而非和上一个fruit行的间隔 - 未按
person维度重置组号计数,跨人员会累计组号导致编号错误 - 未对非fruit行做空值处理,mask逻辑无法覆盖所有边界场景
正确实现代码
首先确保时间字段为datetime格式,若已完成格式转换可跳过第一步:
import pandas as pd import numpy as np # 时间格式转换 df['time_bought'] = pd.to_datetime(df['time_bought'], format='%H:%M')
核心分组逻辑:
# 标记fruit类记录 fruit_mask = df['product'] == 'fruit' # 按人员分组,取当前行之前最近一个fruit记录的购买时间 df['last_fruit_time'] = df.groupby('person')['time_bought'].transform( lambda x: x.where(fruit_mask).ffill().shift(1) ) # 计算当前fruit行和上一个fruit行的时间差(单位:分钟) df['time_diff'] = (df['time_bought'] - df['last_fruit_time']).dt.total_seconds() / 60 # 取当前行上一条记录的product类型 df['prev_product'] = df.groupby('person')['product'].shift(1) # 标记新分组边界 df['new_group_flag'] = np.where( ~fruit_mask, False, # 非fruit行不参与分组 np.where( df['last_fruit_time'].isna(), True, # 每个人员的第一条fruit记录为新组起点 np.where( df['prev_product'] == 'other', df['time_diff'] > 2, # 上一条是other,间隔超2分钟则新建组 df['time_diff'] > 10 # 上一条是fruit,间隔超10分钟则新建组 ) ) ) # 按人员累计分组标记生成组号,非fruit行置为空值 df['new_group'] = np.where( fruit_mask, df.groupby('person')['new_group_flag'].cumsum(), np.nan ) # 可选:删除中间辅助列 df = df.drop(columns=['last_fruit_time', 'time_diff', 'prev_product', 'new_group_flag'])
逻辑说明
- 先通过
ffill对齐每个fruit行对应的上一个fruit行时间,避免other行插入导致时间差计算错误 - 按人员维度独立计算分组标记,保证不同人员的组号从1开始独立计数
- 分层判断新分组条件,完全匹配给定的间隔规则
- 非fruit行不参与组号计数,组号仅在fruit行连续累加
内容的提问来源于stack exchange,提问作者asd
相关产品推荐
相关产品推荐

