2012-08-13 107 views
6

我正在使用多維Numpy數組。使用其他索引數組訪問這些數組時,我注意到了一些不一致的行爲。例如:Numpy多維數組索引交換軸順序

import numpy as np 
start = np.zeros((7,5,3)) 
a  = start[:,:,np.arange(2)] 
b  = start[0,:,np.arange(2)] 
c  = start[0,:,:2] 
print 'a:', a.shape 
print 'b:', b.shape 
print 'c:', c.shape 

在這個例子中,我得到的結果是:

a: (7, 5, 2) 
b: (2, 5) 
c: (5, 2) 

這混淆了我。爲什麼「b」和「c」不具有相同的尺寸?爲什麼「b」交換軸順序,而不是「a」?

我已經能夠設計這些歸功於大量的單元測試的不一致在我的代碼,但瞭解正在發生的事情,將不勝感激。

僅供參考,我使用Python 2.7.3和Numpy 1.6.2通過MacPorts。

回答

8

在語法上,這看起來像一個矛盾,但語義,你在這裏做了兩個非常不同的事情。在您定義的ab中,您正在做advanced indexing,有時稱爲fancy indexing,它返回數據的副本。在c的定義中,您正在執行basic slicing,它返回數據的視圖。

爲了區分它,有助於理解索引如何傳遞給python對象。下面是一些例子:

>>> class ShowIndex(object): 
...  def __getitem__(self, index): 
...   print index 
... 
>>> ShowIndex()[:,:] 
(slice(None, None, None), slice(None, None, None)) 
>>> ShowIndex()[...,:] 
(Ellipsis, slice(None, None, None)) 
>>> ShowIndex()[0:5:2,::-1] 
(slice(0, 5, 2), slice(None, None, -1)) 
>>> ShowIndex()[0:5:2,np.arange(3)] 
(slice(0, 5, 2), array([0, 1, 2])) 
>>> ShowIndex()[0:5:2] 
slice(0, 5, 2) 
>>> ShowIndex()[5, 5] 
(5, 5) 
>>> ShowIndex()[5] 
5 
>>> ShowIndex()[np.arange(3)] 
[0 1 2] 

正如你可以看到,有很多不同可能的配置。首先,可以傳遞單個項目,或者可以傳遞項目的元組。其次,元組可以包含slice對象,Ellipsis對象,普通整數或numpy數組。當傳遞對象像intslice,或Ellipsis對象或None(這是一樣的numpy.newaxis

基本切片被激活。這些可以單獨或以元組形式傳遞。下面介紹一下文檔不得不說一下如何基本切片被激活:

當obj是切片對象發生

基本切片(由開始建造:停止:括號內的步驟符號),整數,或元組切片對象和整數。省略號和newaxis對象也可以與這些散佈在一起。爲了保持向後兼容的數字共同使用,基本切片也發起如果選擇對象是任何序列(如列表)含有切片對象,省略號對象,或newaxis對象,但沒有整數陣列或其它嵌入序列。當你經過一個numpy陣列,僅包含整數或任何種類的含子序列,或含有一個陣列或子序列中的元組的非元組序列

高級索引被激活。

有關如何先進的索引和基本切片不同,請參閱該文檔的細節(與上文)。但在這種特殊情況下,我很清楚發生了什麼事情。它與使用部分索引時的以下行爲有關:

部分索引的規則是結果的形狀(或設置中要使用的對象的解釋形狀)是x將索引子空間替換爲廣播索引子空間。如果索引子空間彼此相鄰,則廣播索引空間直接替換x中的所有索引子空間。如果索引子空間是分開的(按切片對象),則廣播的索引空間是第一個,後面是x的切片子空間。

在你的a定義,它採用先進的索引,您可以有效地傳遞順序[0, 1]中的元組的第三個項目,因爲沒有廣播發生(因爲沒有其他序列),一切都發生如預期。

在你的b定義,也採用了先進的索引,則有效地傳遞序列,[0],第一項(其被轉換成intp陣列),和[0, 1],第三項。這兩個項目一起播放,結果與第三個項目具有相同的形狀。然而,自廣播發生以來,我們面臨着一個問題:我們在新形狀元組中插入廣播形狀的位置?正如文檔所說,

沒有明確的地方放置在索引子空間中,因此它被固定在開頭。

因此,廣播產生的2被移動到形狀元組的開頭,產生明顯的轉置。

+0

感謝您的詳細解釋。這非常有幫助。切片+廣播索引造成的奇怪行爲仍然令人意想不到,難以編碼。例如:start [0,:,np.arange(2)] = np.ones((5,2))似乎是合法的,但由於軸重新排序,它不是 – gbarter 2012-08-13 23:52:38

+0

@gbarter爲了保持原來的形狀,你必須使用切片。即,這將工作:'開始[:1,:,np.arange(2)] = np.ones((5,2))' – jorgeca 2012-08-14 10:11:58