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

按分组匹配最近的较早日期合并DataFrame的方法

我来帮你解决按分组匹配最近较早日期合并两个DataFrame的问题,先把你提供的代码补全并做必要预处理,然后给你两种实用的解决方案:

第一步:补全数据生成代码并预处理

首先,你的代码有些截断,我先补全生成两个带分组列(模拟实际场景中的用户/分组标识)的DataFrame,同时把时间列转为datetime类型(这是时间匹配的前提):

import pandas as pd
import datetime
import numpy as np

def random_date(start, minutesList):
    current = start
    out_ = []
    for min_ in minutesList:
        curr = current + datetime.timedelta(minutes=min_)
        out_.append(curr.strftime("%d/%m/%y %H:%M"))
    return out_

startDate = datetime.datetime(2013, 9, 20,13,00)
minutesListUsages = [2, 5, 6, 35, 38, 45, 57]
minutesListLogins = [0, 1, 1.5, 3, 5.5, 24, 37, 37.5, 39.5, 45, 48, 53, 59, 60]

# 生成带分组列(user_id)的DataFrame,模拟分组场景
df_usages = pd.DataFrame({
    'user_id': np.repeat(['A', 'B'], len(minutesListUsages)//2 + len(minutesListUsages)%2),
    'usage_time': random_date(startDate, minutesListUsages)
})

df_logins = pd.DataFrame({
    'user_id': np.repeat(['A', 'B'], len(minutesListLogins)//2),
    'login_time': random_date(startDate, minutesListLogins)
})

# 转换时间列为datetime类型,才能进行时间比较
df_usages['usage_time'] = pd.to_datetime(df_usages['usage_time'], format="%d/%m/%y %H:%M")
df_logins['login_time'] = pd.to_datetime(df_logins['login_time'], format="%d/%m/%y %H:%M")
方法一:用pd.merge_asof高效匹配(推荐)

Pandas的merge_asof是专门为这种按分组匹配最近时间的场景设计的,效率极高,适合大数据量:

# 先对两个DataFrame按分组列+时间列排序(merge_asof要求必须排序)
df_usages_sorted = df_usages.sort_values(['user_id', 'usage_time'])
df_logins_sorted = df_logins.sort_values(['user_id', 'login_time'])

# 执行按分组的最近较早日期匹配
merged_df = pd.merge_asof(
    df_usages_sorted,
    df_logins_sorted,
    by='user_id',          # 指定分组键,同一分组内匹配
    left_on='usage_time',  # 左表的时间键
    right_on='login_time', # 右表的时间键
    direction='backward'   # 匹配规则:找最近的、早于左表时间的记录
)

print(merged_df)

关键参数说明:

  • direction='backward':确保只匹配早于等于当前usage时间的最近login记录;如果要找最近的晚于usage的记录,用forward;找距离最近的(不管早晚)用nearest。
  • 如果没有分组需求(全局匹配),只需去掉by参数,仅按时间列排序即可。
方法二:分组自定义匹配(灵活适配复杂逻辑)

如果需要更灵活的自定义规则(比如额外过滤条件),可以用groupby+自定义函数实现:

def match_closest_login(usage_group, login_data):
    # 对当前分组的每条usage记录,匹配最近的较早login
    matched_logins = []
    for _, row in usage_group.iterrows():
        # 筛选同一分组下,login时间早于等于当前usage时间的记录
        valid_logins = login_data[
            (login_data['user_id'] == usage_group.name) & 
            (login_data['login_time'] <= row['usage_time'])
        ]
        if not valid_logins.empty:
            # 取时间最晚的那条(即最近的较早login)
            closest_login = valid_logins.loc[valid_logins['login_time'].idxmax()]
            matched_logins.append(closest_login['login_time'])
        else:
            # 没有符合条件的login时,填充空值
            matched_logins.append(pd.NaT)
    usage_group['closest_login_time'] = matched_logins
    return usage_group

# 按分组应用匹配函数
merged_df_custom = df_usages.groupby('user_id').apply(
    lambda x: match_closest_login(x, df_logins)
).reset_index(drop=True)

print(merged_df_custom)

这种方法虽然不如merge_asof高效,但可以轻松添加额外的过滤、计算逻辑,适合个性化需求场景。

注意事项
  • 务必确保时间列是datetime类型,否则无法进行时间比较和匹配。
  • 如果某个usage记录没有更早的login记录,结果中对应的login时间会是pd.NaT,可以根据需求用fillna处理或过滤掉这些行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:53:20