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
解决方案还需要能够处理多个记录在常规合并中匹配的时间,但是当该组中没有一个匹配条件时,将返回空记录(而不是根本没有记录)。
答案 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
标识了联接正确处理的行,您在问题中执行的操作,第二次合并将这些start
和end
值仅附加到相关行。
修改强>
@ MaxU的答案功能相同,语法更优雅。