在pandas中同步两个大数据框的最有效方法是什么?
我想要同步两个非常长的数据框,性能在这个情况下非常重要。这两个数据框是按时间顺序排列的(我们应该利用这一点来尽可能提高速度),使用的是日期时间或时间戳。
这里有一种同步的方法,见下面的例子:
import pandas as pd
df1=pd.DataFrame({'A':[1,2,3,4,5,6], 'B':[1,5,3,4,5,7]}, index=pd.date_range('20140101 101501', freq='u', periods=6))
df2=pd.DataFrame({'D':[10,2,30,4,5,10], 'F':[1,5,3,4,5,70]}, index=pd.date_range('20140101 101501.000003', freq='u', periods=6))
# synch data frames
df3=df1.merge(df2, how='outer', right_index=True, left_index=True).fillna(method='ffill')
我的问题是,这种方法是否是最有效的?如果有更快的解决方案(比如使用numpy或cython),我愿意尝试其他方法。
谢谢
注意:时间戳通常不是均匀分布的(就像上面的例子),这个方法也应该适用于这种情况。
在阅读答案后的评论
我认为有很多情况是对齐、合并或连接都没有帮助的。关键是不要使用与数据库相关的语义来进行对齐(在我看来,对于时间序列来说,这并不太相关)。对我来说,对齐意味着将序列A映射到B,并且有办法处理缺失值(通常使用采样保持的方法),对齐和连接会导致一些不想要的效果,比如连接后出现多个重复的时间戳。我现在还没有找到完美的解决方案,但看起来np.searchsorted可以帮忙(它比多次调用连接/对齐来完成我需要的操作要快得多)。到目前为止,我还没有找到pandas的解决方案。
我该如何将A映射到B,使得结果包含A和B的所有时间戳,但没有重复(除了A和B中已经存在的那些)?
另一个典型的用例是采样保持同步,可以通过以下高效的方法解决(将A与B同步,即对A中的每个时间戳获取B中对应的值):
idx=np.searchsorted(B.index.values, A.index.values, side='right')-1
df=A.copy()
for i in B:
df[i]=B[i].ix[idx].values
结果的数据框包含A的相同索引和B中同步的值。
有没有有效的方法可以直接在pandas中做到这些?
3 个回答
在我看来,时间序列的同步是一个非常简单的过程。假设有一个叫做 ts# (#=0,1,2)
的数据结构,里面包含了以下内容:
ts#[0,:]
- 时间
ts#[1,:]
- 卖出价格(ask)
ts#[2,:]
- 买入价格(bid)
ts#[3,:]
- 卖出数量(asksz)
ts#[4,:]
- 买入数量(bidsz)
输出结果是:
totts[0,:]
- 同步后的时间
totts[1-4,:]
- ts0
的卖出价格/买入价格/卖出数量/买入数量
totts[5-8,:]
- ts1
的卖出价格/买入价格/卖出数量/买入数量
totts[9-12,:]
- ts2
的卖出价格/买入价格/卖出数量/买入数量
函数:
def syncTS(ts0,ts1,ts2):
ti0 = ts0[0,:]
ti1 = ts1[0,:]
ti2 = ts2[0,:]
totti = np.union1d(ti0, ti1)
totti = np.union1d(totti,ti2)
totts = np.ndarray((13,len(totti)))
it0=it1=it2=0
nT0=len(ti0)-1
nT1=len(ti1)-1
nT2=len(ti2)-1
for it,tim in enumerate(totti):
if tim >= ti0[it0] and it0 < nT0:
it0+=1
if tim >= ti1[it1] and it1 < nT1:
it1 += 1
if tim >= ti2[it2] and it2 < nT2:
it2 += 1
totts[0, it] = tim
for k in range(1,5):
totts[k, it] = ts0[k, it0]
totts[k + 4, it] = ts1[k, it1]
totts[k + 8, it] = ts2[k, it2]
return totts
如果你想用一个数据框(DataFrame)的索引来作为同步的模式,这可能会很有用:
df3 = df1.iloc[df1.index.isin(df2.index),]
注意:我猜df1的形状比df2的形状要大
在之前的代码片段中,你可以看到df1和df2中的元素,但如果你想添加新的索引,也许你更喜欢这样做:
new_indexes = df1.index.diff(df2.index) # indexes of df1 and not in df2
default_values = np.zeros((new_indexes.shape[0],df2.shape[1]))
df2 = df2.append(pd.DataFrame(default_values , index=new_indexes)).sort(axis=0)
你可以在这个 帖子 中看到另一种同步的方法
如果你需要对齐数据,那么可以使用 align
这个功能,相关的文档可以在 这里 找到。否则,合并数据也是一个不错的选择。
In [18]: N=100000
In [19]: df1=pd.DataFrame({'A':[1,2,3,4,5,6]*N, 'B':[1,5,3,4,5,7]*N}, index=pd.date_range('20140101 101501', freq='u', periods=6*N))
In [20]: df2=pd.DataFrame({'D':[10,2,30,4,5,10]*N, 'F':[1,5,3,4,5,70]*N}, index=pd.date_range('20140101 101501.000003', freq='u', periods=6*N))
In [21]: %timeit df1.merge(df2, how='outer', right_index=True, left_index=True).fillna(method='ffill')
10 loops, best of 3: 69.3 ms per loop
In [22]: %timeit df1.align(df2)
10 loops, best of 3: 36.5 ms per loop
In [24]: pd.set_option('max_rows',10)
In [25]: x, y = df1.align(df2)
In [26]: x
Out[26]:
A B D F
2014-01-01 10:15:01 1 1 NaN NaN
2014-01-01 10:15:01.000001 2 5 NaN NaN
2014-01-01 10:15:01.000002 3 3 NaN NaN
2014-01-01 10:15:01.000003 4 4 NaN NaN
2014-01-01 10:15:01.000004 5 5 NaN NaN
... .. .. .. ..
2014-01-01 10:15:01.599998 5 5 NaN NaN
2014-01-01 10:15:01.599999 6 7 NaN NaN
2014-01-01 10:15:01.600000 NaN NaN NaN NaN
2014-01-01 10:15:01.600001 NaN NaN NaN NaN
2014-01-01 10:15:01.600002 NaN NaN NaN NaN
[600003 rows x 4 columns]
In [27]: y
Out[27]:
A B D F
2014-01-01 10:15:01 NaN NaN NaN NaN
2014-01-01 10:15:01.000001 NaN NaN NaN NaN
2014-01-01 10:15:01.000002 NaN NaN NaN NaN
2014-01-01 10:15:01.000003 NaN NaN 10 1
2014-01-01 10:15:01.000004 NaN NaN 2 5
... .. .. .. ..
2014-01-01 10:15:01.599998 NaN NaN 2 5
2014-01-01 10:15:01.599999 NaN NaN 30 3
2014-01-01 10:15:01.600000 NaN NaN 4 4
2014-01-01 10:15:01.600001 NaN NaN 5 5
2014-01-01 10:15:01.600002 NaN NaN 10 70
[600003 rows x 4 columns]