目錄
- 布爾索引
- 花式索引 (Fancy Indexing)
- 二者的聯(lián)系?
申明:本文中提到的數(shù)組就是特指numpy的數(shù)據(jù)結構ndarray,同理,一維數(shù)組或者N維數(shù)組,也是指一維活著N維ndarray。
參考資料:
(https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#indexing)
Python for Data Analysis 2nd Edition
布爾索引
我們可以通過一個布爾數(shù)組來索引目標數(shù)組,以此找出與布爾數(shù)組中值為True的對應的目標數(shù)組中的數(shù)據(jù)(后面通過實例可清晰的觀察)。需要注意的是,布爾數(shù)組的長度必須與目標數(shù)組對應的軸的長度一致。下面通過幾個例子來說明。
一維數(shù)組的索引
布爾數(shù)組中,下標為0,3,4的位置是True,因此將會取出目標數(shù)組中對應位置的元素。
In [24]: arr = np.arange(7)
In [25]: booling1 = np.array([True,False,False,True,True,False,False])
In [26]: arr[booling1]
Out[26]: array([0, 3, 4])
二維數(shù)組的索引
布爾數(shù)組中,下標為0,3,4的位置是True,因此將會取出目標數(shù)組中第0,3,4行。
In [27]: arr = np.arange(28).reshape((7,4))
In [28]: arr
Out[28]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23],
[24, 25, 26, 27]])
In [29]: booling1 = np.array([True,False,False,True,True,False,False])
In [30]: arr[booling1]
Out[30]:
array([[ 0, 1, 2, 3],
[12, 13, 14, 15],
[16, 17, 18, 19]])
我們還可以通過數(shù)組的邏輯運算來作為索引(實際上數(shù)組的邏輯運算的結果,也就是一個布爾數(shù)組)。假設我們有一個長度為7的字符串數(shù)組,然后對這個字符串數(shù)組進行邏輯運算,進而把元素的結果(布爾數(shù)組)作為索引的條件傳遞給目標數(shù)組(本質上,和上面那個例子是類似的)。例如,還是上面例子中的數(shù)組arr,現(xiàn)在就胡亂想象成每一行數(shù)據(jù)是一個人的一個月其中四天賺的錢。這7行中,可能有某些行屬于特定的人,那就想象成不同月份賺到的錢。
In [35]: names = np.array(['Ben','Tom','Ben','Jeremy','Jason','Michael','Ben'])
In [36]: names == 'Ben'
Out[36]: array([ True, False, True, False, False, False, True], dtype=bool)
# 找出Ben賺的錢的明細
In [37]: arr[names == 'Ben']
Out[37]:
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11],
[24, 25, 26, 27]])
# 在此基礎上,我們還可以添加常規(guī)的索引和切片操作
In [38]: arr[names == 'Ben',3]
Out[38]: array([ 3, 11, 27])
In [39]: arr[names == 'Ben',1:4]
Out[39]:
array([[ 1, 2, 3],
[ 9, 10, 11],
[25, 26, 27]])
上面的例子,通過邏輯運算把屬于Ben的明細找了出來。那如果我們想查找不屬于Ben的明細,則可以通過!=或者~運算。這種邏輯運算,還可以用來做數(shù)據(jù)清洗,后面會介紹到。
In [40]: arr[names!='Ben']
Out[40]:
array([[ 4, 5, 6, 7],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]])
In [41]: arr[~(names=='Ben')]
Out[41]:
array([[ 4, 5, 6, 7],
[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]])
多個邏輯運算的與和或也是支持的。例如,找出Tom或者Jason的明細,并且想對他們殘忍點,把他們的錢都清零。
In [44]: arr[(names == 'Jason') | (names == 'Tom')]
Out[44]:
array([[ 4, 5, 6, 7],
[16, 17, 18, 19]])
In [45]: arr[(names == 'Jason') | (names == 'Tom')] = 0
In [46]: arr
Out[46]:
array([[ 0, 1, 2, 3],
[ 0, 0, 0, 0],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[ 0, 0, 0, 0],
[20, 21, 22, 23],
[24, 25, 26, 27]])
除此之外,我們也可以目標數(shù)組上做邏輯運算。實際上,這種邏輯運算的到的結果是和目標數(shù)組維度和長度都一樣的布爾數(shù)組。例如,找出arr中大于15的元素,與上面的例子不一樣,這個例子返回的結果是一個一維數(shù)組。
In [51]: arr[arr>15]
Out[51]: array([16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27])
借上面的例子引申一下,假設現(xiàn)在想把上述數(shù)組中,小于或者等于15的數(shù)歸零,類似于數(shù)據(jù)清洗,那么可以通過下面的方式。
In [139]: arr
Out[139]:
array([[ 0, 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12, 13],
[14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27]])
In [140]: arr[arr<=15]=0
In [141]: arr
Out[141]:
array([[ 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27]])
花式索引 (Fancy Indexing)
花式索引是NumPy用來描述使用整型數(shù)組(這里的數(shù)組,可以是NumPy的數(shù)組,也可以是python自帶的list)作為索引的術語,其意義是根據(jù)索引數(shù)組的值作為目標數(shù)組的某個軸的下標來取值。對于使用一維整型數(shù)組作為索引,如果目標是一維數(shù)組,那么索引的結果就是對應位置的元素;如果目標是二維數(shù)組,那么就是對應下標的行。
In [69]: arr = np.array(['zero','one','two','three','four'])
In [70]: arr[[1,4]]
Out[70]:
array(['one', 'four'],
dtype='<U5')
In [71]: arr = np.empty((8,4),dtype=np.int)
In [72]: for i in range(8):
...: arr[i] = i
...:
In [73]: arr
Out[73]:
array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
[5, 5, 5, 5],
[6, 6, 6, 6],
[7, 7, 7, 7]])
In [75]: arr[[4,3,0,6]]
Out[75]:
array([[4, 4, 4, 4],
[3, 3, 3, 3],
[0, 0, 0, 0],
[6, 6, 6, 6]])
In [76]: arr[[-3,-5,-7]]
Out[76]:
array([[5, 5, 5, 5],
[3, 3, 3, 3],
[1, 1, 1, 1]])
對于使用兩個整型數(shù)組作為索引的時候,那么結果是按照順序取出對應軸的對應下標的值。特別注意,這兩個整型數(shù)組的shape應該一致,或者其中一個數(shù)組應該是長度為1的一維數(shù)組(與NumPy的Broadcasting機制于關系)。例如,以[1,3,5],[2,4,6]這兩個整型數(shù)組作為索引,那么對于二維數(shù)組,則取出(1,2),(3,4),(5,6)這些坐標對應的元素。
In [77]: arr = np.arange(42).reshape(6,7)
In [78]: arr
Out[78]:
array([[ 0, 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12, 13],
[14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34],
[35, 36, 37, 38, 39, 40, 41]])
In [79]: arr[[1,3,5],[2,4,6]]
Out[79]: array([ 9, 25, 41])
二者的聯(lián)系?
個人的猜想是,布爾索引和花式索引之間,是有聯(lián)系的。竊以為布爾索引是通過花式索引來實現(xiàn)的(雖然不100%確定,但是我個人感覺可以這么理解。各位請自行判斷)。為什么這么說?先來看看官方的文檔對布爾索引的說明:
Boolean array indexing
A single boolean index array is practically identical to x[obj.nonzero()]
上面的意思,不就是說x[obj]是等價x[obj.nonzero()](這里obj是一個布爾數(shù)組)。我們來驗證一下:
In [112]: arr = np.arange(12).reshape(3,4)
In [113]: i = np.array([True,False,True])
In [114]: i.nonzero()
Out[114]: (array([0, 2]),)
In [115]: arr[i]
Out[115]:
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
In [116]: arr[i.nonzero()]
Out[116]:
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
通過以上例子可知大概的關系:arr[i] <=> arr[i.nonzero()] => arr[(array([0,2]),)] => arr[array([0,2]),] => arr[[0,2]](到此為止就是花式索引了)。在來看一個復雜點的例子:
In [131]: i
Out[131]: array([ True, False, True], dtype=bool)
In [132]: j
Out[132]: array([ True, True, False, False], dtype=bool)
In [133]: i.nonzero()
Out[133]: (array([0, 2]),)
In [134]: j.nonzero()
Out[134]: (array([0, 1]),)
In [135]: arr[i,j]
Out[135]: array([0, 9])
# 這里不大清楚,為什么結果是一個二維數(shù)組
In [136]: arr[i.nonzero(),j.nonzero()]
Out[136]: array([[0, 9]])
In [137]: arr[[0,2],[0,1]]
Out[137]: array([0, 9])