我有两个数据框,除了熊猫的合并功能之外,还需要复杂的联接操作。
数据框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来处理此问题,但是遇到了围绕它的可伸缩性问题。
答案 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'])]