映射一个NumPy数组
是否可以映射一个NumPy数组? 如果是的话,怎么样?
给定a_values
– 二维数组 – 这是目前为我所用的技巧:
for row in range(len(a_values)): for col in range(len(a_values[0])): a_values[row][col] = dim(a_values[row][col])
但是,如果我怀疑在NumPy中的某个地方必须有一个function与以下类似的function一样:
a_values.map_in_place(dim)
但如果像上面这样的东西存在,我一直无法find它。
如果你处于严重的空间限制之下,那么只有在这样做的时候才值得。 如果是这样的话,可以通过遍历数组的平坦视图来加快代码的速度。 由于reshape
在可能的情况下返回新的视图,数据本身不被复制(除非原始结构具有不寻常的结构)。
我不知道有一个更好的方法来实现一个任意Python函数的真正就地应用程序。
>>> def flat_for(a, f): ... a = a.reshape(-1) ... for i, v in enumerate(a): ... a[i] = f(v) ... >>> a = numpy.arange(25).reshape(5, 5) >>> flat_for(a, lambda x: x + 5) >>> a array([[ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24], [25, 26, 27, 28, 29]])
一些时间:
>>> a = numpy.arange(2500).reshape(50, 50) >>> f = lambda x: x + 5 >>> %timeit flat_for(a, f) 1000 loops, best of 3: 1.86 ms per loop
这是嵌套循环版本的两倍:
>>> a = numpy.arange(2500).reshape(50, 50) >>> def nested_for(a, f): ... for i in range(len(a)): ... for j in range(len(a[0])): ... a[i][j] = f(a[i][j]) ... >>> %timeit nested_for(a, f) 100 loops, best of 3: 3.79 ms per loop
当然,vector化的速度还是比较快的,所以如果你能做一个副本的话,可以这样使用:
>>> a = numpy.arange(2500).reshape(50, 50) >>> g = numpy.vectorize(lambda x: x + 5) >>> %timeit g(a) 1000 loops, best of 3: 584 us per loop
如果你可以使用内置的ufuncs重写dim
,那么请不要vectorize
:
>>> a = numpy.arange(2500).reshape(50, 50) >>> %timeit a + 5 100000 loops, best of 3: 4.66 us per loop
numpy
操作就像你所期望的那样,就像+=
一样,所以你可以免费获得一个ufunc的速度。 有时甚至更快! 看这里的例子。
顺便说一下,我对这个问题的原始答案,可以在编辑历史中看到,是荒谬的,并涉及向索引vector化到a
。 它不仅需要做一些时髦的东西来绕过vectorize
的types检测机制 ,事实certificate,它和嵌套循环版本一样慢。 非常聪明!
这是在答复和评论中散布的贡献的写作,我接受了这个问题的答案后写的。 Upvotes总是受欢迎的,但是如果你喜欢这个答案,请不要错过给上传者和他(如果他写的) eryksun ,谁提出了下面的方法。
问:是否可以映射一个numpy数组?
答:可以, 但不能用单个数组方法 。 你必须编写自己的代码。
在比较线程中讨论的各种实现的脚本下面:
import timeit from numpy import array, arange, vectorize, rint # SETUP get_array = lambda side : arange(side**2).reshape(side, side) * 30 dim = lambda x : int(round(x * 0.67328)) # TIMER def best(fname, reps, side): global a a = get_array(side) t = timeit.Timer('%s(a)' % fname, setup='from __main__ import %s, a' % fname) return min(t.repeat(reps, 3)) #low num as in place --> converge to 1 # FUNCTIONS def mac(array_): for row in range(len(array_)): for col in range(len(array_[0])): array_[row][col] = dim(array_[row][col]) def mac_two(array_): li = range(len(array_[0])) for row in range(len(array_)): for col in li: array_[row][col] = int(round(array_[row][col] * 0.67328)) def mac_three(array_): for i, row in enumerate(array_): array_[i][:] = [int(round(v * 0.67328)) for v in row] def senderle(array_): array_ = array_.reshape(-1) for i, v in enumerate(array_): array_[i] = dim(v) def eryksun(array_): array_[:] = vectorize(dim)(array_) def ufunc_ed(array_): multiplied = array_ * 0.67328 array_[:] = rint(multiplied) # MAIN r = [] for fname in ('mac', 'mac_two', 'mac_three', 'senderle', 'eryksun', 'ufunc_ed'): print('\nTesting `%s`...' % fname) r.append(best(fname, reps=50, side=50)) # The following is for visually checking the functions returns same results tmp = get_array(3) eval('%s(tmp)' % fname) print tmp tmp = min(r)/100 print('\n===== ...AND THE WINNER IS... =========================') print(' mac (as in question) : %.4fms [%.0f%%]') % (r[0]*1000,r[0]/tmp) print(' mac (optimised) : %.4fms [%.0f%%]') % (r[1]*1000,r[1]/tmp) print(' mac (slice-assignment) : %.4fms [%.0f%%]') % (r[2]*1000,r[2]/tmp) print(' senderle : %.4fms [%.0f%%]') % (r[3]*1000,r[3]/tmp) print(' eryksun : %.4fms [%.0f%%]') % (r[4]*1000,r[4]/tmp) print(' slice-assignment w/ ufunc : %.4fms [%.0f%%]') % (r[5]*1000,r[5]/tmp) print('=======================================================\n')
上面的脚本的输出 – 至less在我的系统中 – 是:
mac (as in question) : 88.7411ms [74591%] mac (optimised) : 86.4639ms [72677%] mac (slice-assignment) : 79.8671ms [67132%] senderle : 85.4590ms [71832%] eryksun : 13.8662ms [11655%] slice-assignment w/ ufunc : 0.1190ms [100%]
正如你所看到的, 使用numpy的ufunc
速度比第二好的和最坏的select分别增加了2倍以上,几乎3个数量级 。
如果使用ufunc
不是一个选项,这里只是其他select的比较:
mac (as in question) : 91.5761ms [672%] mac (optimised) : 88.9449ms [653%] mac (slice-assignment) : 80.1032ms [588%] senderle : 86.3919ms [634%] eryksun : 13.6259ms [100%]
HTH!
为什么不使用numpy实现,out_技巧?
from numpy import array, arange, vectorize, rint, multiply, round as np_round def fmilo(array_): np_round(multiply(array_ ,0.67328, array_), out=array_)
得到:
===== ...AND THE WINNER IS... ========================= mac (as in question) : 80.8470ms [130422%] mac (optimised) : 80.2400ms [129443%] mac (slice-assignment) : 75.5181ms [121825%] senderle : 78.9380ms [127342%] eryksun : 11.0800ms [17874%] slice-assignment w/ ufunc : 0.0899ms [145%] fmilo : 0.0620ms [100%] =======================================================
如果ufuncs不可能,你应该考虑使用cython。 很容易整合并且大大加快numpy数组的使用。
这只是mac的一个更新版本,为Python 3.x实现,并添加了numba和numpy.frompyfunc 。
numpy.frompyfunc需要一个不规则的python函数,并返回一个函数,当在numpy.array上投射时,函数以元素的forms应用。
但是,它将数组的数据types更改为对象,所以它不在位,将来在这个数组上的计算将会变慢。
为了避免这个缺点,在testingnumpy.ndarray.astype将被调用,返回数据types为int。
如旁注:
Numba不包含在Python的基本库中,如果你想testing,必须从外部下载。 在这个testing中,它实际上什么也不做,如果它被@jit(nopython = True)调用,它会给出一个错误消息,说它不能优化任何东西。 但是,由于numba通常可以加速以function方式编写的代码,因此完整性也包含在内。
import timeit from numpy import array, arange, vectorize, rint, frompyfunc from numba import autojit # SETUP get_array = lambda side : arange(side**2).reshape(side, side) * 30 dim = lambda x : int(round(x * 0.67328)) # TIMER def best(fname, reps, side): global a a = get_array(side) t = timeit.Timer('%s(a)' % fname, setup='from __main__ import %s, a' % fname) return min(t.repeat(reps, 3)) #low num as in place --> converge to 1 # FUNCTIONS def mac(array_): for row in range(len(array_)): for col in range(len(array_[0])): array_[row][col] = dim(array_[row][col]) def mac_two(array_): li = range(len(array_[0])) for row in range(len(array_)): for col in li: array_[row][col] = int(round(array_[row][col] * 0.67328)) def mac_three(array_): for i, row in enumerate(array_): array_[i][:] = [int(round(v * 0.67328)) for v in row] def senderle(array_): array_ = array_.reshape(-1) for i, v in enumerate(array_): array_[i] = dim(v) def eryksun(array_): array_[:] = vectorize(dim)(array_) @autojit def numba(array_): for row in range(len(array_)): for col in range(len(array_[0])): array_[row][col] = dim(array_[row][col]) def ufunc_ed(array_): multiplied = array_ * 0.67328 array_[:] = rint(multiplied) def ufunc_frompyfunc(array_): udim = frompyfunc(dim,1,1) array_ = udim(array_) array_.astype("int") # MAIN r = [] totest = ('mac', 'mac_two', 'mac_three', 'senderle', 'eryksun', 'numba','ufunc_ed','ufunc_frompyfunc') for fname in totest: print('\nTesting `%s`...' % fname) r.append(best(fname, reps=50, side=50)) # The following is for visually checking the functions returns same results tmp = get_array(3) eval('%s(tmp)' % fname) print (tmp) tmp = min(r)/100 results = list(zip(totest,r)) results.sort(key=lambda x: x[1]) print('\n===== ...AND THE WINNER IS... =========================') for name,time in results: Out = '{:<34}: {:8.4f}ms [{:5.0f}%]'.format(name,time*1000,time/tmp) print(Out) print('=======================================================\n')
最后,结果:
===== ...AND THE WINNER IS... ========================= ufunc_ed : 0.3205ms [ 100%] ufunc_frompyfunc : 3.8280ms [ 1194%] eryksun : 3.8989ms [ 1217%] mac_three : 21.4538ms [ 6694%] senderle : 22.6421ms [ 7065%] mac_two : 24.6230ms [ 7683%] mac : 26.1463ms [ 8158%] numba : 27.5041ms [ 8582%] =======================================================