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

如何基于时间区间在两个Pandas DataFrame间映射值?

Pandas 日期区间匹配映射Status方案

第一步:统一日期类型

首先得把所有日期列转成datetime格式,不然没法做区间比较:

import pandas as pd

# 构造示例数据
df1 = pd.DataFrame({
    'Column1': [1, 2, 3],
    'Column2': ['2021-06-07', '2021-06-10', '2021-06-25']
})

df2 = pd.DataFrame({
    'Status': ['A', 'B', 'C', 'D'],
    'Type': ['Closed', 'Closed', 'Closed', 'Active'],
    'Start': ['2021-06-06', '2021-06-20', '2021-07-04', '2021-07-25'],
    'End': ['2021-06-17', '2021-07-01', '2021-07-22', '2021-08-05']
})

# 转换日期类型
df1['Column2'] = pd.to_datetime(df1['Column2'])
df2['Start'] = pd.to_datetime(df2['Start'])
df2['End'] = pd.to_datetime(df2['End'])

方法一:逐行匹配(小数据集用这个简单)

写个小函数,对df1里的每个日期,去df2里找落在Start和End之间的记录,把对应的Status取出来:

def get_status(date):
    # 筛选符合区间的行
    match_row = df2[(df2['Start'] <= date) & (df2['End'] >= date)]
    # 有匹配就返回Status,没有就返回None
    return match_row['Status'].iloc[0] if not match_row.empty else None

# 给df1加Status列
df1['Status'] = df1['Column2'].apply(get_status)

运行后得到的结果就是你要的:

Column1    Column2 Status
0        1 2021-06-07      A
1        2 2021-06-10      A
2        3 2021-06-25      B

方法二:高效匹配(大数据集首选)

如果数据量很大,用apply会很慢,这时候用merge_asof更高效。不过要先把df2按Start排序,然后匹配最后一个不晚于目标日期的Start,再过滤掉超过End的记录:

# 先对df2按Start排序
df2_sorted = df2.sort_values('Start')

# 执行asof合并
merged_df = pd.merge_asof(
    df1.sort_values('Column2'),
    df2_sorted,
    left_on='Column2',
    right_on='Start',
    direction='backward'  # 找最后一个符合条件的Start
)

# 过滤掉日期超过End的行,整理成目标格式
result_df = merged_df[merged_df['Column2'] <= merged_df['End']][['Column1', 'Column2', 'Status']].sort_values('Column1').reset_index(drop=True)

注意点

  • 一定要先转日期类型,不然字符串比较会出错
  • 如果df1里有日期不在任何df2的区间里,上面的方法会返回None,你可以根据需求自己处理空值
  • 如果df2有重叠区间,方法一会返回第一个匹配的Status,方法二会返回最后一个符合Start条件且在End内的Status,根据你的业务选就行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 17:57:23