- Python數據分析與挖掘
- 齊福利 楊玲主編
- 3509字
- 2024-03-14 11:21:31
1.2 NumPy數組的操作
1.2.1 數組的數據類型操作
作為一個強大的科學計算庫,NumPy支持的數據類型遠不止Python原生的幾種數據類型。表1-1所示為NumPy支持的數據類型。
表1-1 NumPy支持的數據類型

續表

1.創建數組時指定數組的數據類型
表1-1中的數據類型可以通過np.bool_、np.float16等形式調用,創建數組時可以指定數據類型。
>>> a = np.array([0, 1, 0, 10], dtype=np.bool_) >>> a array([False, True, False, True]) #將0值轉換為False,非0值轉換為True
這里要分清使用的是Python的數據類型,還是NumPy的數據類型。例如,int是Python的數據類型,可以使用dtype=int;而int_是NumPy的數據類型,必須使用np.int_。
NumPy中后綴帶下畫線“_”的數據類型指向的是Python原生的數據類型,也就是說,np.bool_與Python中的原生bool數據類型等效,np.float_與Python中的原生float類型等效。
2.查看NumPy數組的數據類型
我們可以通過NumPy數組自帶的dtype屬性來查看數組的數據類型。
>>> a.dtype #查看數組a的數據類型。注意,dtype后面沒有括號 dtype('bool') >>>type(a) #查看變量的數據類型 NumPy.ndarray
為什么輸出的類型是bool而不是bool_呢?這是因為顯示了原生的數據類型。查看數組的數據類型是NumPy對象的一種方法;查看變量的數據類型是Python的一個函數。
3.修改已有數組的數據類型
一個數組已經被創建,但是想要改變其數據類型,那就可以使用.astype()方法。
>>> a.astype(np.int) #數組a接上例 array([0, 1, 0, 1]) >>>b = np.random.random((2,2)) >>>b array([[0.02914317, 0.645534 ], [0.61839509, 0.64155607]]) >>>b.dtype dtype('float64') >>>b.astype(np.float16) #返回16位浮點數,但不改變原數組的數據類型 array([[0.02914, 0.6455 ], [0.618 , 0.6416 ]], dtype=float16) >>>b.dtype dtype('float64') #仍是64位浮點數
將64位浮點數改為16位浮點數,精度會變低,顯示的位數會變少。但此時查看原數組仍是64位浮點數。要完全改變原數組的數據類型,需將返回值賦給原數組變量。
1.2.2 數組的形狀及其相關操作
在NumPy中,數組用于存儲多維數據,所以數組的形狀指的是數據的維度大小,以及每一維度元素的個數。與數組形狀相關的概念有維度(軸)和形狀(秩)。
與數組形狀相關的方法如下。
(1).ndim:查看維度(軸)。
(2).shape:查看形狀(秩)。
(3).size:查看元素個數。
(4).itemsize:查看元素所占的字節。
注意,這些方法均沒有括號。
1.查看數組形狀
查看數組形狀的代碼如下。
>>> a = np.array([[2, 3, 4], [5, 6, 7]]) #創建2*3的二維數組 >>> a array([[2, 3, 4], [5, 6, 7]]) >>> a.shape #查看形狀屬性
輸出結果如下。
(2, 3)
可以看到,查看形狀屬性時返回的是一個元組,元素的長度代表數組的維度。元組的每一個屬性代表對應的維度的元素個數,(2,3)就表示第1個維度的元素個數是2(2行),第2個維度的長度是3(3列)。
2.修改數組形狀
創建數組后,數組的形狀也是可以改變的。使用數組的.reshape()方法可以改變數組的形狀。
>>> a = np.ones((2, 12)) >>> a array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]]) >>> a.shape (2, 12) >>> a.size #查看數組的長度,即總元素個數 24 >>> b = a.reshape(2, 3, 4) #a.reshape()方法用于返回改變形狀的數組,但不改變a的數組形狀 >>> b array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> b.shape (2, 3, 4) >>> b = a.reshape((2,3,4)) #元組作為參數,結果相同 >>> b array([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]]) >>> b.shape (2, 3, 4) >>> b.ndim #查看數組的維度(軸)數 3
可以看到,.reshape()方法可以同時傳入多個描述形狀的數字,也可以傳入一個數組。不過,將形狀改變為一維數組時,傳入的必須是元組。另外需要注意,傳入.reshape()方法的多個參數的乘積必須與改變前數組的總長度相等,即2×3×4=24,否則系統會報錯。
顯然,計算時需要特別小心。因此,NumPy還提供了一個“?1”的表示方式。數組新的shape屬性要與原來的匹配,如果等于?1,那么NumPy會根據剩下的維度計算出數組的另外一個shape屬性值。
例如下面的代碼。
b = a.reshape(2, 3, ?1) #指定前兩個維度的長度,第三個維度為“?1”則會自動匹配 b.shape (2, 3, 4)
3.將多維數組轉換為一維數組的專用方法.flatten()
NumPy數組專門提供了.flatten()方法將一個多維數組轉換為一維數組。這個方法在執行數組運算時非常有用。
>>> a = np.ones((2, 3)) >>> b = a.flatten() >>> b array([1., 1., 1., 1., 1., 1.]) >>> b.shape (6,)
1.2.3 數組元素訪問:索引與切片
NumPy的數組訪問一般通過索引與切片實現,NumPy在這一方面可謂功能非常強大。NumPy數組中所有的位置索引都是從0開始的,我們可以根據位置索引來精確讀取數據。
索引與切片的所有實例都以數組a展開。
>>> a = np.arange(36).reshape((4, 9))#a = np.arange(36).reshape((4, -1)) >>> a 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]])
(1)讀取一行數據。
>>> a[1] #讀取第2行數據 array([ 9, 10, 11, 12, 13, 14, 15, 16, 17])
(2)讀取連續多行數據。
>>> a[:2] #讀取前2行數據 array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], [ 9, 10, 11, 12, 13, 14, 15, 16, 17]]) >>> a[1:] #讀取第2行后面的所有行數據 array([[ 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]])
也可以加上步長。
>>> a[::2] #每隔一行讀取一次數據 array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8], [18, 19, 20, 21, 22, 23, 24, 25, 26]])
(3)讀取不連續多行數據。
>>> a[[0,-1]] #讀取第一行和最后一行數據 array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8], [27, 28, 29, 30, 31, 32, 33, 34, 35]])
可以看到,根據索引對NumPy取值的方法與Python中使用列表索引取值的方法類似,都是在方括號中傳入位置進行索引取值。對不連續多行進行索引時,每一位數據之間用逗號隔開行位置形成列表取行數據;對連續行進行索引時,使用冒號隔開開始和結束行位置。實際取行時,結束行標號要減一。連續行的開始行和結束行標號可省略,如a[::]。省略時表示從數組的開始位置到結尾位置獲取全部內容。下面是取列數據的操作,與取行數據相似。
(4)讀取一列數據。
>>> a[:,1] #讀取第2列數據 array([ 1, 10, 19, 28])
(5)讀取連續多列數據。
>>> a[:,1:3] #讀取第2列到第3列數據 array([[ 1, 2], [10, 11], [19, 20], [28, 29]])
(6)讀取不連續多列數據。
>>> a[:,[0,3]] #讀取第1列和第4列數據 array([[ 0, 3], [ 9, 12], [18, 21], [27, 30]]))
(7)讀取連續多行多列數據。
>>> a[1:3:,1:3] #讀取第2、3行中的第2、3列數據 array([[10, 11], [19, 20]])
(8)讀取多個不連續位置的數據。
通過上面的講解,你應該明白讀取行、讀取列的規律了,那么如果讀取不連續的多行多列呢?例如讀取第1、3行與第2、4列,你可能認為是a[[0, 2], [1, 3]],我們來看看以下代碼。
>>> a[[0, 2], [1, 3]] array([ 1, 21])
由結果可知,返回的并不是預期的數據,而是第1行第2列、第3行第4列的數據,也就是(0,1)和(2,3)位置的數據。讀取第1、3行與第2、4列數據方法如下。
>>> a[[0,0,2,2],[1,3,1,3]] array([ 1, 3, 19, 21])
(9)讀取單個數據。
>>> b = a[3,3] >>> b 30 >>> type(b) #取單個類型時,返回的就是一個確切的NumPy類型數值 <class 'NumPy.int64'>
1.2.4 數組運算
1.算術運算
NumPy可以進行加減乘除、求n次方和取余數等運算。
參與數組運算的可以是兩個ndarray數組,也可以是數組與單個數值。如果兩個均為數組,需要注意的是數組必須具有相同的形狀或符合數組廣播規則。
NumPy的廣播規則如下。
● 如果兩個數組的維度不相同,那么小維度數組的形狀會在最左邊補1。
● 如果兩個數組的形狀在任何一個維度上都不匹配,那么數組的形狀會沿著維度為1擴展,以匹配另外一個數組的形狀。
● 如果兩個數組的形狀在任何一個維度上都不匹配并且沒有任何一個維度為1,那么會引起異常。
NumPy數組主要的算術運算如下。
print(np.add(x1,x2)) #與x1+x2等價 print(np.subtract(x1,x2)) #與x1-x2等價 print(np.multiply(x1,x2)) #與x1*x2等價 print(np.divide(x1,x2)) #與x1/x2等價 print(np.power(x1,x2)) #x1的x2次方 print(np.remainder(x1,x2)) #取余數也可以用np.mod(x1,x2)
【動動手練習1-4】 數組算術運算
(1)相同形狀數組的運算。
>>> x1=np.array([[1,2,3],[5,6,7],[9,8,7]]) >>>x1.shape (3, 3) >>> x2=np.ones([3,3]) >>> x2.shape (3, 3) >>> x1+x2 #與np.add(x1,x2)等效 array([[ 2., 3., 4.], [ 6., 7., 8.], [10., 9., 8.]]) #其他運算(減、乘、除)請讀者自行實驗 >>> np.mod(((x1+x2)*x1/3).astype(np.int8),x1) #取余操作 array([[0, 0, 1], [0, 2, 4], [3, 0, 4]], dtype=int32) >>>x2*=3 >>>x2 array([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]]) >>> np.power(x1,x2) #乘方操作 array([[ 1., 8., 27.], [125., 216., 343.], [729., 512., 343.]])
取余操作時,需要經過多重算術運算再進行取余操作。
(2)不同形狀數組的運算,必須符合廣播規則。
>>> x3=np.full((2,3),4) >>> x3 array([[4, 4, 4], [4, 4, 4]]) >>> x3.shape (2, 3) >>> x1+x3 ValueError: operands could not be broadcast together with shapes (3,3) (2,3)
系統提示值錯誤:操作數不能與形狀(3,3)(2,3)一起廣播。
下面看一下一維數組能否和已有的多維數組進行運算。
>>> x4=np.arange(8) >>> x4.shape (8,) >>>x1+x4 ValueError: operands could not be broadcast together with shapes (3,3) (8,)
一維數組形狀值大于二維數組的形狀值時,會返回錯誤。數組廣播規則已經明確,只有一維數組的形狀值不大于二維數組的形狀值時才可以廣播匹配。
下面驗證一維數組形狀值不大于二維數組的形狀值時,數組運算的情況。
>>> x4=np.arange(3) #一維數組形狀值等于二維數組的形狀值 >>> x4.shape (3,) >>> x1+x4 array([[1, 3, 5], [5, 7, 9], [9, 9, 9]]) >>> x3+x4 array([[4, 5, 6], [4, 5, 6]]) >>> x4=np.arange(1) #一維數組形狀值小于二維數組的形狀值 >>> x1+x4 array([[1, 2, 3], [5, 6, 7], [9, 8, 7]])
2.數組邏輯判斷表達式的連接操作
在數據分析過程中,我們常常會遇到需要將序列中的數值元素進行對比或加以條件判斷的情況,在Python中可以運用NumPy數組的布爾邏輯來解決這些問題。NumPy中的邏輯運算包括邏輯判斷表達式與連接操作符兩個方面。
(1)邏輯判斷表達式:由等于(==)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=)、不等于(!=)等邏輯運算符實現。這些邏輯運算符都是對兩個相同形狀的數組進行相應位置元素的比較判斷,或是一個數組對一個值進行比較判斷,結果是一個邏輯值的列表。而對整個數組進行比較得到一個邏輯值的判斷則需要借助后面介紹的all和any函數完成。
(2)連接操作符:與(&)、或(|)。連接操作符可以連接邏輯判斷表達式。這里與后面介紹的邏輯運算函數logical_and()和logical_or()的作用一致,用于比較試驗。
【動動手練習1-5】 數組邏輯運算
>>> import NumPy as np >>> x_obj = np.random.rand(10) #包含10個隨機數的數組 >>> x_obj array([0.27891809, 0.8573368 , 0.78180964, 0.89926442, 0.44110754, 0.69994068, 0.84545436, 0.31694934, 0.7900553 , 0.58884895]) >>> x_obj > 0.5 #比較數組中每個元素值是否大于0.5 array([False, True, True, True, False, True, True, False, True, True]) >>>x_obj[x_obj> 0.5] #獲取數組中大于0.5的元素 array([0.8573368 , 0.78180964, 0.89926442, 0.69994068, 0.84545436, 0.7900553 , 0.58884895]) >>> sum((x_obj>0.4) & (x_obj<0.6)) #值大于0.4且小于0.6的元素數量,True表示1,False表示0 2 >>> a = np.array([1, 2, 3]) >>> b = np.array([3, 2, 1]) >>> a > b #兩個數組中對應位置上元素的比較 array([False, False, True]) >>> a[a>b] #數組a中大于數組b對應位置上元素的值 array([3]) >>> a == b array([False, True, False]) >>> a[a==b] array([2]) >>> x_obj = np.arange(1, 10) >>> x_obj array([1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> x_obj[(x%2==0)&(x>5)] #大于5的偶數,兩個數組進行布爾“與”運算;判斷表達式加括號 array([6, 8]) >>> x_obj[(x%2==0)|(x>5)] #大于5的元素或者偶數元素,布爾“或”運算;判斷表達式加括號 array([2, 4, 6, 7, 8, 9])
以上任何通過邏輯運算能夠訪問的數組,自然也可以被賦值。
>>> x_obj[(x_obj %2==0)|( x_obj >5)] =0 >>> x_obj array([1, 0, 3, 0, 5, 0, 0, 0, 0])