Python 提高重复分组操作的性能
我有一个带有多索引的数据帧,它基本上是一个二进制矩阵:Python 提高重复分组操作的性能,python,pandas,numpy,Python,Pandas,Numpy,我有一个带有多索引的数据帧,它基本上是一个二进制矩阵: day day01 day02 session session1 session2 session3 session1 session2 session3 0 1 0 0 0 0 0 1 0 0 1
day day01 day02
session session1 session2 session3 session1 session2 session3
0 1 0 0 0 0 0
1 0 0 1 1 1 0
2 1 1 1 0 0 1
3 1 0 0 1 0 0
4 1 0 1 0 0 0
从这个数据框中,我需要计算每行的每日总和:
day01 day02
0 1 0
1 1 2
2 3 1
3 1 1
4 2 0
session1 session2 session3
0 1 0 0
1 1 1 1
2 1 1 2
3 2 0 0
4 1 0 1
并获取0、1的数量。。。(值计数)在此总和中:
0 2
1 5
2 2
3 1
我也需要为会议做这个。每行的会话总数:
day01 day02
0 1 0
1 1 2
2 3 1
3 1 1
4 2 0
session1 session2 session3
0 1 0 0
1 1 1 1
2 1 1 2
3 2 0 0
4 1 0 1
并获取值计数:
0 5
1 8
2 2
作为基线,这是df.groupby(level='day',axis=1.sum().stack().value_counts()(和df.groupby(level='session',axis=1.sum().stack().value_counts()
)的结果。数据帧在模拟退火算法的每次迭代中发生变化,并重新计算这些计数。当我分析代码时,我发现在groupby操作上花费了大量时间
我试着保存groupby对象,并在每次迭代中对这些对象求和,但改进了大约10%。下面是创建更大数据帧的代码(类似于我的代码):
在我的计算机中,以下两种方法分别需要3.8s和3.38s
def try1(df, num_repeats=1000):
for i in range(num_repeats):
session_counts = (df.groupby(level='session', axis=1, sort=False)
.sum()
.stack()
.value_counts(sort=False))
daily_counts = (df.groupby(level='day', axis=1, sort=False)
.sum()
.stack()
.value_counts(sort=False))
return session_counts, daily_counts
def try2(df, num_repeats=1000):
session_groups = df.groupby(level='session', axis=1, sort=False)
day_groups = df.groupby(level='day', axis=1, sort=False)
for i in range(num_repeats):
df.iat[0, 0] = (i + 1) % 2
session_counts = session_groups.sum().stack().value_counts(sort=False)
daily_counts = day_groups.sum().stack().value_counts(sort=False)
return session_counts, daily_counts
%time try1(df)
Wall time: 3.8 s
%time try2(df)
Wall time: 3.38 s
注意:函数中的循环仅用于计时。对于第二个函数,为了获得正确的计时,我需要修改数据帧
我目前正在研究另一种方法,在不重新计算组的情况下,直接将数据帧中的更改反映到计数,但我还没有成功。跟踪受影响的行和更新保存的数据帧的速度变慢了
有没有办法提高这些groupby操作的性能 假设采用常规数据格式(每行的天数和会话数相等),下面是一种基于NumPy的方法,使用
np.unique
,输出的索引按排序顺序排列-
# Extract array
a,b = df.columns.levels
arr = df.values.reshape(-1,len(a),len(b))
# Get session counts
session_sums = arr.sum(1)
unq,count = np.unique(session_sums,return_counts=True)
session_counts_out = pd.Series(count,index=unq)
# Get daily count
daily_sums = arr.sum(2)
unq,count = np.unique(daily_sums,return_counts=True)
daily_counts_out = pd.Series(count,index=unq)
如果您只对没有索引的值感兴趣,那么这里有一个使用np.bincount
的替代方法,它基本上只进行计数,就像return\u counts
part withnp.unique
-
# Get session counts
session_sums = arr.sum(1)
count = np.bincount(session_sums.ravel())
session_counts_out = count[count>0]
# Get daily count
daily_sums = arr.sum(2)
count = np.bincount(daily_sums.ravel())
daily_counts_out = count[count>0]
两个输出中元素的顺序重要吗?另外,两个输出的索引是否重要?不,只要我知道其中有多少个0、1等,顺序(或哪个数据结构保存该信息)就不重要。我应该知道哪个对应0,哪个对应1。谢谢。看起来很有希望。让我试试。bincount大约比groupby快7倍(我删除了
count[count>0]
的部分,以便通过索引访问)。让我把它打开几天,看看是否还有其他选择。再次感谢您。使用np.bincount是一种非常反模式的方法-它只在非常有限的输入集上有效,并且不进行任何转换;你在强迫用户做这么多事情work@Jeff如果你说的是非常有限的输入集,你指的是数字,是的,它只指数字。但好的一面是表现。我想这是一个用户可以决定的权衡<代码>np。如第一种方法所述,unique始终作为良好的备份存在。来投票吧@杰夫:是的,我不知道如何在这种情况下使用pd.unique
。为什么不把它作为一个答案,我们可以计时呢?