NumPy 陣列維度、形狀與軸


在〈陣列程式設計〉中可以看到,NumPy 的陣列可以是一維、二維,其實也可以是三維以上的陣列。

先來看看一維與二維好了,想要知道陣列的維度,可以透過陣列的 ndim 屬性得知。

>>> import numpy as np
>>> a = np.array([1, 2, 3, 4, 5, 6])
>>> b = np.array([[1, 2], [3, 4], [5, 6]])
>>> a.ndim
1
>>> b.ndim
2
>>>

如果想知道陣列的形狀(Shape),可以透過 shape 屬性,例如:

>>> a.shape
(6,)
>>> b.shape
(3, 2)
>>>

shape 傳回 tuple,元素數量表示它的維度,元素數值表示每一維的長度,就上例來看,(6,) 表示 a 是個一維陣列,長度為 6,(3, 2) 表示 b 是個二維陣列,各維度長度分別是 3 與 2,也就是 3 列(row) 2 行(column)。

NumPy 的陣列有軸(axis)的概念,在計算時可以指定要沿哪個軸進行,就一維陣列來說只有一個軸,也就是 axis 0,就二維陣列的話,axis 0 對應行,axis 1 對應列,可以看到,陣列的 shape 傳回的 tuple,元素值就是依軸的順序:

NumPy 陣列維度、形狀與軸

NumPy 有個 apply_along_axis,可以指定軸來進行運算,例如,想將上例 b 陣列,對每一列加總,第一個參數可以指定函式,第二個參數指定軸,第三個參數指定陣列,例如:

>>> np.apply_along_axis(sum, 1, b)
array([ 3,  7, 11])
>>>

如果要對每一行加總,就是指定第二個參數為 0:

>>> np.apply_along_axis(sum, 0, b)
array([ 9, 12])
>>>

那麼三維陣列呢?ndim 當然就是 3,至於 shape、軸的話,可以先看看下圖:

NumPy 陣列維度、形狀與軸

這張圖對應以下建立的陣列:

>>> c = np.array([
...     [[1, 2], [3, 4], [5, 6]],
...     [[7, 8], [9, 10], [11, 12]],
...     [[13, 14], [15, 16], [17, 18]]
... ])
>>> c.ndim
3
>>> c.shape
(3, 3, 2)
>>>

看到了嗎?軸的順序其實就是撰寫時每一層的順序,因為第一層有三個元素,第二層有三個元素,第三層有兩個元素,shape 傳回的就是 (3, 3, 2)

那麼沿三個軸分別計算的結果會是?例如沿著 axis 0?

>>> np.apply_along_axis(sum, 0, c)
array([[21, 24],
       [27, 30],
       [33, 36]])

沿著 axis 0 計算比較好懂,就是將上圖的三張二維陣列用 sum 運算,sum 就是用 +,最後得到的結果不難理解,沿著 axis 1 呢?

>>> np.apply_along_axis(sum, 1, c)
array([[ 9, 12],
       [27, 30],
       [45, 48]])

看看下圖就會理解了,將同色看成一個二維陣列,由上層往下層算就對了,另兩軸的軸數小的是行,軸數大的是列:

NumPy 陣列維度、形狀與軸

沿著 axis 2 的話,就會是:

NumPy 陣列維度、形狀與軸

對照上圖算一下,記得另兩軸的軸數小的是行,軸數大的是列,看看你算的結果對不對:

>>> np.apply_along_axis(sum, 2, c)
array([[ 3,  7, 11],
       [15, 19, 23],
       [27, 31, 35]])
>>>

更高維度應該也可以算,只不過更高維度就習慣在三維空間的人類來說,會更難想像與掌握,不建議就是了;另外,apply_along_axis 只是個簡便的函式,它的底層是逐一取出每個元素來套用指定的純 Python 函式,速度並不快,若要提昇速度,必須分解任務、設計出更適合的 NumPy 陣列以進行運算。