为什么numpy函数的组合比np.mean更快?

7
我在想在numpy中进行平均计算的最快方法是什么。我使用了以下代码进行实验:
import time
n = 10000
p = np.array([1] * 1000000)

t1 = time.time()
for x in range(n):
    np.divide(np.sum(p), p.size)
t2 = time.time()

print(t2-t1)

3.9222593307495117

t3 = time.time()
for x in range(n):
    np.mean(p)
t4 = time.time()

print(t4-t3)

5.271147012710571

我会认为np.mean会更快或者至少速度相当,然而看起来numpy函数的组合比np.mean更快。为什么numpy函数的组合更快呢?

2
确认了,在我的电脑上也得到了同样的结果。这是一个奇怪的结果!归根结底,np.sum()np.mean() 更快。 (np.divide 的调用是微不足道的,因为你只是给它两个数字作为输入,而不是数组。) 我必须检查这两个函数的底层实现才能理解原因。我发现令人惊讶的是,在幕后,np.mean() 实际上没有运行你的替代代码... - joanis
2个回答

9

对于整数输入,默认情况下,numpy.mean 在 float64 dtype 中计算总和。这可以防止溢出错误,但需要为每个元素进行转换。

使用 numpy.sum 的代码只需在求和后进行一次转换。


1
这得益于我进行的实验,将random(1000000)相加而不是[1]*1000000。在这种情况下,np.mean实际上更快,确切地说几乎完全相同。 - Dr. V
谢谢您的回答!但是,我不太确定如何重现V博士的实验。我是否需要创建一个包含必须以float64数据类型存储的数字的列表才能看到相同的性能? - Kevin van der Gugten
2
@KevinvanderGugten:一种方法是设置p2 = p.astype('float64'),然后使用p2进行计时。(确保将转换放在计时代码之外。) - user2357112
谢谢!结果正如你们所说的一样。 - Kevin van der Gugten
使用 ipythontimeit 和一个大的浮点数 x,在 np.mean(x)np.sum(x)/x.sizex.sum()/x.sizenp.add.reduce(x)/x.size 之间的差异在统计上是可以忽略不计的。 - hpaulj
对于整数数组,“np.mean(x)”的时间大约相当于“np.add.reduce(x, dtype = float) / x.size”。 - hpaulj

-4
在这种情况下,np.sum和np.divide的组合比np.mean更快的原因是,np.mean是用Python实现的,而np.sum和np.divide是用C实现的,这使它们更快。
np.mean内部调用np.sum然后将结果除以数组的大小,因此,np.sum和np.divide的组合本质上正在执行与np.mean相同的操作,但不需要调用Python函数的开销。
通常,对于大型数组,使用NumPy中用C实现的函数比使用Python更快。

4
我并不真的相信这个解释。开销是恒定且可以忽略不计的。 - Dr. V
你可以看到 np.mean 是一个 Python 函数,而 np.sum 和 np.divide 是 C 函数。这就是为什么对于大数组而言,np.sum 和 np.divide 通常比 np.mean 更快的原因。@PranavHosangadi https://github.com/numpy/numpy/blob/master/numpy/core/fromnumeric.py#L2995 | https://github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/methods.c#L3299 | https://github.com/numpy/numpy/blob/master/numpy/core/src/umath/divmod.c.src#L2964 - Dexty
3
虽然np.mean是Python,但大部分代码都是设置代码。 _methods._mean怎么样? np.sum有一个设置,最终委托给wrap add/sum。 调用层可能是处理小数组的评估时间的重要组成部分,但在处理大数组时变得可以忽略不计。 - hpaulj
1
我可以很容易地从文档或 ipython ?? 中找到 np.mean 的源代码。_mean 需要稍微多一点搜索。看起来核心、大小相关的计算是用 np.add.reduce 完成的。我相信 x.sum() 也使用了 add.reduce,只是有一个小的函数调用时间延迟。np.sum 有一个更大的分派延迟。 - hpaulj
1
如果您认为代码很重要,您可以编辑您的答案以包含代码。 - hpaulj
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接