我有一个相当大的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或日期。然而,这看起来像熊猫应该比我能更有效地为我做的事情。
总而言之,问题将是:
是否有pandas函数允许我将数据框拆分为字典,其中的键是某些列的值,其值是原始数据帧的切片?
为什么预先削减数据帧比使用索引直接访问数据帧快200倍?由于我正在使用的索引只是从0到len(df)的整数,我希望它能够像访问任何Python数据结构一样快,但字典似乎赢了。
否则,更一般地说,如果我们知道我们需要哪条线路,是否有更快的方式来访问逃离我的大型数据帧?