[python] 切片NumPy 2d數組,或者如何從nxn數組(n> m)中提取mxm子矩陣?


Answers

要回答這個問題,我們必須看看如何索引一個多維數組在Numpy中的工作。 我們首先說你有你的問題的數組x 。 分配給x的緩衝區將包含16個從0到15的遞增整數。如果訪問一個元素,比如說x[i,j] ,NumPy必須計算出該元素相對於緩衝區開始的內存位置。 這是通過實際計算i*x.shape[1]+j (並與int的大小相乘以獲得實際的內存偏移量)來完成的。

如果通過像y = x[0:2,0:2]這樣的基本分片來提取子數組,則所得到的對象將與x共享底層緩衝區。 但是如果你訪問y[i,j]會發生什麼? NumPy不能使用i*y.shape[1]+j來計算數組中的偏移量,因為屬於y的數據在內存中不是連續的。

NumPy通過引入大步解決了這個問題。 當計算訪問x[i,j]的內存偏移量時,實際計算的是i*x.strides[0]+j*x.strides[1] (並且這已經包含了int大小的因子) :

x.strides
(16, 4)

當像上面那樣提取y ,NumPy不會創建新的緩衝區,但它確實會創建一個引用相同緩衝區的新數組對象(否則y只會等於x )。新的數組對象將具有不同的形狀,然後x和也許一個不同的起始偏移到緩衝區中,但會與x共享大步(在這種情況下至少):

y.shape
(2,2)
y.strides
(16, 4)

這樣,計算y[i,j]的存儲器偏移將產生正確的結果。

但是NumPy應該為z=x[[1,3]]做些什麼呢? 如果原始緩衝區用於z則strides機制將不允許正確的索引。 理論上NumPy 可以添加一些比步幅更複雜的機制,但是這會使得元素訪問相對昂貴,從某種程度上違背了數組的整個想法。 另外,視圖不再是一個真正的輕量級對象。

這在編制索引的NumPy文檔中有深入的介紹

哦,幾乎忘了你的實際問題:下面是如何使索引與多個列表按預期工作:

x[[[1],[3]],[1,3]]

這是因為索引數組被broadcasted到一個普通的形狀。 當然,對於這個特殊的例子,你也可以使用基本的切片:

x[1::2, 1::2]
Question

我想分割一個NumPy nxn數組。 我想提取該數組的m行和列的任意選擇(即在行數/列數中沒有任何模式),使其成為一個新的mxm數組。 對於這個例子,讓我們說數組是4x4,我想從中提取一個2x2數組。

這是我們的陣列:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

要刪除的行和列是相同的。 最簡單的情況是當我想提取一個開頭或結尾的2x2子矩陣時,即:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

但是如果我需要刪除另一行/列的混合呢? 如果我需要刪除第一行和第三行,從而提取子矩陣[[5,7],[13,15]] ? 可以有任何行/行的組合。 我讀了一些地方,我只需要索引我的數組使用數組/行列索引列表,但似乎並沒有工作:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

我找到了一種方法,它是:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

與此相關的第一個問題是它很難讀,儘管我可以忍受這一點。 如果有人有更好的解決方案,我當然想听到它。

其他的事情是我讀了一個論壇 ,索引陣列數組強制NumPy複製所需的數組,因此,當處理大型數組時,這可能會成為一個問題。 為什麼這個機制如此/如何運作?




如果你想跳過每一行和其他列,那麼你可以使用基本切片來完成:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

這將返回一個視圖,而不是數組的副本。

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

z=x[(1,3),:][:,(1,3)]使用高級索引並返回一個副本:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

請注意x不變:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

如果你想選擇任意的行和列,那麼你不能使用基本的切片。 您必須使用高級索引,使用像x[rows,:][:,columns] ,其中rowscolumns是序列。 這當然會給你一個原始數組的副本,而不是視圖。 這是人們應該期待的,因為numpy數組使用連續內存(具有常量步長),並且無法生成任意行和列的視圖(因為這需要非常量步長)。




我在這裡有一個類似的問題: 以最蟒蛇的方式寫在ndarray的副劇目中。 Python 2

根據您的案例的上一篇文章的解決方案,解決方案如下所示:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

使用ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

這是:

array([[ 5,  7],
       [13, 15]])



Links