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
類似,可以改變矩陣的形狀,但它有幾點不同,
- 沒有
order
引數了,它只有跟reshape
裡面order='C'
的方式。 - 假如要轉換成的矩陣形狀中的元素數量跟原矩陣不同,它會強制進行轉換,而不報錯。
我們具體來看一下第二點
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]])