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

带日期约束的Pandas DataFrame连接:基于两个数据集的关联需求

基于日期约束连接Pandas DataFrames的解决方案

看起来你需要根据Id匹配,同时找到df2中日期不晚于df1对应行日期的最新记录(毕竟你提到计数器随时间递增),下面是最简洁高效的实现方式:

步骤1:预处理数据(修正格式 + 转换日期)

首先得把原数据里的字符串日期转换成Pandas能识别的datetime类型,顺便补上原数据里缺失的字符串引号:

import pandas as pd

# 修正原数据的字符串格式
df1 = pd.DataFrame(
    {'Id': ['A', 'B', 'A', 'A', 'C'],
     'date': ['01-01-15', '31-01-15', '03-02-15', '03-02-15', '15-03-16'],
     'attr': [1, 5, 3, 5, 6]},
    columns=['Id', 'date', 'attr']
)

df2 = pd.DataFrame(
    {'Id': ['A', 'B', 'A', 'A', 'B'],
     'date': ['01-02-15', '01-01-15', '02-02-15', '03-12-15', '15-03-16'],
     'counter': [1, 1, 2, 3, 2]},
    columns=['Id', 'date', 'counter']
)

# 将日期列转换为datetime类型(指定格式避免转换出错)
df1['date'] = pd.to_datetime(df1['date'], format='%d-%m-%y')
df2['date'] = pd.to_datetime(df2['date'], format='%d-%m-%y')

步骤2:使用merge_asof进行高效时间匹配连接

Pandas的merge_asof就是专门干这种按时间顺序匹配连接的活的,完美适配你的需求。不过它要求两个DataFrame都得按分组键(Id)和日期列先排序:

# 对两个DataFrame按Id和date排序(merge_asof的必要前提)
df1_sorted = df1.sort_values(['Id', 'date'])
df2_sorted = df2.sort_values(['Id', 'date'])

# 执行基于日期约束的连接
merged_df = pd.merge_asof(
    df1_sorted,
    df2_sorted,
    on='date',          # 用来做时间匹配的列
    by='Id',            # 分组匹配的键(保证只连接相同Id的记录)
    direction='backward'# 匹配规则:找不晚于当前date的最近一条记录
)

参数说明:

  • by='Id':确保只在相同Id的记录里匹配,不会跨Id乱连
  • direction='backward':对df1的每一行,在df2同Id的记录中,找日期小于等于当前行日期的最新那条(刚好契合你计数器随时间递增的场景,最新日期对应最大的计数器值)
  • 如果你的需求是找日期大于等于当前行日期的最早记录,把direction改成forward就行

步骤3:处理缺失值(可选)

如果df1里有df2没有对应Id、或者没有符合日期约束的记录,结果里的counter会是NaN,你可以按需填充:

# 用0填充缺失的counter值
merged_df['counter'] = merged_df['counter'].fillna(0)

运行结果示例

执行完上面的代码后,merged_df的输出大概是这样:

Id       date  attr  counter
0  A 2015-01-01     1      0.0
1  B 2015-01-31     5      1.0
2  A 2015-02-03     3      2.0
3  A 2015-02-03     5      2.0
4  C 2016-03-15     6      0.0

替代方法(适合小数据集)

如果你的数据集很小,也可以用groupby+apply的方式实现,但效率远不如merge_asof:

# 对df2按Id分组并按日期排序
df2_grouped = df2.sort_values('date').groupby('Id')

def get_latest_counter(row):
    # 获取当前Id对应的df2记录
    group = df2_grouped.get_group(row['Id']) if row['Id'] in df2_grouped.groups else pd.DataFrame()
    if group.empty:
        return 0
    # 筛选符合日期约束的记录
    valid_records = group[group['date'] <= row['date']]
    return valid_records.iloc[-1]['counter'] if not valid_records.empty else 0

# 应用到df1的每一行
df1['counter'] = df1.apply(get_latest_counter, axis=1)

总结

  • 优先用merge_asof,它是Pandas专为时间序列匹配设计的矢量化操作,大数据量下速度比循环/apply快太多
  • 一定要先把日期列转成datetime类型,不然没法正确比较日期大小
  • 根据你实际的日期约束需求调整direction参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:53:45