代码之家  ›  专栏  ›  技术社区  ›  AZhao

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

  •  1
  • AZhao  · 技术社区  · 7 年前

    我有两个数据帧需要一个复杂的连接操作,这超出了熊猫的合并能力。

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

    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与每一个guid关联起来,但无法与pandas关联。例如,正确联接的df1和df2的第一行的guid为1,因为它是并且在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.分段解决方案会奏效:

    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来处理这个问题,但是在它周围遇到了可伸缩性问题。

    1 回复  |  直到 7 年前
        1
  •  3
  •   BENY    7 年前

    这是我们的第一条路 unnesting 在创建日期范围之后,我们只需要 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')
    
        2
  •  0
  •   AZhao    7 年前

    Wenyoben的答案是有效的,但是它是如此复杂(或优雅),以至于我们很难证明将它放入代码库中是正确的。

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

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