NumPy 陣列重塑形狀和調整大小

Jinku Hu 2023年1月30日 2018年7月14日
  1. numpy.reshape()
  2. ndarray.reshape()
  3. reshape() 函式/方法記憶體
  4. numpy.resize()
NumPy 陣列重塑形狀和調整大小

NumPy 中有兩個跟形狀轉換相關的函式(及方法) reshape 以及 resize,它們都能方便的改變矩陣的形狀,但是它們之間又有一個顯著的差別,我們會著重的來講。

numpy.reshape()

我們先來看看會在各種資料計算中經常用到的改變陣列形狀的函式 reshape()

import numpy as np

arrayA = np.arange(8)
# arrayA = array([0, 1, 2, 3, 4, 5, 6, 7])

np.reshape(arrayA, (2, 4))
#array([[0, 1, 2, 3],
#       [4, 5, 6, 7]])

這裡它把一個有 8 個元素的向量,轉換成了形狀為 (4, 2) 的一個矩陣。因為轉換前後的元素數目一樣,所以能夠成功的進行轉換,假如前後數目不一樣的話,就會有錯誤 ValueError 報出。

In [1]: np.reshape(arrayA, (3, 4))
    ---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: cannot reshape array of size 8 into shape (3,4)

我們仔細看轉換後的資料,第一行是 arrayA 的前四個資料,第二行是 arrayA 的後四個資料,也就是它是按行來填充資料的,有些時候我們需要把資料填充的順序改成按列來填充,那我們需要改變函式中的另外一個輸入引數 order=

In [1]: np.reshape(arrayA, (2, 4), order='F')
Out[1]: array([[0, 2, 4, 6],
        	  [1, 3, 5, 7]])

order 預設的引數是 C,也就是按行填充,當引數變為 F 時,就變成按列填充。

說明
這裡用按行或者按列來填充,是為了便於理解,其實具體的不同是由於函式是按照類似 C 的索引順序還是按照類似 Fortan 的索引順序來讀取輸入矩陣的內容,具體可以參照 Numpy reshape 的官方說明文件

ndarray.reshape()

除了用 NumPy 的 reshape 函式外,你還可以用資料 ndarray 物件裡面的 reshape 方法來進行矩陣形狀變化。它的語法跟 numpy.reshape() 類似,區別在於不用輸入矩陣作為引數了。

In [1]: arrayB = arrayA.reshape((2, 4))
    
In [2]: arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [1]: arrayA
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7])    

可以看得出,用法跟 reshape 函式類似。這裡想強調的一點是 ndarray.reshape() 方法不會改變原矩陣的資料、形狀等,而只是返回一個新的矩陣。

reshape() 函式/方法記憶體

reshape 函式或者方法生成的新陣列和原始陣列是共用一個記憶體的,有點類似於 Python 裡面的 shallow copy,當你改變一個陣列的元素,另外一個陣列的元素也相應的改變了。

In [1]: arrayA = np.arange(8)
    	arrayB = arrayA.reshape((2, 4))
        arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [2]: arrayA[0] = 10
    	arrayA
Out[2]: array([10, 1, 2, 3, 4, 5, 6, 7]) 
In [3]: arrayB    
Out[3]:	array([[10, 1, 2, 3],
       		[4, 5, 6, 7]])    

numpy.resize()

numpy.resize()reshape 類似,可以改變矩陣的形狀,但它有幾點不同,

  1. 沒有 order 引數了,它只有跟 reshape 裡面 order='C'的方式。
  2. 假如要轉換成的矩陣形狀中的元素數量跟原矩陣不同,它會強制進行轉換,而不報錯。

我們具體來看一下第二點

In [1]: arrayA = np.arange(8)
        arrayB = np.resize(arrayA, (2, 4))
Out[1]: array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

這是尺寸大小正常情況,跟 reshape 的結果是一樣的。

In [1]: arrayC = np.resize(arrayA, (3, 4))
    	arrayC
Out[1]: array([[0, 1, 2, 3],
       [4, 5, 6, 7],
       [0, 1, 2, 3]])
In [2]: arrayD = np.resize(arrayA, (4, 4))
    	arrayD
Out[2]: array([[0, 1, 2, 3],
       [4, 5, 6, 7],
       [0, 1, 2, 3],
       [4, 5, 6, 7]])

當新形狀行數超出的話,它會開始重複填充原始矩陣的內容,實現形狀大小的自動調整而不報錯。

In [1]: arrayE = np.resize(arrayA, (2, 2))
		arrayE
Out[1]: array([[0, 1],
       [2, 3]])    
In [2]: np.resize(arrayA, (1,4))
Out[2]: array([[0, 1, 2, 3]])

當新形狀比原形狀所需要的資料小的時候,它會從原矩陣讀讀取出所需要個數的資料,然後按照先按行填充的方式來對新矩陣元素賦值。

注意
當新矩陣的形狀在原矩陣形狀內時,比如 (2, 2)(2, 4) 的情形,新矩陣的元素不是按照子集來獲取的。比如上例中,np.resize(arrayA, (2, 2)) 不是 array([[0, 1], [4, 5])。假如你需要這樣的擷取方法,我們會在後續的索引和切片操作中介紹到的。
In [1]: np.resize(arrayA, (3, 5))
Out[1]: array([[0, 1, 2, 3, 4],
       [5, 6, 7, 0, 1],
       [2, 3, 4, 5, 6]])    

當新形狀比原形狀要大時,它會先按行去填充舊矩陣的元素,並且在元素被用光後,再重複的填充這些元素,直到新矩陣的最後一元素。

resize 函式/方法記憶體

reshape 不一樣的是,resize 函式/方法生成的新陣列跟原陣列並不共用一個記憶體,所以彼此元素的改變不會影響到對方。

In [1]: arrayA = np.arange(8)
    	arrayB = arrayA.reshape((2, 4))
        arrayB
Out[2]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])
In [2]: arrayA[0] = 10
    	arrayA
Out[2]: array([10, 1, 2, 3, 4, 5, 6, 7]) 
In [3]: arrayB    
Out[3]:	array([[0, 1, 2, 3],
       		[4, 5, 6, 7]])    
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn