- 深度學習的數學:使用Python語言
- (美)羅納德·T.紐塞爾
- 2571字
- 2024-04-22 11:54:55
1.2 NumPy
前面已經安裝了NumPy。現在我來介紹一下NumPy的一些基本概念和運算方法。你如果有興趣,也可以自行查找完整的技術手冊。
啟動Python,嘗試執行以下代碼:
>>> import numpy as np >>> np.__version__ '1.16.2'
第一行代碼導入numpy模塊并將其重命名為np。這種用簡稱來重命名模塊的方式雖然不是必需的,卻幾乎成了通用做法。第二行代碼則輸出版本號,以確保安裝的NumPy版本滿足前面所說的最低要求。
1.2.1 定義數組
NumPy以數組為運算對象,它可以方便地將列表轉換為數組。想想看,與C和Java等語言中的數組類型相比,Python中的列表類型雖然使用起來非常優雅,但是當使用列表模擬數組進行科學計算的時候,效率還是非常低的。NumPy在這方面很有優勢,使用NumPy的數組類型時實際上效率很高。下面的例子首先把列表轉換為數組,然后展示了一些數組屬性:
>>> a = np.array([1,2,3,4]) >>> a array([1, 2, 3, 4]) >>> a.size 4 >>> a.shape (4,) >>> a.dtype dtype('int64')
上面的例子將一個包含4個元素的列表傳給了np.array函數,得到一個NumPy數組。數組最基本的屬性包括size和shape。這個數組的size屬性為4,表示包含4個元素;shape屬性則是包含4的一個元組,表示這是一個包含4個元素的一維數組或者說一維向量。如果數組是二維的,那么其shape屬性將包含兩個元素,分別對應每一維的大小。在下面的例子中,數組b的shape屬性為(2, 4),這表示它是一個2行4列的數組。
>>> b = np.array([[1,2,3,4],[5,6,7,8]]) >>> print(b) [[1 2 3 4] [5 6 7 8]] >>> b.shape (2, 4)
1.2.2 數據類型
Python中的數據類型大體分為兩種:取值幾乎可以是任意大小的整型以及浮點型。NumPy數組則支持更多的數據類型。由于NumPy的底層是用C語言實現的,因此NumPy支持C語言中所有的數據類型。前面的例子向np.array函數傳入了一個各元素為整數的列表,結果得到一個各元素為64位有符號整數的數組。表1-1展示了NumPy支持的數據類型。我們既可以讓NumPy替我們選擇數據類型,也可以顯式指定數據類型。
表1-1 NumPy支持的數據類型,C語言中等價的數據類型以及取值范圍

我們來看一些為數組指定類型的例子:
>>> a = np.array([1,2,3,4], dtype="uint8") >>> a.dtype dtype('uint8') >>> a = np.array([1,2,3,4], dtype="int16") >>> a = np.array([1,2,3,4], dtype="uint32") >>> b = np.array([1,2,3,4.0]) >>> b.dtype dtype('float64') >>> b = np.array([1,2,3,4.0], dtype="float32") >>> c = np.array([111,222,333,444], dtype="uint8") >>> c array([111, 222, 77, 188], dtype=uint8)
在上面的例子中,數組a的元素為整型,而數組b的元素為浮點型。注意在第一個關于數組b的例子中,Python自動為數組b的元素選擇了64位浮點型。之所以會這樣,是因為輸入的列表里有一個浮點數4.0。
關于數組c的例子看起來似乎是錯誤的,其實不然。如果給定的數據超出指定類型的表示范圍,NumPy并不會報錯。在這個例子中,我們指定的8位整型只能表示[0, 255]取值范圍內的整數。前面的兩個數111和222屬于這個范圍;但后面兩個數333和444都太大,NumPy默認只保留這兩個數的最后8位,分別是77和188。NumPy用這個例子給我們上了一課,讓我們明白了該指定什么數據類型。雖然這類問題不常出現,但我們仍須牢記于心。
1.2.3 二維數組
如果說把列表轉換為數組后得到的是一維向量,那么我們可以猜測,如果把一個列表的列表轉換為數組,那么得到的將是一個二維向量。事實的確如此,我們猜對了。
>>> d = np.array([[1,2,3],[4,5,6],[7,8,9]]) >>> d.shape (3, 3) >>> d.size 9 >>> d array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
可以看到,一個由三個子列表構成的列表被映射成了一個3 × 3的向量(即矩陣)。由于NumPy數組從0開始對元素編號,因此引用d[1,2]返回的是6。
1.2.4 全0數組和全1數組
NumPy有兩個非常有用的函數:np.zeros和np.ones。它們都用于定義指定大小的數組。前者用0作為數組全部元素的初始值,而后者則將數組元素全部初始化為1。這是NumPy從頭創建數組的主要方式。
>>> a = np.zeros((3,4), dtype="uint32") >>> a[0,3] = 42 >>> a[1,1] = 66 >>> a array([[ 0, 0, 0, 42], [ 0, 66, 0, 0], [ 0, 0, 0, 0]], dtype=uint32) >>> b = 11*np.ones((3,1)) >>> b array([[11.], [11.], [11.]])
這兩個函數的第一個參數都是元組,用于指定各個維度的大小。如果傳入標量,那么默認定義的是一個一維向量。以數組b為例,它是初始值為1、大小為3 × 1的數組,通過與標量11相乘,可以使數組b的每個元素都為11。
1.2.5 高級索引
上面的例子介紹了訪問單個元素的簡單索引方式。NumPy支持更復雜的索引形式,常用的一種就是用單個索引查詢整個子數組,舉個例子:
>>> a = np.arange(12).reshape((3,4)) >>> a array([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) >>> a[1] array([4, 5, 6, 7]) >>> a[1] = [44,55,66,77] >>> a array([[ 0, 1, 2, 3], [44, 55, 66, 77], [ 8, 9, 10, 11]])
這個例子用到了np.arange函數,它等價于Python中的range函數。注意這里使用reshape方法將一個大小為12的一維向量轉換成了一個3 × 4的矩陣。另外請注意,a[1]返回的是整個子數組,索引是從第一維開始進行的。a[1]其實是a[1, :]的簡化形示,其中的“:”表示某一維的全部元素。這種簡化形式也可以用于賦值操作。
NumPy還支持Python列表的所有切片索引方式,繼續上面的例子:
>>> a[:2] array([[ 0, 1, 2, 3], [44, 55, 66, 77]]) >>> a[:2,:] array([[ 0, 1, 2, 3], [44, 55, 66, 77]]) >>> a[:2,:3] array([[ 0, 1, 2], [44, 55, 66]]) >>> b = np.arange(12) >>> b array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) >>> b[::2] array([ 0, 2, 4, 6, 8, 10]) >>> b[::3] array([0, 3, 6, 9]) >>> b[::-1] array([11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
首先,a[:2]返回數組a中前兩行的全部元素,這里隱含了用“:”索引第二維元素,a[:2]與a[:2,:]等價。關于數組a的第三個例子對兩個維度都進行了索引,通過a[:2, :3]返回了數組a的前兩行和前三列元素。關于數組b的例子展示了如何每隔一個或兩個元素進行查詢。最后一個例子非常有用,這個例子使用一個負的增量實現了索引的倒序。當增量為?1時,表示對所有元素倒序排列。如果增量為?2,則表示以倒序每隔一個元素進行一次查詢。
NumPy使用“:”來表示查詢某一維的全部元素。NumPy還支持用英文省略號來表示“盡可能多的‘:’符號”。為了舉例說明,下面我們先定義一個三維數組:
>>> a = np.arange(24).reshape((4,3,2)) >>> 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]]])
可以把數組a看成4個3 × 2的子數組。如果要更新其中的第2個子數組,我們可以這么做:
>>> a[1,:,:] = [[11,22],[33,44],[55,66]] >>> a array([[[ 0, 1], [ 2, 3], [ 4, 5]], [[11, 22], [33, 44], [55, 66]], [[12, 13], [14, 15], [16, 17]], [[18, 19], [20, 21], [22, 23]]])
這里我們顯式地用“:”進行各個維度的索引,可以看到“:”的使用讓NumPy很有兼容性,NumPy能夠自動將列表的列表識別為數組并且執行相應的更新操作。接下來我們可以看到,用英文省略號也可以實現同樣的效果。
>>> a[2,...] = [[99,99],[99,99],[99,99]] >>> a array([[[ 0, 1], [ 2, 3], [ 4, 5]], [[11, 22], [33, 44], [55, 66]], [[99, 99], [99, 99], [99, 99]], [[18, 19], [20, 21], [22, 23]]])
這里對第3個3 × 2的子數組也進行了更新。
1.2.6 讀寫磁盤
NumPy數組可以通過調用np.save寫到磁盤上,并通過調用np.load從磁盤上加載,比如:
>>> a = np.random.randint(0,5,(3,4)) >>> a array([[4, 2, 1, 3], [4, 0, 2, 4], [0, 4, 3, 1]]) >>> np.save("random.npy",a) >>> b = np.load("random.npy") >>> b array([[4, 2, 1, 3], [4, 0, 2, 4], [0, 4, 3, 1]])
我們先用np.random.randint創建了一個大小為3 × 4,元素取值在0和5之間(包含0和5)的隨機整型數組(NumPy有很多關于隨機數的函數)。然后我們將該數組寫到磁盤上,命名為random.npy。數組文件必須以“.npy”為后綴,如果沒有指定,NumPy會自動添加該后綴。最后,我們通過調用np.load從磁盤上加載了保存的NumPy數組。
本書還會涉及其他的NumPy函數,我會在遇到時詳細介紹這些函數。接下來,我們快速了解一下SciPy。
- 從零開始構建企業級RAG系統
- Android Studio Essentials
- 教孩子學編程:C++入門圖解
- Oracle從入門到精通(第5版)
- Learning Probabilistic Graphical Models in R
- Python自然語言理解:自然語言理解系統開發與應用實戰
- Django Design Patterns and Best Practices
- ASP.NET 4.0 Web程序設計
- ROS機器人編程實戰
- 基于MATLAB的控制系統仿真及應用
- Koa與Node.js開發實戰
- 零基礎學Java(第5版)
- Dart:Scalable Application Development
- Getting Started with Windows Server Security
- Android熱門應用開發詳解