Numpy的各種下標操作


技術背景

本文所使用的Numpy版本為:Version: 1.20.3。基於Python和C++開發的Numpy一般被認為是Python中最好的Matlab替代品,其中最常見的就是各種Numpy矩陣類型的運算。對於矩陣的運算而言,取對軸和元素是至關重要的,這里我們來看看一些常見的Numpy下標取法和標記。

二維矩陣的取法

這里我們定義一個4*4的矩陣用於取下標,為了方便理解,這個矩陣中所有的元素都是不一樣的:

In [1]: import numpy as np

In [2]: x = np.arange(16).reshape((4,4))

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

取單行和單個元素

比如我們想取第一行的所有元素,那么就是x[0],如果想取第一行的第一列的元素,那么就是x[0][0],而在numpy中為了簡化,可以講x[0][0]寫成x[0,0]的形式:

In [4]: id = 0

In [5]: x[id]
Out[5]: array([0, 1, 2, 3])

In [6]: x[id][id]
Out[6]: 0

In [7]: x[id,id]
Out[7]: 0

下標的list和tuple格式區分

在上一個章節中我們提到的取單個元素x[0,0]的方法,其實本質上等同於x[(0,0)],也就是一個tuple的格式,但是如果把這里的tuple格式換成list,所表示的含義和得到的結果是完全不一樣的:

In [8]: id = [1,1]

In [9]: x[id]
Out[9]: 
array([[4, 5, 6, 7],
       [4, 5, 6, 7]])

In [10]: x[id,id]
Out[10]: array([5, 5])

In [11]: id = (1,1)

In [12]: x[id]
Out[12]: 5

這里list格式的id,代表的意思是分別取第二行和第二行的內容,再放到一個完整的矩陣中。如果id設置為[1,2]的話,就是分別取第二行和第三行,而不是取第二行的第二個元素。如果需要取第二行的第二列的元素,那么還是需要用tuple的格式來取下標。有一個比較有意思的點是,如果把剛才的下標重復輸入兩次,也就是x[[1,2],[1,2]]的話,所表示的含義是分別取x[1][1]和x[2][2],再放到同一個矩陣中,也是一種比較常用的分離式取下標的方法。

冒號的使用

在Numpy的下標中,冒號和后置逗號同時出現,表示軸向全取,比如x[0,:]表示取x的第一行的所有數據,x[:,0]表示取第一列的所有數據:

In [14]: id = 1

In [15]: x[id,:]
Out[15]: array([4, 5, 6, 7])

In [16]: x[:,id]
Out[16]: array([ 1,  5,  9, 13])

現存的list與numpy.array不相兼容的取法

雖然上文我們提到,如果下標被定義成一個list格式的話,就表示分別取。但是目前Numpy的實現中還有這樣的一個遺留問題,就是使用多維的list格式取下標,會自動將最外層轉化成tuple的格式,采用tuple的取法。雖然計算時會給出告警,但是目前來說也需要引起一定的注意。

In [17]: id = [[1],[1]]

In [18]: x[id]
<ipython-input-18-23f8764f4b7e>:1: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
  x[id]
Out[18]: array([5])

In [19]: id = np.array([[1],[1]])

In [20]: x[id]
Out[20]: 
array([[[4, 5, 6, 7]],

       [[4, 5, 6, 7]]])

兩個冒號的組合用法

在Numpy中冒號不與后置逗號同時出現時,表示的含義是從冒號前的元素取值到冒號后的元素,比如x[0:3]所表示的元素是[x[0],x[1],x[2]]。如果是兩個冒號連用中間沒有逗號的話,比如x[0:3:2],表示的是每隔2個元素取一個,最后得到的應該是[x[0],x[2]]。還有一種非常常見的操作是取[::-1]這樣的下標,所表示的含義就是對當前軸進行倒序。

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

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

用None作擴維

雖然在Numpy中有broadcast和expand_dim之類的函數可以對矩陣進行擴維或者是廣播,但是更方便的操作是對需要擴展的維度取一個None的下標,比如要把一個(4,4)大小的矩陣擴展成(1,4,4),那么就對下標取[None,:]或者[None,:,:]即可。而如果需要把(4,4)變成(4,1,4),那就需要把None換個位置為[:,None,:]就可以實現:

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

In [34]: x[:,None,:]
Out[34]: 
array([[[ 0,  1,  2,  3]],

       [[ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11]],

       [[12, 13, 14, 15]]])

In [35]: x[:,:,None]
Out[35]: 
array([[[ 0],
        [ 1],
        [ 2],
        [ 3]],

       [[ 4],
        [ 5],
        [ 6],
        [ 7]],

       [[ 8],
        [ 9],
        [10],
        [11]],

       [[12],
        [13],
        [14],
        [15]]])

高維矩陣的取法

在高維矩陣中,因為沒有了行和列這樣的概念,因此需要從軸上去理解相關操作,我們先定義一個簡單的三維張量:

In [49]: y = np.arange(32).reshape((2,4,4))

In [50]: y
Out[50]: 
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]]])

常規的操作其實都跟前面章節中介紹二維張量一致,這里我們考慮一種比較特殊的場景。就是如果同樣用二維矩陣的取法去取,只是第一條軸每個元素取一個id,比如取第0條軸的[0,1]元素和第1條軸的[2,3]元素,那么其實最簡單的方案就是在第一個下標的位置加上一個位置元素,這個位置元素用下標id的第一個軸的長度去定義即可:

In [58]: id = np.array([[0,1],[2,3]])

In [59]: y[np.arange(id.shape[0]),id[:,0],id[:,1]]
Out[59]: array([ 1, 27])

總結概要

這篇文章的主要內容是梳理在Numpy中經常用到的各種取下標的操作,包括但不限於取指定軸的所有元素、取指定位置的單個元素、取指定位置的多個元素、擴維以及取未顯式給定位置的多個元素等等。比較重要的是在Numpy中tuple的取法和list的取法是代表不一樣的含義,並且由於歷史原因,Numpy中存在一些list取法和numpy.array的取法表示不一致的地方,在本文中進行了總結。

版權聲明

本文首發鏈接為:https://www.cnblogs.com/dechinphy/p/numpy-id.html

作者ID:DechinPhy

更多原著文章請參考:https://www.cnblogs.com/dechinphy/

打賞專用鏈接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

騰訊雲專欄同步:https://cloud.tencent.com/developer/column/91958


免責聲明!

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



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