我想找到一种更有效的方法(在峰值内存使用率和可能的时间方面)来完成panda的groupby.ngroup
工作,这样在处理大型数据集时就不会遇到内存问题(下面我提供了为什么本专栏对我有用的原因)。以一个小数据集为例。我可以使用groupby.ngroup
轻松完成这项任务。你知道吗
import pandas as pd
import numpy as np
df = pd.DataFrame(np.array(
[[0, 1, 92],
[0, 0, 39],
[0, 0, 32],
[1, 0, 44],
[1, 1, 50],
[0, 1, 11],
[0, 0, 14]]), columns=['male', 'edu', 'wage'])
df['group_id'] = df.groupby(['male', 'edu']).ngroup()
df
male edu wage group_id
0 0 1 92 1
1 0 0 39 0
2 0 0 32 0
3 1 0 44 2
4 1 1 50 3
5 0 1 11 1
6 0 0 14 0
但是当我开始使用更大的数据集时,内存使用和计算时间会爆炸,groupby中的内存使用与数据帧内存使用的比率在N=100,000,000
比N=100,000
增加了近三倍。见下文。你知道吗
from memory_profiler import memory_usage
import time
N_values = [10**k for k in range(4, 9)]
stats = pd.DataFrame(index=N_values, dtype=float, columns=['time', 'basemem', 'groupby_mem'])
for N in N_values:
df = pd.DataFrame(
np.hstack([np.random.randint(0, 2, (N, 2)), np.random.normal(5, 1, (N, 1))]),
columns=['male', 'edu', 'wage']
)
def groupby_ngroup():
df.groupby(['male', 'edu']).ngroup()
def foo():
pass
basemem = max(memory_usage(proc=foo))
tic = time.time()
mem = max(memory_usage(proc=groupby_ngroup))
toc = time.time() - tic
stats.loc[N, 'basemem'] = basemem
stats.loc[N, 'groupby_mem'] = mem
stats.loc[N, 'time'] = toc
stats['mem_ratio'] = stats.eval('groupby_mem/basemem')
stats
time basemem groupby_mem mem_ratio
10000 0.037834 104.781250 105.359375 1.005517
100000 0.051785 108.187500 113.125000 1.045638
1000000 0.143642 128.156250 182.437500 1.423555
10000000 0.644650 334.148438 820.183594 2.454549
100000000 6.074531 2422.585938 7095.437500 2.928869
为什么我对这个组标识符感兴趣?因为我想创建使用pandas groupby
函数的列,比如使用.map
方法的groupby.mean
,而不是占用大量内存和时间的groupby.transform
。此外,.map
方法可用于dask
数据帧,因为dask
当前不支持.transform
。对于"group_id"
列,我可以简单地执行means = df.groupby(['group_id'])['wage'].mean()
和df['mean_wage'] = df['group_id'].map(means)
来执行transform
的工作。你知道吗
不使用
ngroup
,而是编写我们自己的函数来创建group_id
列怎么样?你知道吗下面是一段代码片段,它似乎提供了更好的性能:
本质上,我们使用列是数字的事实,并将它们视为二进制数。
group_id
应为十进制等效值。你知道吗将其缩放为三列可以得到类似的结果。为此,请将数据帧初始化替换为以下内容:
组id函数:
测试结果如下:
让我们尝试使用
hash
对于groupby中groupby变量的模式未知的groupby来说,
groupby.ngroup
似乎是最好的。但是如果groupby变量都是分类的,例如,取值0,1,2,3....
,那么我们可以从@saurjog
给出的解决方案中得到启发。你知道吗为了生成组ID,我们可以构建一个数值表达式来计算groupby变量的特殊和。考虑以下功能
函数
gen_groupby_numexpr
生成数值表达式,ngroup_cat
为by
中具有唯一值计数numcats
的groupby变量生成组id。因此,考虑以下与我们的用例匹配的数据集。它包含3个分类变量,我们将使用它们来形成groupby,其中两个变量在{0,1}
中取值,一个变量在{0,1,2}
中取值。你知道吗如果我们生成数值表达式,我们得到:
总之,我们可以用
从中我们得到
2*2*3=12
唯一组ID:当我将上述解决方案与
groupby.ngroup
作基准时,它在N=10,000,000
的数据集上运行速度快了近3倍,并且使用的额外内存明显减少。你知道吗现在我们可以通过方法来估计这些组,然后将它们映射回整个数据帧来进行转换工作。我计算了一些关于使用
transform
还是groupby
的基准测试,结果不一,那么map
更快,占用的内存更少。如果你计算的是多变量组的平均数,那么我认为后者更有效。此外,后者也可以在dask
中完成,其中transform
还不受支持。你知道吗相关问题 更多 >
编程相关推荐