我正处于学习NumPy的开始阶段。我有一个3x3矩阵的Numpy数组。我想创建一个新的数组,其中每个矩阵旋转90度。我已经研究了answer,但我仍然无法弄清楚我做错了什么。
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])
答案 0 :(得分:5)
无需单独进行轮换:numpy
具有内置numpy.rot90(m, k=1, axes=(0, 1))
功能。默认情况下,矩阵在第一维和第二维上旋转。
如果你想要更深一级旋转,你只需设置旋转发生的轴,更深一层(如果你想在不同的方向旋转,也可以选择交换它们)。或者如文档所述:
axes: (2,) array_like
阵列在由。定义的平面中旋转 轴。轴必须不同。
所以我们旋转 y 和 z 平面(如果我们标注尺寸 x , y 和 z )因此我们指定(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
。
答案 1 :(得分:1)
通常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]])
将(x,y)
标量元组提供给lambda,用于针对(2,1)数组广播的(3,)数组的所有组合,从而得到(2,3)数组。它可以被视为map
的广播扩展。
np.vectorize(np.rot90)
的问题是rot90
需要一个二维数组,但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
描述为2d数组的数组,但最好将其视为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元素进行动作。
vectorize
与signature
:
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
方法对于较大的数组会做得更好,但我怀疑在这种情况下。
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
更快。)
我怀疑vectorize
signature
正在做类似的事情:
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数组,就像其他代码一样。
frompyfunc
是vectorize
的基础函数,并返回一个对象数组。在这里,我创建了一个类似a
的数组,除了它是1d,包含多个m
数组。它是一个数组数组,而不是3d数组。