我想知道如何将带有开始日期和结束日期列的定义表中的数据连接到我的事件表中,并带有日期列,其中日期列位于startdate和enddate列之间。
例如:
Table A: Columns = [Date, Reading]
Table B: Columns = [StartDate, EndDate, Amount]
在SQL中,我会执行以下操作:
Select Date, Reading, Amount From
A Join B on B.StartDate <= A.Date and B.EndDate > B.Date
我尝试创建一个过滤表B的函数,然后使用如下的apply。
def find(d):
r = B[(B['StartDate'] <= d['Date']) & (B['EndDate'] > d['Date'])]['Amount']
if r.count()>0:
return r.index[0]
return 0
A['Amount'] = A.apply(find, axis=1)
这可行,但速度非常慢,因为我的事件表大小约为20MB。
谢谢
答案 0 :(得分:0)
在A
中有40K行且B
中有100行的虚拟数据集中,以下
方法比使用提供的函数.apply
快约1000倍。
假设B看起来像下一帧,
>>> B
StartDate EndDate Amount
0 2000-01-01 2000-01-16 10
1 2000-02-01 2000-02-16 20
2 2000-03-01 2000-03-16 30
3 2000-04-01 2000-04-16 40
4 2000-05-01 2000-05-16 50
... ... ...
[100 rows x 3 columns]
B可以转换为具有索引的TimeSeries,如下所示 包含由B的范围和相应的'金额'定义的每一天 值。
def make_series(start, end, amount):
idx = pd.date_range(start, end, freq='D', closed='left')
return pd.Series([amount] * len(idx), index=idx)
def make_series2(s):
idx = pd.date_range(s['StartDate'], s['EndDate'], freq='D', closed='left')
return pd.Series([s['Amount']] * len(idx), index=idx)
# for non-overlapping ranges
>>> B2 = pd.concat([make_series(s, e, a) for _, s, e, a in B.itertuples()])
# for overlapping ranges
>>> B2 = B.apply(make_series2, axis=1).bfill().T[0]
>>> timeit B2 = pd.concat([make_series(s, e, a) for _, s, e, a in B.itertuples()])
100 loops, best of 3: 15.9 ms per loop
>>> timeit B2 = B.apply(make_series2, axis=1).bfill().T[0]
10 loops, best of 3: 54.9 ms per loop
>>> B2
2000-01-01 10
2000-01-02 10
...
2008-04-14 1000
2008-04-15 1000
Length: 1500
最后一步是使用B2
索引A.Date
:
>>> A['Amount'] = B2[A.Date].fillna(0).values
>>> timeit A['Amount'] = B2[A.Date].fillna(0).values
1000 loops, best of 3: 1.91 ms per loop
为了进行比较,这是同一帧上apply
的时间:
>>> timeit A.apply(find, axis=1)
1 loops, best of 3: 26.3 s per loop