我正在计算系列的一部分和系列的滞后部分之间的平均斜率:
一种方法是:
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循环快,但总和仍然是平均值的两倍多。
答案 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.sum
和np.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/