繁体   English   中英

使用numpy.vectorize()旋转NumPy数组的所有元素

[英]Using numpy.vectorize() to rotate all elements of a NumPy array

我正处于学习NumPy的开始阶段。 我有一个3x3矩阵的Numpy数组。 我想创建一个新的数组,其中每个矩阵旋转90度。 我已经研究了这个答案,但我仍然无法弄清楚我做错了什么。

import numpy as np

# 3x3
m = np.array([[1,2,3], [4,5,6], [7,8,9]])

# array of 3x3
a = np.array([m,m,m,m])

# rotate a single matrix counter-clockwise
def rotate90(x):
    return np.rot90(x)

# function that can be called on all elements of an np.array
# Note: I've tried different values for otypes= without success
f = np.vectorize(rotate90)

result = f(a)
# ValueError: Axes=(0, 1) out of range for array of ndim=0.
# The error occurs in NumPy's rot90() function.

注意:我意识到我可以执行以下操作,但我想了解矢量化选项。

t = np.array([ np.rot90(x, k=-1) for x in a])

无需单独进行旋转: numpy具有内置numpy.rot90(m, k=1, axes=(0, 1))函数。 默认情况下,矩阵在第一维和第二维上旋转。

如果你想要更深一级旋转,你只需设置旋转发生的轴,更深一层(如果你想在不同的方向旋转,可选择交换它们)。 或者如文档所述:

axes: (2,) array_like

阵列在由轴定义的平面中旋转。 轴必须不同。

所以我们在yz平面上旋转(如果我们标注尺寸xyz ),那么我们指定(2,1)(1,2)

当您想要向右/向左旋转时,您所要做的就是正确设置axes

np.rot90(a,axes=(2,1)) # right
np.rot90(a,axes=(1,2)) # left

这将旋转所有矩阵,如:

>>> np.rot90(a,axes=(2,1))
array([[[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]],

       [[7, 4, 1],
        [8, 5, 2],
        [9, 6, 3]]])

或者如果你想向左旋转

>>> np.rot90(a,axes=(1,2))
array([[[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]],

       [[3, 6, 9],
        [2, 5, 8],
        [1, 4, 7]]])

请注意,您只能从numpy 1.12和(可能)未来版本中指定axes

通常, np.vectorize用于将标量(Python,非numpy)函数应用于数组或数组集的所有元素。 有一个经常被忽视的笔记:

提供vectorize功能主要是为了方便,而不是为了提高性能。 实现基本上是for循环。

In [278]: m = np.array([[1,2,3],[4,5,6]])
In [279]: np.vectorize(lambda x:2*x)(m)
Out[279]: 
array([[ 2,  4,  6],
       [ 8, 10, 12]])

这将m的每个元素乘以2,为我们处理循环的文书工作。

更好的是,当给出几个阵列时,它会广播(“外部产品”的概括)。

In [280]: np.vectorize(lambda x,y:2*x+y)(np.arange(3), np.arange(2)[:,None])
Out[280]: 
array([[0, 2, 4],
       [1, 3, 5]])

对于(2,1)数组广播的(3,)数组的所有组合,这将(x,y)标量元组提供给lambda,得到(2,3)数组。 它可以被视为map的广播扩展。

np.vectorize(np.rot90)的问题是rot90需要一个2d数组,但是vectorize会为它提供标量。

但是我在文档中看到,对于v1.12他们添加了一个签名参数。 这是我第一次使用它。

您的问题 - 将np.rot90应用于3d数组的2d元素:

In [266]: m = np.array([[1,2,3],[4,5,6]])
In [267]: a = np.stack([m,m])
In [268]: a
Out[268]: 
array([[[1, 2, 3],
        [4, 5, 6]],

       [[1, 2, 3],
        [4, 5, 6]]])

虽然你可以形容这a为二维数组的数组,最好是把它当作一个整数3D阵列。 这就是np.vectorize(myfun)(a)看到它的方式,给myfun每个数字。

适用于2d m

In [269]: np.rot90(m)
Out[269]: 
array([[3, 6],
       [2, 5],
       [1, 4]])

使用Python工作马,列表理解:

In [270]: [np.rot90(i) for i in a]
Out[270]: 
[array([[3, 6],
        [2, 5],
        [1, 4]]), array([[3, 6],
        [2, 5],
        [1, 4]])]

结果是一个列表,但我们可以将其包装在np.array

Python map做同样的事情。

In [271]: list(map(np.rot90, a))
Out[271]: 
[array([[3, 6],
        [2, 5],
        [1, 4]]), array([[3, 6],
        [2, 5],
        [1, 4]])]

理解和映射都在a的第一维上迭代,对结果的2d元素进行动作。

使用signature vectorize

In [272]: f = np.vectorize(np.rot90, signature='(n,m)->(k,l)')
In [273]: f(a)
Out[273]: 
array([[[3, 6],
        [2, 5],
        [1, 4]],

       [[3, 6],
        [2, 5],
        [1, 4]]])

signature告诉它传递2d数组并期望返回2d数组。 (我应该探讨signature如何与otypes参数一起播放。)

一些快速时间比较:

In [287]: timeit np.array([np.rot90(i) for i in a])
10000 loops, best of 3: 40 µs per loop
In [288]: timeit np.array(list(map(np.rot90, a)))
10000 loops, best of 3: 41.1 µs per loop
In [289]: timeit np.vectorize(np.rot90, signature='(n,m)->(k,l)')(a)
1000 loops, best of 3: 234 µs per loop
In [290]: %%timeit f=np.vectorize(np.rot90, signature='(n,m)->(k,l)')
     ...: f(a)
     ...: 
1000 loops, best of 3: 196 µs per loop

因此对于一个小数组,Python列表方法更快,相当多。 有时,对于较大的阵列, numpy方法会做得更好,但我怀疑在这种情况下。

使用axes参数的rot90甚至更好,并且适用于更大的数组:

In [292]: timeit np.rot90(a,axes=(1,2))
100000 loops, best of 3: 15.7 µs per loop

看一下np.rot90代码,我看到它只是做np.flip (反向)和np.transpose ,各种组合取决于k 对于这种情况,它实际上是这样做的:

In [295]: a.transpose(0,2,1)[:,::-1,:]
Out[295]: 
array([[[3, 6],
        [2, 5],
        [1, 4]],

       [[3, 6],
        [2, 5],
        [1, 4]]])

(这比rot90更快。)


我怀疑带有signature vectorize是这样的:

In [301]: b = np.zeros(2,dtype=object)
In [302]: b[...] = [m,m]
In [303]: f = np.frompyfunc(np.rot90, 1,1)
In [304]: f(b)
Out[304]: 
array([array([[3, 6],
       [2, 5],
       [1, 4]]),
       array([[3, 6],
       [2, 5],
       [1, 4]])], dtype=object)

np.stack(f(b))会将对象数组转换为3d数组,就像其他代码一样。

frompyfuncvectorize的基础函数,并返回一个对象数组。 在这里,我创建喜欢你的阵列a除了它是1d中,包含多个m阵列。 它是一个数组数组,而不是3d数组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM