pandas:将大型数据框拆分为许多较小的数据框,以便更快地访问

时间:2017-04-24 16:09:21

标签: python performance pandas dataframe

我有一个相当大的pandas数据帧(~1.5M行,但只有6列),其中包含类似结构化的数据:一个“Date”维度(大约2200个日期),一个“Tenor”维度(9个tenors)和一个“id”维度(大约75个ID),加上3个“值”列。缺少三元组的是nans,所以~1.5M行完全来自2200 x 75 x 9.举例来说,当选择第一个日期和前两个ID时,它看起来如下:

    Tenor   Date    id  v1  v2  v3
0   1W  2008-03-27  id1 NaN NaN NaN
1   1M  2008-03-27  id1 0.2546  1.1022  13.5355
2   2M  2008-03-27  id1 0.4634  1.4812  13.4620
3   3M  2008-03-27  id1 0.6346  1.7242  13.4069
4   6M  2008-03-27  id1 1.0010  2.1278  13.3204
5   9M  2008-03-27  id1 NaN NaN NaN
6   12M 2008-03-27  id1 1.2576  2.3298  13.3668
7   18M 2008-03-27  id1 NaN NaN NaN
8   24M 2008-03-27  id1 1.3658  2.6527  13.6518
9   1W  2008-03-27  id2 NaN NaN NaN
10  1M  2008-03-27  id2 0.8800  -1.0000 12.7500
11  2M  2008-03-27  id2 1.0340  -1.0000 12.7500
12  3M  2008-03-27  id2 1.2050  -1.0000 12.7500
13  6M  2008-03-27  id2 1.4670  -1.0000 12.7500
14  9M  2008-03-27  id2 NaN NaN NaN
15  12M 2008-03-27  id2 1.7230  -1.0000 12.7500
16  18M 2008-03-27  id2 NaN NaN NaN
17  24M 2008-03-27  id2 1.7075  -1.6000 12.7500

我需要以有效的方式多次访问每个长度为9的~165k子数据帧(通过选择一个日期和一个id)。标准切片如建议的那样在Splitting dataframe into multiple dataframes中不会成功:每个切片都必须查看整个数据帧,如果我希望查看所有子数据帧,通常会产生线性访问时间和O(n ^ 2)操作。以这种方式隔离9x3子数据帧,例如:

from dateutil import parser
d = parser.parse('20150101')
%timeit df[np.logical_and(df.id=='id1', df.Date==d)

需要大约120毫秒,这在我的情况下是不可接受的。

在通过三个有趣的列(df.sort_values(['Date','id','Tenor']))对数据帧进行排序后,我可以通过利用数据帧的高规律性来使用索引:

ids= pd.unique(df.id)
starting = {
    p: {d: j*9*len(ids) + i*9 for j,d in enumerate(pd.to_datetime(dates))}
    for i, p in enumerate(list(ids))
}

然后我可以通过切片来访问它,这样就无需查看整个数据帧并显着提高效率:

s = starting['id1'][parser.parse('20150101')]
%timeit df.loc[s:s+8]

大约200μs,大约快500倍。

但是,这远远不是最好的。如果我尽可能地绕过pandas,我可以将数据帧拆分成~16.5万个微小的数据帧,将它们放入字典中,然后轻松访问它们:

dates = pd.unique(df.Date)
dfs = {p: {} for p in set(df.id.values)}
for p in ids:
    dfs[p] = {d: df.loc[starting[p][d]:starting[p][d]+8,:] for d in pd.to_datetime(dates)}

这会使(至少)使用的内存增加一倍,但

d = parser.parse('20150101')
%timeit dfs['id1'][d]

在1μs左右给出了更好的结果,比上面提高了200倍,并且可能达到了我们可以达到的最低值(已经%timeit starting['id1'][d]需要大约1μs)。

然而,这感觉不好,不仅仅是因为额外的内存使用,而且特别是因为它需要我手动创建大量的数据帧,而在幕后这需要为malloc调用165k次。这是一个巨大的浪费,因为我们事先知道我们需要多少内存(实际上我们已经分配了它!),虽然它仍然是我的情况下的最佳选择,因为预计算时间与我的次数相比相形见绌。将需要访问这些子数据帧。此外,它可以推广到在不太理想的情况下数据帧的不均匀划分,例如,期限的数量取决于id或日期。然而,这看起来像熊猫应该比我能更有效地为我做的事情。

总而言之,问题将是:

  1. 是否有pandas函数允许我将数据框拆分为字典,其中的键是某些列的值,其值是原始数据帧的切片?

  2. 为什么预先削减数据帧比使用索引直接访问数据帧快200倍?由于我正在使用的索引只是从0到len(df)的整数,我希望它能够像访问任何Python数据结构一样快,但字典似乎赢了。

  3. 否则,更一般地说,如果我们知道我们需要哪条线路,是否有更快的方式来访问逃离我的大型数据帧?

0 个答案:

没有答案