numpy之索引和切片


索引和切片

一維數組


一維數組很簡單,基本和列表一致。
它們的區別在於數組切片是原始數組視圖(這就意味着,如果做任何修改,原始都會跟着更改)。
這也意味着,如果不想更改原始數組,我們需要進行顯式的復制,從而得到它的副本(.copy())。


import numpy as np #導入numpy

arr = np.arange(10) #類似於list的range()
arr
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr[4] #索引(注意是從0開始的)
Out[4]: 4

arr[3:6] #切片
Out[6]: array([3, 4, 5])

arr_old = arr.copy() #先復制一個副本
arr_old
Out[8]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

arr[3:6] = 33

arr #可以發現將標量賦值給一個切片時,該值可以傳播到整個選區
Out[10]: array([ 0,  1,  2, 33, 33, 33,  6,  7,  8,  9])

arr_old
Out[11]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

二維數組

二維數組中,各索引位置上的元素不再是標量,而是一維數組(好像很難理解哈)。

arr1 = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])

arr1[0]
Out[13]: array([1, 2, 3])

arr1[1,2]
Out[14]: 6

好像很難理解,是吧。

那這樣看:

array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

想到了什么?咱們當做一個平面直角坐標系。
這樣看

相當於arr1[x,y], x相當於行數,y相當於列數(必須聲明,圖中x和y標反了,但不影響理解)。

多維數組

先說明下reshape()更改形狀:

np.reshape(a, newshape, order='C')

a :array_like以一個數組為參數。
newshape : int or tuple of ints。整數或者元組

順便說明下,np.reshape()不更改原數組形狀(會生成一個副本)。


arr1 = np.arange(12)
arr2 = arr1.reshape(2,2,3) #將arr1變為2×2×3數組

arr2
Out[9]: 
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])

其實多維數組就相當於:

row * col * 列中列

平面坐標系

那么:

arr2[0]
Out[10]: 
array([[0, 1, 2],
       [3, 4, 5]])

arr2[1]
Out[11]: 
array([[ 6,  7,  8],
       [ 9, 10, 11]])

arr2[0,1]
Out[12]: array([3, 4, 5])

arr2[0] = 23 #賦值
arr2
Out[15]: 
array([[[23, 23, 23],
        [23, 23, 23]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])

切片索引

那么這樣也就很容易的就可以理解下面這種索引了。

切片索引把每一行每一列當做一個列表就可以很容易的理解。
返回的都是數組。

再復雜一點:

我們想要獲得下面這個數組第一行的第2,3個數值。

arr1 = np.arange(36)#創建一個一維數組。

arr2 = arr1.reshape(6,6) #更改數組形狀。
Out[20]: 
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]])

為了得到第2,3個數,我們可以:


arr2[0,2:4]
Out[29]: array([2, 3])

可以發現ndarray的切片其實與列表的切片是差不太多的。

我們還可以這樣:


arr2[1] #取得第2行
Out[37]: array([ 6,  7,  8,  9, 10, 11])

arr2[:,3] #取得第3列, 只有:代表選取整列(也就是整個軸)
Out[38]: array([ 3,  9, 15, 21, 27, 33])

arr2[1:4,2:4] # 取得一個二維數組
Out[40]: 
array([[ 8,  9],
       [14, 15],
       [20, 21]])

arr2[::2,::2] #設置步長為2
Out[41]: 
array([[ 0,  2,  4],
       [12, 14, 16],
       [24, 26, 28]])

arr3 = arr2.reshape(4,3,3)


arr3[2:,:1] = 22 #對切片表達式賦值

arr3
Out[25]: 
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [22, 13, 14, 15, 16, 17],
       [22, 19, 20, 21, 22, 23],
       [22, 25, 26, 27, 28, 29],

布爾型索引

arr3 = (np.arange(36)).reshape(6,6)#生成6*6的數組
arr3
Out[35]: 
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]])

x = np.array([0, 1, 2, 1, 4, 5])

x == 1#通過比較運算得到一個布爾數組
Out[42]: array([False,  True, False,  True, False, False], dtype=bool)

arr3[x == 1] #布爾索引
Out[43]: 
array([[ 6,  7,  8,  9, 10, 11],
       [18, 19, 20, 21, 22, 23]])

從結果上看,布爾索引取出了布爾值為True的行。
布爾型數組的長度和索引的數組的行數(軸長度)必須一致。
布爾型數組可與切片,整數(整數序列)一起使用。

arr3[x == 1,2:]#切片
Out[44]: 
array([[ 8,  9, 10, 11],
       [20, 21, 22, 23]])

arr3[x == 1,-3:]#切片
Out[47]: 
array([[ 9, 10, 11],
       [21, 22, 23]])

arr3[x == 1,3]#整數
Out[48]: array([ 9, 21])

!= 不等於符號。
負號可以對條件進行否定。logical_not()函數也可以。

x != 1
Out[49]: array([ True, False,  True, False,  True,  True], dtype=bool)

arr3[~(x == 1)] #實際類似於取反
Out[51]: 
array([[ 0,  1,  2,  3,  4,  5],
       [12, 13, 14, 15, 16, 17],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

arr3[np.logical_not(x == 1)] #作用於 ~ 相同
Out[53]: 
array([[ 0,  1,  2,  3,  4,  5],
       [12, 13, 14, 15, 16, 17],
       [24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35]])

組合多個條件,使用布爾運算符&(和),|(或)

(x == 1 ) & (x == 4)#和
Out[67]: array([False, False, False, False, False, False], dtype=bool)

(x==1)|(x==4)#或
Out[68]: array([False,  True, False,  True,  True, False], dtype=bool)

arr3[(x==1)|(x==4)]#布爾索引
Out[71]: 
array([[ 6,  7,  8,  9, 10, 11],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29]])

通過以上的代碼實驗,我們也可以發現,布爾索引不更改原數組,創建的都是原數組的副本。

那這個東西能做什么呢?其他索引能做的,他基本也都可以。

比如有這樣一個數組:

arr5 = np.random.randn(4,4)#randn返回一個服從標准正態分布的數組。

arr5
Out[77]: 
array([[-0.64670829,  1.53428435,  0.20585387,  0.42680995],
       [-0.63504514,  0.54542881, -0.82163028, -0.89835051],
       [-0.66770299,  0.22617913,  0.16358189, -0.75074314],
       [-0.25439447, -0.96135628, -0.10552532, -1.06962358]])

我們要將arr5大於0的數值變為10:

arr5[arr5 > 0] = 10

arr5
Out[80]: 
array([[ -0.64670829,  10.        ,  10.        ,  10.        ],
       [ -0.63504514,  10.        ,  -0.82163028,  -0.89835051],
       [ -0.66770299,  10.        ,  10.        ,  -0.75074314],

當然,布爾索引也可以結合上面的運算符來進行操作。

花式索引

花式索引(Fancy indexing),指的是利用整數數組進行索引。

第一次看到這個解釋,我是一臉懵的。

試驗后,我才理解。


arr6 = np.empty((8,4))# 創建新數組,只分配內存空間,不填充值

for i in range(8):#給每一行賦值
    arr6[i] = i
    
arr6
Out[5]: 
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.]])

arr6[[2,6,1,7]] #花式索引
Out[14]: 
array([[ 2.,  2.,  2.,  2.],
       [ 6.,  6.,  6.,  6.],
       [ 1.,  1.,  1.,  1.],
       [ 7.,  7.,  7.,  7.]])

我們可以看到花式索引的結果,以一個特定的順序排列。
而這個順序,就是我們所傳入的整數列表或者ndarray。
這也為我們以特定的順序來選取數組子集,提供了思路。


arr6[2]
Out[15]: array([ 2.,  2.,  2.,  2.])

arr6[6]
Out[17]: array([ 6.,  6.,  6.,  6.])

arr6[1]
Out[18]: array([ 1.,  1.,  1.,  1.])

可以看到,花式索引的結果與普通索引是一致的。只不過,花式索引簡化了索引過程,而且還實現了按一定的順序排列。

還可以使用負數(其實類似於列表)進行索引。


arr6[[-2,-6,-1]]
Out[21]: 
array([[ 6.,  6.,  6.,  6.],
       [ 2.,  2.,  2.,  2.],
       [ 7.,  7.,  7.,  7.]])

一次傳入多個索引數組,會返回一個一維數組,其中的元素對應各個索引元組。

有點懵。

arr7 = np.arange(35).reshape(5,7)#生成一個5*7的數組

arr7
Out[24]: 
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]])

arr7[[1,3,2,4],[2,0,6,5]]
Out[27]: array([ 9, 21, 20, 33])

經過對比可以發現,返回的一維數組中的元素,分別對應(1,2)、(3,0)....

這一樣一下子就清晰了,我們傳入來兩個索引數組,相當於傳入了一組平面坐標,從而進行了定位。

此處,照我這樣理解的話,那么一個N維數組,我傳入N個索引數組的話,是不是相當於我傳入了一個N維坐標。

我試驗了下三維,是這樣的,但是以后的不知道了。誰知道求告訴。

ar = np.arange(27).reshape(3,3,3)

ar
Out[31]: 
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]]])

ar[[1,2],[0,1],[2,2]]
Out[32]: array([11, 23])

那么應該如何得到一個矩形區域呢。可以這樣做:

arr7[[1,3,2,4]][:,[2,0,6,5]]
Out[33]: 
array([[ 9,  7, 13, 12],
       [23, 21, 27, 26],
       [16, 14, 20, 19],
       [30, 28, 34, 33]])

必須明白,arr7[2][3]等價於arr7[2,3]

那么上面這種得到矩形區域的方法,就相當於行與列去了交集。

此外還可用np.ix_函數,它的作用與上面的方法類似,只不過是將兩個一維的數組轉換為了一個可以選擇矩形區域的索引器。


arr7[np.ix_([1,3,2,4],[2,0,6,5])]
Out[34]: 
array([[ 9,  7, 13, 12],
       [23, 21, 27, 26],
       [16, 14, 20, 19],
       [30, 28, 34, 33]])

通過,這些試驗,還可發現,花式索引將數據復制到了一個新的數組中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM