映射一个NumPy数组



是否可以映射NumPy数组?如果是,怎么做?

给定a_values - 2D数组-这是目前为我做的代码位:

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的某个地方一定有一个函数做同样的事情,看起来像:

a_values.map_in_place(dim)

但是如果上面的东西存在,我一直无法找到它

只有在空间非常有限的情况下才值得尝试就地执行此操作。如果是这种情况,可以通过迭代数组的平面化视图来加快代码的速度。由于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

当然矢量化仍然更快,所以如果你可以复制,使用它:

>>> 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在原地执行与+=类似的操作,正如您可能期望的那样——因此您可以免费获得原地应用程序的运行速度。有时甚至更快!请看这里的例子。


顺便说一下,我对这个问题的原始答案,可以在其编辑历史中查看,是荒谬的,并且涉及到将索引矢量化到a。它不仅要做一些古怪的事情来绕过vectorize的类型检测机制,而且结果证明它和嵌套循环版本一样慢。聪明到此为止!

这是一个分散在答案和贡献的撰写评论,我在接受问题的答案后写的。点赞总是受欢迎的,但如果你点赞这个答案,请不要错过为senderle和(如果)他写的那些投票1) eryksun,他建议以下方法

Q:有可能映射一个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')
以上脚本的输出—至少在我的系统中—是:
  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分别比第二好的和最差的替代方案提高了超过2和几乎3个数量级的速度

如果不能使用ufunc,这里只比较了其他选项:

  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实现。

添加numba和numpy.frompyfunc。

numpy.frompyfunc接受一个任意python函数并返回一个函数,该函数在numpy上强制转换时返回。数组,按元素方式应用函数。
但是,它将数组的数据类型更改为object,因此它不合适,并且将来对该数组的计算将会变慢。
为了避免这个缺点,在测试中将调用numpy. narray .astype,将数据类型返回为int。

旁注:
Numba不包含在Python的基本库中,如果想要测试它,必须从外部下载。在这个测试中,它实际上什么都不做,如果用@jit(nopython=True)调用它,它会给出一个错误消息,说它无法优化那里的任何东西。然而,由于numba通常可以加速以函数式风格编写的代码,因此为了完整性而包含它。

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%]
=======================================================

相关内容

  • 没有找到相关文章