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

如何用Pandas按时间距离实现正负数据的一对一匹配

问题描述

需要将数据中正stem(数值为正的条目)匹配到时间上最近的负stem(数值为负的条目),匹配规则如下:

  • 匹配核心依据为两者的时间距离,取正stem时间点之前最近的负stem
  • 每个负stem仅能被匹配一次,匹配完成后不可再用于其他正stem
  • 若正stem与目标负stem的时间差≥自定义阈值X,则不参与匹配

示例:正stem A匹配负stem B;负stem B已被占用,无法再匹配正stem C;负stem D与A、C的时间差过大,不参与匹配。

对应的DataFrame如下:

import pandas as pd

df = pd.DataFrame({
    'BOX_187_084_11': [-190.379230, -261.853410, 268.353538, 258.084186],
    'label': ['D', 'B', 'A', 'C']
}, index=pd.to_datetime(['2005-12-01', '2008-03-01', '2008-09-01', '2011-09-01']))
Pandas风格解决方案

利用Pandas的merge_asof工具结合去重逻辑,可简洁实现需求,步骤如下:

  1. 分离正负stem数据集
    将原数据拆分为正、负stem两个子集,并重置索引保留时间列:

    pos_df = df[df['BOX_187_084_11'] > 0].reset_index().rename(columns={'index': 'pos_time'})
    neg_df = df[df['BOX_187_084_11'] < 0].reset_index().rename(columns={'index': 'neg_time'})
    
  2. 按时间匹配最近负stem
    使用merge_asof按时间正序匹配,设置时间差阈值X(示例设为3年),仅匹配正stem之前最近的负stem:

    X = pd.Timedelta(days=3*365)  # 自定义时间差阈值
    matched = pd.merge_asof(
        pos_df.sort_values('pos_time'),
        neg_df.sort_values('neg_time'),
        left_on='pos_time',
        right_on='neg_time',
        direction='backward',  # 匹配时间更早的最近负stem
        tolerance=X
    )
    
  3. 确保负stem唯一匹配
    对匹配结果按负stem去重,保留每个负stem对应的最早正stem匹配:

    final_matched = matched.drop_duplicates(subset='neg_time', keep='first')
    
  4. 整理可视化结果
    重命名列名,输出直观的匹配结果:

    result = final_matched[['pos_time', 'label_x', 'neg_time', 'label_y', 'BOX_187_084_11_x', 'BOX_187_084_11_y']]
    result.columns = ['正stem时间', '正stem标签', '负stem时间', '负stem标签', '正stem数值', '负stem数值']
    print(result)
    

输出结果

运行后会得到符合规则的匹配结果:

正stem时间正stem标签负stem时间负stem标签正stem数值负stem数值
2008-09-01A2008-03-01B268.353538-261.853410

正stem C与最近的负stem B时间差超过3年,且无其他符合条件的负stem,因此不被匹配;负stem D因时间差过大也未参与匹配。

内容的提问来源于stack exchange,提问作者Dante van der Heijden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:50:25