如何使用 groupbys 成为更快的熊猫
How to be a faster Panda with groupbys
我有一个包含 1.5 亿行的 Pandas 数据框。其中大约有 100 万个组,我想对其进行一些非常简单的计算。例如,我想采用一些现有的列 'A'
并创建一个新列 'A_Percentile'
,将 'A'
的值表示为组内的百分位等级。这是一个执行此操作的小函数:
from scipy.stats import percentileofscore
def rankify(column_name,data=my_data_frame):
f = lambda x: [percentileofscore(x, y) for y in x]
data[column_name+'_Percentile'] = data.groupby(['Group_variable_1',
'Group_variable_2'])[column_name].transform(f)
return
那么你可以这样称呼它:
rankify('Column_to_Rank', my_data_frame)
并等待...相当长的时间。
我可以做一些明显的事情来加快速度(例如,我确信有一种方法可以矢量化 [percentileofscore(x, y) for y in x]
)。但是,我觉得我可以使用一些 Pandas 技巧来极大地加快速度。我可以用 groupby
逻辑做些什么吗?我考虑过将其拆分并并行化,但是 1. 我不确定这样做的好方法和 2. 写出数据和读取结果的通信时间似乎需要几乎一样长的时间(也许我认为是因为第 1 点)。
正如您可能知道的那样,groupby 操作的速度可能会有很大差异——尤其是当组的数量变多时。这是一个非常简单的替代方法,它在我尝试过的一些测试数据集上要快得多(快 2 倍到 40 倍)。如果你能避免用户编写的函数(与 groupby 结合)并坚持使用内置函数(通常是 cythonized)通常会更快:
In [163]: %timeit rankify('x',df)
1 loops, best of 3: 7.38 s per loop
In [164]: def rankify2(column_name,data):
...: r1 = data.groupby('grp')[column_name].rank()
...: r2 = data.groupby('grp')[column_name].transform('count')
...: data[column_name+'_Percentile2'] = 100. * r1 / r2
In [165]: %timeit rankify2('x',df)
10 loops, best of 3: 178 ms per loop
请注意,与 percentileofscore()
相比,我的方法给出的结果略有不同(例如 10e-15
的差异)。因此,如果您使用 x == y
测试结果,大多数结果为 True,但有些为 False,但 x.round() == y.round()
将通过。
对于上面的结果,这是我的测试数据集(对于我尝试过的其他情况,差异较小但总是 2 倍或更好的加速):
df = pd.DataFrame( { "grp" : np.repeat( np.arange(1000), 100 ),
"x" : np.random.randn(100000) } )
如果您愿意,我相信您可以做得更好。实际上,您在这里需要做的就是排序和排名。我怀疑我采用的基本方法将是一个很好的方法,但如果你在 numpy 或 numba 中完成了部分或全部,你可能会加快速度。此外,您也许可以使用一些 pandas 索引技巧来加快速度。
我有一个包含 1.5 亿行的 Pandas 数据框。其中大约有 100 万个组,我想对其进行一些非常简单的计算。例如,我想采用一些现有的列 'A'
并创建一个新列 'A_Percentile'
,将 'A'
的值表示为组内的百分位等级。这是一个执行此操作的小函数:
from scipy.stats import percentileofscore
def rankify(column_name,data=my_data_frame):
f = lambda x: [percentileofscore(x, y) for y in x]
data[column_name+'_Percentile'] = data.groupby(['Group_variable_1',
'Group_variable_2'])[column_name].transform(f)
return
那么你可以这样称呼它:
rankify('Column_to_Rank', my_data_frame)
并等待...相当长的时间。
我可以做一些明显的事情来加快速度(例如,我确信有一种方法可以矢量化 [percentileofscore(x, y) for y in x]
)。但是,我觉得我可以使用一些 Pandas 技巧来极大地加快速度。我可以用 groupby
逻辑做些什么吗?我考虑过将其拆分并并行化,但是 1. 我不确定这样做的好方法和 2. 写出数据和读取结果的通信时间似乎需要几乎一样长的时间(也许我认为是因为第 1 点)。
正如您可能知道的那样,groupby 操作的速度可能会有很大差异——尤其是当组的数量变多时。这是一个非常简单的替代方法,它在我尝试过的一些测试数据集上要快得多(快 2 倍到 40 倍)。如果你能避免用户编写的函数(与 groupby 结合)并坚持使用内置函数(通常是 cythonized)通常会更快:
In [163]: %timeit rankify('x',df)
1 loops, best of 3: 7.38 s per loop
In [164]: def rankify2(column_name,data):
...: r1 = data.groupby('grp')[column_name].rank()
...: r2 = data.groupby('grp')[column_name].transform('count')
...: data[column_name+'_Percentile2'] = 100. * r1 / r2
In [165]: %timeit rankify2('x',df)
10 loops, best of 3: 178 ms per loop
请注意,与 percentileofscore()
相比,我的方法给出的结果略有不同(例如 10e-15
的差异)。因此,如果您使用 x == y
测试结果,大多数结果为 True,但有些为 False,但 x.round() == y.round()
将通过。
对于上面的结果,这是我的测试数据集(对于我尝试过的其他情况,差异较小但总是 2 倍或更好的加速):
df = pd.DataFrame( { "grp" : np.repeat( np.arange(1000), 100 ),
"x" : np.random.randn(100000) } )
如果您愿意,我相信您可以做得更好。实际上,您在这里需要做的就是排序和排名。我怀疑我采用的基本方法将是一个很好的方法,但如果你在 numpy 或 numba 中完成了部分或全部,你可能会加快速度。此外,您也许可以使用一些 pandas 索引技巧来加快速度。