如何在pandas中同步两个大型数据框的最有效方法?

3

我希望能够同步两个非常长的数据框,性能在这种情况下非常关键。这两个数据框按照时间顺序进行索引(这应该被充分利用以尽可能快地完成同步),使用datetimes或Timestamps。

以下是一个同步的示例:

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可以帮助解决问题(它比使用多次调用join / align做我需要做的事情要快得多)。到目前为止,我还没有找到一种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

结果 df 包含 A 的相同索引和 B 中同步的值。

在 Pandas 中有没有直接执行此操作的有效方法?


1
还有join方法,但我没有测试过它是否更快。大部分的pandas代码已经被cython化了,所以我认为你不会能够显著提高所需的时间。 - Midnighter
@Midnighter:看起来join是最快的,%timeit df1.join(df2, how='outer') # 100次循环,3个中的最佳表现:每个循环10.1毫秒。 - Mannaggia
3个回答

5
如果您需要同步,则使用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]

对齐和连接不是我需要的,我需要将时间序列A映射到B中,不需要关系型数据库逻辑,我不想创建新的时间戳(重复)。通过在A和B中都有重复的时间戳来创建新的时间戳。 - Mannaggia

2

如果你希望使用其中一个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)

您可以在此帖子中看到另一种同步方式。


0
在我看来,时间序列同步是一个非常简单的过程。假设ts# (#=0,1,2)被填充为:

ts#[0,:] - 时间

ts#[1,:] - 询价

ts#[2,:] - 出价

ts#[3,:] - 询价量

ts#[4,:] - 出价量

输出为:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接