Python 如何基于最近日期合并两个数据帧

Python 如何基于最近日期合并两个数据帧,python,pandas,dataframe,Python,Pandas,Dataframe,我想基于两列合并两个数据帧:“Code”和“Date”。基于“代码”合并数据帧是很简单的,但是在“日期”的情况下会变得很棘手——df1和df2中的日期之间没有精确的匹配。所以,我想选择最近的日期。我该怎么做 df = df1[column_names1].merge(df2[column_names2], on='Code') 我不认为有一种快速、简单的方法来做这类事情,但我相信最好的方法是这样做: 向df1添加一列,列中的日期与df2 对这些对象调用标准合并 随着数据量的增长,这种“最接近日

我想基于两列合并两个数据帧:“Code”和“Date”。基于“代码”合并数据帧是很简单的,但是在“日期”的情况下会变得很棘手——df1和df2中的日期之间没有精确的匹配。所以,我想选择最近的日期。我该怎么做

df = df1[column_names1].merge(df2[column_names2], on='Code')

我不认为有一种快速、简单的方法来做这类事情,但我相信最好的方法是这样做:

  • df1
    添加一列,列中的日期与
    df2

  • 对这些对象调用标准合并

  • 随着数据量的增长,这种“最接近日期”的操作可能会变得相当昂贵,除非您做一些复杂的事情。我喜欢用scikit learn的代码来做这类事情

    我提出了一种解决方案的方法,这种方法应该可以比较好地扩展。 首先,我们可以生成一些简单的数据:

    import pandas as pd
    import numpy as np
    dates = pd.date_range('2015', periods=200, freq='D')
    
    rand = np.random.RandomState(42)
    i1 = np.sort(rand.permutation(np.arange(len(dates)))[:5])
    i2 = np.sort(rand.permutation(np.arange(len(dates)))[:5])
    
    df1 = pd.DataFrame({'Code': rand.randint(0, 2, 5),
                        'Date': dates[i1],
                        'val1':rand.rand(5)})
    df2 = pd.DataFrame({'Code': rand.randint(0, 2, 5),
                        'Date': dates[i2],
                        'val2':rand.rand(5)})
    
    让我们看看这些:

    >>> df1
       Code       Date      val1
    0     0 2015-01-16  0.975852
    1     0 2015-01-31  0.516300
    2     1 2015-04-06  0.322956
    3     1 2015-05-09  0.795186
    4     1 2015-06-08  0.270832
    
    >>> df2
       Code       Date      val2
    0     1 2015-02-03  0.184334
    1     1 2015-04-13  0.080873
    2     0 2015-05-02  0.428314
    3     1 2015-06-26  0.688500
    4     0 2015-06-30  0.058194
    
    现在,让我们编写一个
    apply
    函数,使用scikit learn将最近日期列添加到
    df1

    from sklearn.neighbors import NearestNeighbors
    
    def find_nearest(group, match, groupname):
        match = match[match[groupname] == group.name]
        nbrs = NearestNeighbors(1).fit(match['Date'].values[:, None])
        dist, ind = nbrs.kneighbors(group['Date'].values[:, None])
    
        group['Date1'] = group['Date']
        group['Date'] = match['Date'].values[ind.ravel()]
        return group
    
    df1_mod = df1.groupby('Code').apply(find_nearest, df2, 'Code')
    >>> df1_mod
       Code       Date      val1      Date1
    0     0 2015-05-02  0.975852 2015-01-16
    1     0 2015-05-02  0.516300 2015-01-31
    2     1 2015-04-13  0.322956 2015-04-06
    3     1 2015-04-13  0.795186 2015-05-09
    4     1 2015-06-26  0.270832 2015-06-08
    
    最后,我们可以通过直接调用
    pd.merge
    ,将它们合并在一起:

    >>> pd.merge(df1_mod, df2, on=['Code', 'Date'])
       Code       Date      val1      Date1      val2
    0     0 2015-05-02  0.975852 2015-01-16  0.428314
    1     0 2015-05-02  0.516300 2015-01-31  0.428314
    2     1 2015-04-13  0.322956 2015-04-06  0.080873
    3     1 2015-04-13  0.795186 2015-05-09  0.080873
    4     1 2015-06-26  0.270832 2015-06-08  0.688500
    

    请注意,行0和行1都匹配相同的
    val2
    ;考虑到您描述所需解决方案的方式,这是意料之中的事。

    这里有一个替代解决方案:

  • 在代码上合并

  • 根据需要添加一个日期差异列(我在下面的示例中使用了abs),并使用新列对数据进行排序

  • 根据第一个数据帧的记录分组,并为每个组从第二个数据帧中获取一条日期最近的记录

  • 代码:


    相关问题:这里有一个更好的答案:@jakevdp:谢谢,但我如何将其与代码合并结合起来?我应该先使用“searchsorted”,然后再使用“mask=idx>=0&…”吗?与这个问题无关,但您的PYCON 2015 SKLEARN非常好。非常感谢分享!我对格式中的日期有问题:2015-10-19T07:42:00.000您知道如何解析它们以便代码工作吗?您可以使用
    pd将字符串转换为日期。到\u datetime()
    是否有方法修改
    查找最接近的日期
    ,以便只需要较少的参数和匹配日期?
    df = df1.reset_index()[column_names1].merge(df2[column_names2], on='Code')
    df['DateDiff'] = (df['Date1'] - df['Date2']).abs()
    df.sort_values('DateDiff').groupby('index').first().reset_index()