Numpy小記——有關axis/axes的理解

【先聲明:本文盡量用簡單直觀的方式解釋說明,可能會有些許錯誤——歡迎指正交流】

  • NumPy『s array type augments the Python language with an efficient data structure useful for numerical work, e.g., manipulating matrices. NumPy also provides basic numerical routines, such as tools for finding eigenvectors.

Numpy作為Python基礎科學計算庫,可以很方便地用來處理多維數組的numerical work。裡面包含了強大的多維數組類型:array(homogeneous)、各種可直接用於矩陣計算的函數(比如求矩陣的秩、求特徵值特徵向量、SVD分解等等)、用於集成C/C++或Fortran代碼的工具,以及用於生成計算隨機數、傅里葉變換、線性代數等等數據處理的tools。

經常用Numpy的猿應該都了解axis/axes對於array的重要性(雖然我不經常用- -、、),許多對於多維數組的操作函數都涉及到這個參數,axis取值不同往往會計算出不同的結果,但axis的存在也讓array的計算更加方便靈活,所以這篇文章的主要內容就是把我對於axis/axes的理解寫出來,幫助大家更好地grasp這個概念,迎娶白富美走上人生巔峰——(霧

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~動感光波~~~~~~~~~~嗶~~~~~~~~~~~~~~~~~~~~~

首先,在Numpy中,維數(dimensions)通過軸(axes)來擴展,軸的個數被稱作rank。這裡的rank不是線性代數中的rank(秩),它指代的依舊是維數(number of dimensions)

The number of axes (dimensions) of the array. In the Python world, the number of dimensions is referred to as rank.

在數學或者物理的概念中,dimensions被認為是在空間中表示一個點所需要的最少坐標個數,但是在Numpy中,dimensions指代的是axes。也就是說,axes、dimensions、rank這幾個概念是相通的。

下面舉例子,比如我們在3-D空間中定義三個點,用array的形式表達:

>>> import numpy as np>>> a = np.array([[1,2,3],[2,3,4],[3,4,9]])>>> aarray([[1, 2, 3], [2, 3, 4], [3, 4, 9]])

按照以往的理解,我們可能會認為這個array是三維的,但實際上在Numpy中,這個array的dimensions,也就是rank的值是等於2的:

>>> np.ndim(a)2

因為它只有兩個axis,只不過這兩個axis的長度(length)均為3:

>>> np.shape(a)(3, 3)

同時,這個矩陣在線性代數中的rank(秩)依舊是3(這裡要加以區分):

>>> np.linalg.matrix_rank(a)3

也就是說,在Numpy中,任何vector都被看作具有不同長度(length)的一維array。

~~~~~~~~~~~~~~~~~~~~~~~~~~動感光波~~~~~~~~~嗶~~嗶~~~~~~~~~~~~~~~~~~~~~~~

下面繼續聊聊axis/axes,我們依舊先定義一個3-D的array:

>>> b = np.array([[[1,2,3,4],[1,3,4,5]],[[2,4,7,5],[8,4,3,5]],[[2,5,7,3],[1,5,3,7]]])>>> barray([[[1, 2, 3, 4], [1, 3, 4, 5]], [[2, 4, 7, 5], [8, 4, 3, 5]], [[2, 5, 7, 3], [1, 5, 3, 7]]])>>> b.shape(3, 2, 4)

我們定義了一個shape為(3, 2, 4)的array,這個shape(用tuple表示)可以理解為在每個軸(axis)上的size,也即佔有的長度(length),三個值的含義:axis = 0對應軸上的元素length = 3,axis = 1對應軸上的元素length = 2,axis = 2對應軸上的元素length = 4,也就是說,當我們在array中選定某個元素時,可能用到的最大索引(index)範圍,舉例:

>>> b[1]array([[2, 4, 7, 5], [8, 4, 3, 5]])>>> b[1,:,:] # Equivalent valuearray([[2, 4, 7, 5], [8, 4, 3, 5]])

這裡的b[1]和b[1, :, :]表示在axis = 0的軸上選取第一個元素,同時選取axis = 1和axis = 2上的全部元素。我們可以暫時把多個axes想像成多層layers,當然也可以想像成樹結構,雖然這麼說並不準確,但是為了方便想像可以暫時這麼理解:

axis = 0表示第一層layer:在代碼中看到的效果就是b從外向里數第一層 [ ],對應的元素(length = 3)就是第一層 [ ] 中用逗號分隔的全部元素(注意:不要關注第二層及以上 [ ] 里的逗號),很明顯這一層的元素數目為3;同理,axis = 1表示第二層layer:第一層layer中用逗號分隔開的任意一個 [ ] 中,再由逗號分隔開的全部元素(length = 2);axis = 2同理,表示最後一層layer中的元素(length = 4)。這麼說還是有些亂,我把剛才的array重新布置一下:

像這樣用猿們寫c/c++代碼時候的風格排布下,黑色代表第一層layer,可以看到有三個元素,紅色代表第二層layer,在第一層layer相同的前提下有兩個元素,藍色代表第三層layer,在第一層和第二層layer都相同的前提下有4個元素。這樣也就對應上了b.shape = (3, 2, 4)的結果。

這時候如果我們想定位到元素8應該怎麼索引?可以看到8在第一層layer的第二個元素內,同時在第二層layer的第二個元素內、第三層layer的第一個元素,也就是說:8是axis = 0軸上的第二個位置、axis = 1軸上的第二個位置,axis = 2的第一個位置共同retrieve到的元素。寫成代碼:

>>> b[1,1,0]8

噔噔噔~就醬。同時也可以發現,array有幾維,我們就可以用幾個integer去索引到(index)它的元素

進一步看下,對於b這個array來說,最後一個元素對應的位置就是可以用到的最大index(從0開始):

>>> b[2,1,3]7

任一axis上再大一點就會出現IndexError了:

>>> b[2,1,4]Traceback (most recent call last): File "<pyshell#38>", line 1, in <module> b[2,1,4]IndexError: index 4 is out of bounds for axis 2 with size 4

~~~~~~~~~~~~~~~~~~~~動感光波~~~~嗶~~~嗶~~嗶~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

之所以要設置不同的axis,是因為對於數據我們可以進行不同維度的處理。例如現在我們收集了四個同學對蘋果、榴槤、西瓜這三種水果的喜愛程度進行打分的數據(總分為10),每個同學都有三個特徵(嗯我一定是第四個同學- -、、):

>>> item = np.array([[1,4,8],[2,3,5],[2,5,1],[1,10,7]])>>> itemarray([[1, 4, 8], [2, 3, 5], [2, 5, 1], [1, 10, 7]])

每一行包含了同一個人的三個特徵,如果我們想看看哪個同學最喜歡吃水果,那就可以用:

>>> item.sum(axis = 1)array([13, 10, 8, 18])

可以大概看出來同學4最喜歡吃水果。

如果我們想看看哪種水果最受歡迎,那就可以用:

>>> item.sum(axis = 0)array([ 6, 22, 21])

可以看出基本是榴槤最受歡迎~~~耶~~~咳、這就是axis存在的意義- -、為了方便地從不同角度對數據進行處理。(嘛、把榴槤加粗應該不會挨打吧……)

~~~~~~~~~~~~~~~~~~~~~~~動感光波~~嗶~~嗶~~~~嗶~~~~~~~~嗶~~~~~~~~~~~~~~~~~

接下來我們將對更高維度的array進行計算,會用到提供axis這個parameter的一些函數對軸這個概念進一步說明:

依舊是求和:ndarray.sum(axis=None, dtype=None, out=None, keepdims=False),等價於numpy.sum(a, axis=None, dtype=None, out=None, keepdims=False):

>>> b.sum()94>>> b.sum(axis = 0)array([[ 5, 11, 17, 12], [10, 12, 10, 17]])>>> b.sum(axis = 1)array([[ 2, 5, 7, 9], [10, 8, 10, 10], [ 3, 10, 10, 10]])>>> b.sum(axis = 2)array([[10, 13], [18, 20], [17, 16]])

不設置axis的時候,sum返回所有元素的加和;當指定axis時,意味著對某一軸內的所有元素對應求和,同時這一層layer就collapsed掉了,因為這個過程是一個reduction operation。例如求b.sum(axis = 1)時,我們還是回到這個圖:

axis = 1,直觀上看,意味著我們需要關注紅色layer之間的元素,順次對axis = 0、axis = 2這兩個軸上相同位置的不同紅框之間的元素求和(好拗口),也就是下圖中連線的元素對應求和:

可以得到(非規範寫法):

array([ [ 2, 5, 7, 9], [10, 8, 10, 10], [ 3, 10, 10, 10] ])

也就是說,假設求和後的array為b_sum,就有:

b_sum[0, 0] = b[0, 0, 0]+b[0, 1, 0]; b_sum[0, 1] = b[0, 0, 1]+b[0, 1, 1]; b_sum[0, 2] = b[0, 0, 2]+b[0, 1, 2]; b_sum[0, 3] = b[0, 0, 3]+b[0, 1, 3];

b_sum[1, 0] = b[1, 0, 0]+b[1, 1, 0]; b_sum[1, 1] = b[1, 0, 1]+b[1, 1, 1]; b_sum[1, 2] = b[1, 0, 2]+b[1, 1, 2]; b_sum[1, 3] = b[1, 0, 3]+b[1, 1, 3];

b_sum[2, 0] = b[2, 0, 0]+b[2, 1, 0]; b_sum[2, 1] = b[2, 0, 1]+b[2, 1, 1]; b_sum[2, 2] = b[2, 0, 2]+b[2, 1, 2]; b_sum[2, 3] = b[2, 0, 3]+b[2, 1, 3];

即在axis = 0、axis = 2相同的情況下,對axis = 1上的全部元素進行求和所得的結果。

同時,在對這個軸上的元素求和之後,這個axis就消失了,我們可以再檢查一下求和後的shape:

>>> b.sum(axis = 1).shape(3, 4)

可以看到,對axis = 1上的元素進行求和後的shape由原來的(3, 2, 4)坍塌到(3, 4),其他軸可以自行驗證。

接下來再看排序函數:ndarray.sort(axis=-1, kind="quicksort", order=None),也可以用numpy.sort(a, axis=-1, kind="quicksort", order=None),但是前者會改變b後者不會改變b:

>>> np.sort(b)array([[[1, 2, 3, 4], [1, 3, 4, 5]], [[2, 4, 5, 7], [3, 4, 5, 8]], [[2, 3, 5, 7], [1, 3, 5, 7]]])>>> np.sort(b, axis = 0)array([[[1, 2, 3, 3], [1, 3, 3, 5]], [[2, 4, 7, 4], [1, 4, 3, 5]], [[2, 5, 7, 5], [8, 5, 4, 7]]])>>> np.sort(b, axis = 1)array([[[1, 2, 3, 4], [1, 3, 4, 5]], [[2, 4, 3, 5], [8, 4, 7, 5]], [[1, 5, 3, 3], [2, 5, 7, 7]]])

第一種情況,sort默認的axis值為-1,對於我們這個case來說,相當於對axis = 2軸上的元素進行排序(也就是倒數第一個軸)。

還是拿其中一種情況舉例子,對於axis = 0,先直觀來看,我們需要關注黑色框之間對應的元素:

我只標註出來了在軸axis = 0上進行排序的過程中,第一組元素之間和最後一組元素之間的對應關係,其他組元素同理。

從索引的角度也可以表示:

對b[0, 0, 0]、b[1, 0, 0]、b[2, 0, 0]進行排序,並將結果由小到大重新排列;

對b[0, 0, 1]、b[1, 0, 1]、b[2, 0, 1]進行排序,並將結果由小到大重新排列;

對b[0, 0, 2]、b[1, 0, 2]、b[2, 0, 2]進行排序,並將結果由小到大重新排列;

對b[0, 0, 3]、b[1, 0, 3]、b[2, 0, 3]進行排序,並將結果由小到大重新排列;

對b[0, 1, 0]、b[1, 1, 0]、b[2, 1, 0]進行排序,並將結果由小到大重新排列;

其餘同理……

嗯就先舉這兩個例子吧,其他帶有參數axis的函數也可以從這兩個角度去分析。

~~~~~~~~~~~~~~~~~~~動感光波~~~~~~~~~~尾聲~~~~~~~~~嗶嗶嗶嗶嗶~~~~~~~~~~~~~

嘛,剛開始碰到各種axis/axes的時候也是一臉doge,所以這幾天感覺get差不多了就把自己的理解寫出來了,既是幫自己屢下思路也是希望這篇文章能給同樣有些疑惑的你帶來一點幫助、

最後,謝謝親的閱讀~~ (〃?〃)ゞ~~

推薦閱讀:

Python中實現iterator
如何用7天學會開發 Django 版的蘋果官網?
左手用R右手Python系列之——noSQL基礎與mongodb入門
OpenCV:圖片操作基本知識
Python 機器學習之 SVM 預測買賣(標的物:比特幣)

TAG:Python | 深度学习DeepLearning | numpy |