for循环比numpy平均值快,也有点不同的结果

时间:2017-03-07 10:43:07

标签: python numpy

我正在计算系列的一部分和系列的滞后部分之间的平均斜率:

一种方法是:

def get_trend(series, slen)
    sum = 0.0
    for i in range(slen):
        sum += series[i+slen] - series[i]
    return sum / slen**2 
第二种方式是:

numpy.average(numpy.subtract(series[slen:2*slen], series[:slen]))/float(slen)

根据timeit,第一个代码比第二个代码快50%左右,并且结果在第18位和以后的数字上有所不同,一系列大小为200,其中slen = 66,而系列中的数字介于0和1之间。

我也尝试用sum取代平均值并除以slen ** 2,就像我在总和中所做的那样:

numpy.sum(numpy.subtract(series[slen:2*slen], series[:slen]))/float(slen**2)

这相当于for循环版本的执行时间,但结果仍然不完全相同,它也(有时)与平均版本不同,尽管通常它与平均版本相同版。

问题是: 哪一个应该给出最准确的答案? 为什么最后一个版本给出了for循环的不同答案? 为什么平均函数效率低下呢?

注意:对于时间我正在测量标准列表上的操作,在numpy数组上,平均值比for循环快,但总和仍然是平均值的两倍多。

2 个答案:

答案 0 :(得分:4)

更好的建议

我认为更好的矢量化方法是使用切片 -

(series[slen:2*slen] - series[:slen]).sum()/float(slen**2)

运行时测试和验证 -

In [139]: series = np.random.randint(11,999,(200))
     ...: slen= 66
     ...: 

# Original app
In [140]: %timeit get_trend(series, slen) 
100000 loops, best of 3: 17.1 µs per loop

# Proposed app
In [141]: %timeit (series[slen:2*slen] - series[:slen]).sum()/float(slen**2)
100000 loops, best of 3: 3.81 µs per loop

In [142]: out1 = get_trend(series, slen)

In [143]: out2 = (series[slen:2*slen] - series[:slen]).sum()/float(slen**2)

In [144]: out1, out2
Out[144]: (0.7587235996326905, 0.75872359963269054)

调查基于平均值的方法与循环方法的比较

让我们从测试问题中添加第二种方法(矢量化方法) -

In [146]: np.average(np.subtract(series[slen:2*slen], series[:slen]))/float(slen)
Out[146]: 0.75872359963269054

Timings比loopy更好,结果看起来很好。所以,我怀疑你的时机可能会被关闭。

如果您使用NumPy ufuncs来利用NumPy的矢量化操作,则应该使用数组。因此,如果您的数据是列表,请将其转换为数组,然后使用矢量化方法。让我们再研究一下 -

案例#1:列出200元素和slen = 66

In [147]: series_list = np.random.randint(11,999,(200)).tolist()

In [148]: series = np.asarray(series_list)

In [149]: slen = 66

In [150]: %timeit get_trend(series_list, slen)
100000 loops, best of 3: 5.68 µs per loop

In [151]: %timeit np.asarray(series_list)
100000 loops, best of 3: 7.99 µs per loop

In [152]: %timeit np.average(np.subtract(series[slen:2*slen], series[:slen]))/float(slen)
100000 loops, best of 3: 6.98 µs per loop

案例#2:将其缩放10x

In [157]: series_list = np.random.randint(11,999,(2000)).tolist()

In [159]: series = np.asarray(series_list)

In [160]: slen = 660

In [161]: %timeit get_trend(series_list, slen)
10000 loops, best of 3: 53.6 µs per loop

In [162]: %timeit np.asarray(series_list)
10000 loops, best of 3: 65.4 µs per loop

In [163]: %timeit np.average(np.subtract(series[slen:2*slen], series[:slen]))/float(slen)
100000 loops, best of 3: 8.71 µs per loop

所以,这是转换成一个伤害你的阵列的开销!

调查基于总和的方法与基于平均值的方法的比较

在将sum-based代码与average-based代码进行比较的第三部分中,因为np.avarege确实慢于"手动"使用summation执行此操作。同时对此进行定时 -

In [173]: a = np.random.randint(0,1000,(1000))

In [174]: %timeit np.sum(a)/float(len(a))
100000 loops, best of 3: 4.36 µs per loop

In [175]: %timeit np.average(a)
100000 loops, best of 3: 7.2 µs per loop

np.average更好的np.mean -

In [179]: %timeit np.mean(a)
100000 loops, best of 3: 6.46 µs per loop

现在,查看np.average的源代码,它似乎正在使用np.mean。这解释了为什么它比np.mean慢,因为我们避免了那里的函数调用开销。在np.sumnp.mean之间的争斗中,我认为np.mean确实会在我们添加大量元素的情况下处理溢出,我们可能会错过np.sum }。所以,为了安全起见,我认为与np.mean一起使用会更好。

答案 1 :(得分:0)

至于前两个问题......我不会说你有不同的结果! 1e-18的顺序差异很小(也可以在脚本的上下文中考虑)。 这就是为什么你不应该为了严格的相等而比较浮点数,而是设置一个容差:

What is the best way to compare floats for almost-equality in Python? https://www.python.org/dev/peps/pep-0485/