在Pandas条件加入中保留左/右连接逻辑

时间:2017-05-26 15:01:20

标签: python pandas numpy join

Pandas目前不支持SQL意义上的条件连接;但是,可以通过在公共字段上执行更大的连接,然后在连接后处理上应用过滤器来模拟(性能较差)。

但是,我正在寻找通过此条件连接保留左(或右)元素的方法。我通过后期处理失去了它们。

import pandas

# Create a dataframe
df = pandas.DataFrame([{'name': 'A', 'start': '20171201', 'end': '20180205'}, {'name': 'A', 'start': '20170901', 'end': '20170905'}, {'name': 'B', 'start': '20190101', 'end': '20190205'}])
df['start'] = pandas.to_datetime(df['start'])
df['end'] = pandas.to_datetime(df['end'])
print df


             end name      start
    0 2018-02-05    A 2017-12-01
    1 2017-09-05    A 2017-09-01
    2 2019-02-05    B 2019-01-01



# Create another dataframe, don't want to lose any data here.
v_df = pandas.DataFrame([{'name': 'A', 'val': 10, 'date': '20180101'}, {'name': 'B', 'val': 20, 'date': '20170101'}])
v_df['date'] = pandas.to_datetime(v_df['date'])
print v_df

            date name  val
    0 2018-01-01    A   10
    1 2017-01-01    B   20



# Conditional Left Join both dataframes, want to avoid losing the name B val.
v_df = v_df.merge(df, how='left', on=['name'])
v_df = v_df[v_df['date'].between(v_df['start'], v_df['end'])]
print v_df


            date name  val        end      start
    0 2018-01-01    A   10 2018-02-05 2017-12-01

在这种情况下,所需的输出如下,其中包括来自B的left记录。

        date name  val        end      start
0 2018-01-01    A   10 2018-02-05 2017-12-01
2 2017-01-01    B   20        NaT        NaT

解决方案还需要能够处理多个记录在常规合并中匹配的时间,但是当该组中没有一个匹配条件时,将返回空记录(而不是根本没有记录)。

2 个答案:

答案 0 :(得分:2)

我不确定这是最好的方法,但似乎可以做到这一点:

In [191]: v_df.merge(v_df.merge(df).query("start <= date <= end"), how='left')
Out[191]:
        date name  val        end      start
0 2018-01-01    A   10 2018-02-05 2017-12-01
1 2017-01-01    B   20        NaT        NaT

答案 1 :(得分:2)

filter_df = df.merge(v_df)
filter_df = filter_df[filter_df['date'].between(filter_df['start'], filter_df['end'])]
v_df.merge(filter_df, how='left')

应该返回你想要的东西。 filter_df标识了联接正确处理的行,您在问题中执行的操作,第二次合并将这些startend值仅附加到相关行。

修改

@ MaxU的答案功能相同,语法更优雅。