如何向量化Pandas中连续正值配对时间差计算代码?
向量化实现Pandas连续正值配对时间差计算
需求规则
- 仅计算连续正值组成的配对的时间差,未配对的后续正值不计算
- 连续正值之间允许存在任意数量的0
- 遇到负值时立即重置配对追踪状态
原遍历实现代码
import pandas as pd from datetime import datetime # Sample data data = { 'datetime': [ datetime(2023, 11, 11, 8, 0, 0), datetime(2023, 11, 11, 8, 5, 0), datetime(2023, 11, 11, 8, 10, 0), datetime(2023, 11, 11, 8, 15, 0), datetime(2023, 11, 11, 8, 20, 0), datetime(2023, 11, 11, 8, 25, 0), datetime(2023, 11, 11, 8, 30, 0), datetime(2023, 11, 11, 8, 35, 0), datetime(2023, 11, 11, 8, 40, 0), datetime(2023, 11, 11, 8, 45, 0), datetime(2023, 11, 11, 8, 50, 0), ], 'value': [1, 3, 4, 2, -1, 1, 0, 2, -3, 0, -3], } # Create the DataFrame df = pd.DataFrame(data) df['value_timespan'] = -1 # Initialize variables to keep track of the last positive value and its timestamp last_positive_value = None last_positive_timestamp = None # Iterate through the DataFrame for index, row in df.iterrows(): if row['value'] > 0: if last_positive_value is not None: # Calculate the time span between the current positive value and the last positive value time_difference = (row['datetime'] - last_positive_timestamp).total_seconds() df.at[index, 'value_timespan'] = time_difference last_positive_value = None last_positive_timestamp = None else: last_positive_value = row['value'] last_positive_timestamp = row['datetime'] if row['value'] < 0: last_positive_value = None last_positive_timestamp = None print(df)
示例输出(原逻辑)
当value为[1, 3, 4, 2, -1, 1, 0, 2, -3, 0, -3]时,输出:
datetime value value_timespan 0 2023-11-11 08:00:00 1 -1 1 2023-11-11 08:05:00 3 300 2 2023-11-11 08:10:00 4 -1 3 2023-11-11 08:15:00 2 300 4 2023-11-11 08:20:00 -1 -1 5 2023-11-11 08:25:00 1 -1 6 2023-11-11 08:30:00 0 -1 7 2023-11-11 08:35:00 2 600 8 2023-11-11 08:40:00 -3 -1 9 2023-11-11 08:45:00 0 -1 10 2023-11-11 08:50:00 -3 -1
更新示例:当value为[1, 3, 1, -2, -1, 1, 0, 0, 3, 0, -3]时,输出:
datetime value timespan 0 2023-11-11 08:00:00 1 -1.0 1 2023-11-11 08:05:00 3 300.0 2 2023-11-11 08:10:00 1 -1.0 3 2023-11-11 08:15:00 -2 -1.0 4 2023-11-11 08:20:00 -1 -1.0 5 2023-11-11 08:25:00 1 -1.0 6 2023-11-11 08:30:00 0 -1.0 7 2023-11-11 08:35:00 0 -1.0 8 2023-11-11 08:40:00 3 900.0 9 2023-11-11 08:45:00 0 -1.0 10 2023-11-11 08:50:00 -3 -1.0
向量化实现方案
向量化实现的核心是通过标记分组、筛选有效配对,避免逐行遍历:
import pandas as pd from datetime import datetime # 示例数据(可替换为目标数据) data = { 'datetime': [ datetime(2023, 11, 11, 8, 0, 0), datetime(2023, 11, 11, 8, 5, 0), datetime(2023, 11, 11, 8, 10, 0), datetime(2023, 11, 11, 8, 15, 0), datetime(2023, 11, 11, 8, 20, 0), datetime(2023, 11, 11, 8, 25, 0), datetime(2023, 11, 11, 8, 30, 0), datetime(2023, 11, 11, 8, 35, 0), datetime(2023, 11, 11, 8, 40, 0), datetime(2023, 11, 11, 8, 45, 0), datetime(2023, 11, 11, 8, 50, 0), ], 'value': [1, 3, 4, 2, -1, 1, 0, 2, -3, 0, -3], } df = pd.DataFrame(data) df['value_timespan'] = -1 # 1. 标记关键状态:正值、负值 df['is_positive'] = df['value'] > 0 df['is_negative'] = df['value'] < 0 # 2. 创建分组:遇到负值则重置分组(用于隔离不同的配对区间) df['group'] = df['is_negative'].cumsum() # 3. 在每个分组内,标记有效正值的配对序号:每两个正值为一组(忽略0) pos_mask = df['is_positive'] # 对每个分组内的正值进行计数,然后取模2得到配对序号 df['pair_id'] = df.groupby('group')['is_positive'].cumsum().where(pos_mask, -1) % 2 # 4. 提取配对的起始时间:每个分组内,pair_id=0的时间作为配对开始时间 df['start_time'] = df.groupby(['group', 'pair_id'])['datetime'].transform('first').where(df['pair_id'] == 1, pd.NaT) # 5. 计算时间差,仅保留有效配对的结果 df['value_timespan'] = df.apply( lambda row: (row['datetime'] - row['start_time']).total_seconds() if pd.notna(row['start_time']) else -1, axis=1 ) # 清理临时列(可选) df.drop(['is_positive', 'is_negative', 'group', 'pair_id', 'start_time'], axis=1, inplace=True) print(df)
代码解释
- 状态标记:用
is_positive和is_negative标记每行的数值状态,方便后续筛选和分组。 - 分组隔离:通过
is_negative的累计求和创建分组,确保负值出现后,后续的配对不会和之前的正值关联。 - 配对序号分配:在每个分组内,对正值行进行连续计数,再取模2得到0/1的配对序号,0代表配对起始,1代表配对结束。
- 起始时间广播:将每个配对起始行(pair_id=0)的时间,传递到对应的结束行(pair_id=1)。
- 时间差计算:仅对配对结束行计算时间差,其他行保持-1。
该方案完全通过Pandas的向量化API实现,避免了iterrows()的低效遍历,性能更优,尤其适合大数据量场景。
内容的提问来源于stack exchange,提问作者jad
相关产品推荐
相关产品推荐

