具有日期范围和相等条件的复杂联接

时间:2019-05-21 22:12:24

标签: pandas numpy

我有两个数据框,除了熊猫的合并功能之外,还需要复杂的联接操作。

数据框1是一组帐户记录,其中有多个GUID映射到各个RIDS。但是,保证它们具有不重叠的开始和结束日期。

df1 = pd.DataFrame({'StartDate': pd.date_range('2010-01-01', periods=4, freq='5D'),
                    'EndDate': pd.date_range('2010-01-04', periods=4, freq='5D'),
                    'rid': ['A', 'A', 'A', 'B'],
                    'GUID': ['1', '2', '3', '4']})
>>> df1

        StartDate   EndDate    rid  GUID
    0   2010-01-01  2010-01-04  A   1
    1   2010-01-06  2010-01-09  A   2
    2   2010-01-11  2010-01-14  A   3
    3   2010-01-01  2010-01-19  B   4

数据框2是按日期和RID进行的一组交易。

rid_list = [random.choice(("A", "B")) for x in range(50)]

df2 = pd.DataFrame(dict(values=np.random.randn(
    50), date_time=pd.date_range('2010-01-01', periods=50, freq='D'), rid=rid_list))

>>> df2.head()

         values     date_time   rid 
    0   -0.214056   2010-01-01  A
    1   0.168259    2010-01-02  A
    2   -1.214433   2010-01-03  B
    3   0.314966    2010-01-04  A
    4   1.953925    2010-01-05  B
    5   -0.027883   2010-01-06  A
    6   -0.207795   2010-01-07  B
    7   0.530119    2010-01-08  A
    8   -0.297716   2010-01-09  B
    9   2.080151    2010-01-10  B

我需要将正确的GUID与每个人相关联,但无法与大熊猫关联。例如,正确连接的df1和df2的第一行的GUID为1,因为它是A,并且出现在GUID 1的时间范围内。

>>> df2.head()

         values     date_time   rid  GUID
    0   -0.214056   2010-01-01  A    1
    1   0.168259    2010-01-02  A    1
    2   -1.214433   2010-01-03  B    4
    3   0.314966    2010-01-04  A    1
    4   1.953925    2010-01-05  B    4
    5   -0.027883   2010-01-06  A    2
    6   -0.207795   2010-01-07  B    4
    7   0.530119    2010-01-08  A    2
    8   -0.297716   2010-01-09  B    4
    9   2.080151    2010-01-10  B    4

从这个线程Merging dataframes based on date range,我认为一个np.piecewise解决方案会起作用:

df2['GUID'] = np.piecewise(np.zeros(len(df2)), [(df2.date_time.values >= start_date) & (
    df2.date_time.values <= end_date) & (df2.rid == rid) for start_date, end_date, rid in zip(df1.StartDate.values, df1.EndDate.values, df1.rid.values)], df1.GUID.values)

不幸的是,这返回:

ValueError: with 1 condition(s), either 1 or 2 functions are expected

任何想法如何做到这一点?我过去使用pandasql来处理此问题,但是遇到了围绕它的可伸缩性问题。

2 个答案:

答案 0 :(得分:3)

这是第一种方法unnesting df1,创建日期范围后,我们只需要merge

df1['date_time']=[pd.date_range(x,y) for x , y in zip(df1.StartDate,df1.EndDate)]
df2=df2.merge(unnesting(df1,['date_time']).drop(['StartDate','EndDate'],1),how='left')

def unnesting(df, explode):
    idx = df.index.repeat(df[explode[0]].str.len())
    df1 = pd.concat([
        pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
    df1.index = idx
    return df1.join(df.drop(explode, 1), how='left')

答案 1 :(得分:0)

WeNYoBen的答案有效,但过于复杂(或优雅),以至于我们很难证明将其放入我们的代码库中。

我们最终得到了一个更简单,更黑的解决方案,它的内存效率可能要低得多。它会在rid上创建一个较大的合并df,然后进行过滤。

df3 = df2.merge(df1, on='rid')
df3[(df3['date_time'] <= df3['EndDate']) &
    (df3['StartDate'] >= df3['date_time'])]