簡體   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