為什么有numpy這個庫呢?
1. 准安裝的Python中用列表(list)保存一組值,可以用來當作數組使用,不過由於列表的元素可以是任何對象,因此列表中所保存的是對象的指針。這樣為了保存一個簡單的[1,2,3],需要有3個指針和三個整數對象。對於數值運算來說這種結構顯然比較浪費內存和CPU計算時間。
2. 此外Python還提供了一個array模塊,array對象和列表不同,它直接保存數值,和C語言的一維數組比較類似。但是由於它不支持多維,也沒有各種運算函數,因此也不適合做數值運算。
3. 所以numpy就這么登場了,NumPy是Python的一種開源的數值計算擴展。這種工具可用來存儲和處理大型矩陣,比Python自身的嵌套列表(nested list structure)結構要高效的多(該結構也可以用來表示矩陣(matrix))。 NumPy的主要對象是同種元素的多維數組。這是一個所有的元素都是一種類型、通過一個正整數元組索引的元素表格(通常是元素是數字)。在NumPy中維度(dimensions)叫做軸(axes),軸的個數叫做秩(rank)。
4. numpy的所有的函數docs 可以看:https://docs.scipy.org/doc/numpy/genindex.html
numpy 提供了兩個基本的對象: ndarray 和 ufunc. ndarray是存儲數據的多維數組, 而 ufunc 是對數組進行處理的函數。
以下內容參考自:python科學計算 第二章的內容。
ndarray對象:
1. 如何創建一個ndarray對象——數組??
方法一: 使用 np.array()函數把 python傳入的序列對象創建成數組。 這個序列對象可以是用 []括起來的列表,也可以是用()括起來的元組。 多層序列嵌套使用 , 隔開。例子如下:
#使用列表作為參數: >>> np.array([1, 2, 3]) array([1, 2, 3]) >>> np.array([[1, 2],[3, 4]]) array([[1, 2], [3, 4]]) #使用帶括號的元組作為參數 >>> np.array((4, 5, 6)) array([4, 5, 6]) >>> np.array(((4, 5),(5, 6))) array([[4, 5], [5, 6]])方法二:
np.arange()函數可以通過指定開始值、終值、步長來創建一個等差數列, 不包括終值;
np.linspace()函數可以通過指定開始值、終值、元素個數創建等差數列, 通過endpoint參數指定是否包括終值,默認包括;
np.logspace() 函數創建等比數列,具體用法使用help()查看。
zeros()、ones()和empty()函數可以創建指定的數組, 參數使用元組或列表, 大家一般都使用元組,例如:
>>> np.ones((2,2)) array([[ 1., 1.], [ 1., 1.]]) >>> np.zeros((2,2)) array([[ 0., 0.], [ 0., 0.]]) >>> np.empty((2,2)) array([[ 1.25849429e-316, 4.71627160e-317], [ 6.91798776e-310, 0.00000000e+000]])zeros_like() 、 ones_like()和empty_like()創建與參數的數組相同形狀的數組;
frombuffer()、 fromstring()、 fromfile()等函數可以從字節序列或文件創建數組
使用: fromfunction()通過此函數創建數組, func的參數就是數組元素的索引。例如:
>>> def func(i): ... return i * i ... >>> np.fromfunction(func, (9,)) array([ 0., 1., 4., 9., 16., 25., 36., 49., 64.])
充小知識點:
1) 使用數組的 shape 屬性可以查看一個數組的形狀,它的返回值是一個元組:
>>> a = np.array([[1, 2], [3, 4]]) >>> a array([[1, 2], [3, 4]]) >>> a.shape (2, 2)2)可以通過修改 shape的屬性來修改一個數組的元素, 但是它內存位置不變化 。 例如:
3) 還可以通過 reshape()方法,修改原數組的形狀來創建一個新數組, 特征注意: 新建的數組與原數組是共享內存空間的, 修改其中一個就會影響另一個!!!>>> a.shape = (4, 1) >>> a array([[1], [2], [3], [4]]) >>> a.shape = (4,) >>> a array([1, 2, 3, 4])>>> b = a.reshape((2,2)) >>> b array([[1, 2], [3, 4]])4) 當使用reshape()方法時, 如果其它一個軸的大小設置 為 –1, 則自動計算該軸的長度;
5) 數組元素的類型可以通過 dtype 獲得, 各個類型都存儲在 np.typeDict 字典里。
2. 讀取數組:
使用 [] 操作符對數組內的元素進行讀取 , 那么下標都可以是什么呢?
1. 使用整數, 整數的下標是從0開始的; 如 a[0]等;
2. 使用切片, 切片的使用這里不多說明。只說明一點為: 切片得到的數組與原始數組共享內存單元。
3. 使用整數列表, 如:[1, 3, 5], 說明:使用它得到的新數組與原始的數組不共享內存單元。
4. 使用整數數組, 可以是多維的, 它同樣不會共享內存單元。 如:
>>> b array([[1, 2, 3], [4, 5, 6]]) >>> a = np.arange(100,120,2) >>> a array([100, 102, 104, 106, 108, 110, 112, 114, 116, 118]) >>> a[b] array([[102, 104, 106], [108, 110, 112]])
5. 使用布爾數組, 這個很有意思!!!, 它只保留是對應是 true 的元素。 布爾數組一般都是生成的,例如:
>>> x = np.random.rand(8) >>> x array([ 0.3179888 , 0.44513988, 0.94475611, 0.8954217 , 0.79704721, 0.33844282, 0.56761519, 0.87936442]) >>> x > 0.5 array([False, False, True, True, True, False, True, True], dtype=bool) >>> x[x>0.5] array([ 0.94475611, 0.8954217 , 0.79704721, 0.56761519, 0.87936442])
3. 多維數組:
1. 在多維數組中,使用元組作為數組的下標, 元組的每一個元素與數組的每一個軸對應, 當元組中的元素個數少於少於數組的維數時,默認剩余的各軸為 :, 即表示所有。 a[1, 2] 與a[(1, 2)] 是一樣的;
2. 下標對象不是元組, NumPy 會首先把它轉換為元組。這種轉換可能會和用戶所希望的不一致,因此為了避免出現問題,請顯式地使用元組作為下標。
3. 元組中的每一個元素可以是一個整數, 也可以是一個列表,也可以又是一個元組,也可以是一個數組,也可以是一個布爾數組。 在最后, 這些經過各種轉換和添加“:”之后 ,
得到了一個標准的下標元組。它的各個元素有如下幾種類型:切片、整數、整數數組和布爾數組。如果元素不是這些類型,如列表或元組,就將其轉換成整數數組!!!!
4. 如果下標元組的所有元素都是切片,那么用它作為下標得到的是原始數組的一個視圖,即它和原始數組共享數據存儲空間。 可以使用 a.flags查看一下 OWNDATA字段,如果為False,則是共享的。
當在下標中使用這些對象時,所獲得的數據是原始數據的副本,因此修改結果數組不會改變原始數。
>>> a array([[10, 11, 12, 13, 14], [20, 21, 22, 23, 24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44]]) #使用列表作為元組的第一個元系,第二個元素省略; >>> a[[1,2]] array([[20, 21, 22, 23, 24], [30, 31, 32, 33, 34]]) # 使用兩個元組, 它值其實就是a[0,2]和a[1, 3] >>> a[(0,1), (2,3)] array([12, 23]) #使用一個二維數組作為元組中的第一個元素, 第十個元素省略, 這樣會得到一個三維的數組; >>> a array([[10, 11, 12, 13, 14], [20, 21, 22, 23, 24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44]]) >>> b array([[1, 2], [3, 1]]) >>> b.shape (2, 2) >>> a[b] array([[[20, 21, 22, 23, 24], [30, 31, 32, 33, 34]], [[40, 41, 42, 43, 44], [20, 21, 22, 23, 24]]]) >>> a[b].shape (2, 2, 5)
4. 結構數組:
可以自行創建一個結構數組的類型. 然后,就可以使用這個結構類型創建結構數組了. 只舉一個簡單的例子:
# 創建一個結構體類型, 是一個字典類型,里面有names與formats的鍵, 鍵值為一個列表 persontype = np.dtype({ 'names':['name', 'age', 'weight'], 'formats':['S32','i', 'f']}, align= True ) #使用結構類型創建結構數組 a = np.array([("Zhang",32,75.5),("Wang",24,65.2)], dtype=persontype)
其中:
'S32' :長度為 32 字節的字符串類型,由於結構中每個元素的大小必須固定,因此需要指定字符串的長度。
'i' : 32 bit 的整數類型,相當於 np.int32。
'f' : 32 bit 的單精度浮點數類型,相當於 np.float32。
5 內存結構
這一部分講明了為什么切片操作,可以是共享原始數據的內存,而不用復制的。
每一個數組都可以使用flags屬性查看相關的信息,如:
>>> a array([[10, 11, 12, 13, 14], [20, 21, 22, 23, 24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44]]) >>> a.flags C_CONTIGUOUS : True F_CONTIGUOUS : False OWNDATA : True WRITEABLE : True ALIGNED : True UPDATEIFCOPY : False
ufunc運算
ufunc 是 universal function 的縮寫,它是一種能對數組中每個元素進行操作的函數。 NumPy內置的許多 ufunc 函數都是在 C 語言級別實現的,因此它們的計算速度非常快。
1. 四則運算
它提供了數組的四則運算的相關函數, 即可以通過函數調用進行四則運算,也可以直接使用 運算符進行計算。
其中, 除號的意義根據是否激活 __future__.division 會有所不同:python 2 與 python3中的除法問題。
2.2 比較和布爾運算
1. 比較操作:
使用”==“、”>”等比較運算符對兩個數組進行比較, 將返回一個布爾數組, 它的每一個元素值都是兩個數組對應元素比較的結果。
2. 布爾運算:
由於 Python 中的布爾運算使用 and、 or 和 not 等關鍵字,它們無法被重載,因此數組的布 爾運算只能通過相應的 ufunc 函數進行。這些函數名都以“logical_”開頭, 包括:
np.logical_and()、 np.logical_not()、 np.logical_or() 、 np.logical_xor()四個函數。 使用方法例如:
>>> a>b array([False, False, True, True], dtype=bool) >>> a<b array([ True, True, False, False], dtype=bool) >>> np.logical_or(a>b, a<b) array([ True, True, True, True], dtype=bool)
注意: 當我們對布爾數組使用 python的邏輯運算符 and ,or, not 操作時,會提示錯誤,原因在於,它們只能比較單個的布爾值,不能比較多個。
>>> a>b or a<b Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
3. 比特運算函數:
以“bitwise_”開頭的函數是比特運算函數,包括 bitwise_and、 bitwise_not、 bitwise_or 和 bitwise_xor 等。也可以使用"&"、 "~"、 "|"和"^"等操作符進行計算。 對於布爾數組來說,位運算和布爾運算的結果相同。
4. 廣播
什么意思呢?當兩個數組的維度不對應是,如何進行四則運算呢???這時候,數組就會被擴展處理(廣播處理):
原則(抄書上寫的):
(1) 讓所有輸入數組都向其中維數最多的數組看齊, shape 屬性中不足的部分都通過在前面加 1 補齊。
(2) 輸出數組的 shape 屬性是輸入數組的 shape 屬性在各個軸上的最大值。
(3) 如果輸入數組的某個軸長度為 1 或與輸出數組對應軸的長度相同,這個數組就能夠用來計算,否則出錯。 (意思就是說為1時,可以進行擴展了)
(4) 當輸入數組的某個軸長度為 1 時,沿着此軸運算時都用此軸上的第一組值。
舉例說明:
#創建一個二維數組 a,其形狀為(6,1): >>> a = np.arange(0, 60, 10).reshape(-1, 1) >>> a array([[ 0], [10], [20], [30], [40], [50]]) >>> a.shape (6, 1) #再創建一維數組 b,其形狀為(5,): >>> b = np.arange(0, 5) >>> b array([0, 1, 2, 3, 4]) >>> b.shape (5,) #計算數組 a 和 b 的和,得到一個加法表,它相當於計算兩個數組中所有元素組的和,得到一個形狀為(6,5)的數組 >>> c = a + b >>> c array([[ 0, 1, 2, 3, 4], [10, 11, 12, 13, 14], [20, 21, 22, 23, 24], [30, 31, 32, 33, 34], [40, 41, 42, 43, 44], [50, 51, 52, 53, 54]]) >>> c.shape (6, 5) #驚訝了嗎??,下面解釋一下: #由於數組 a 和 b 的維數不同,根據規則(1),需要讓數組 b 的 shape 屬性向數組 a 對齊,於是將數組 b 的 shape 屬性前面加 1,補齊為(1,5)。相當於做了如下計算 >>> b.shape=1,5 >>> b array([[0, 1, 2, 3, 4]]) #根據規則(2),輸出數組各個軸的長度為輸入數組各個軸長度的最大值,可知輸出數組的 shape 屬性為(6,5)。 #由於數組 b 第 0 軸的長度為 1,而數組 a 第 0 軸的長度為 6,因此為了讓它們在第 0 軸上能夠相加,根據(4)需要將數組 b 第 0 軸的長度擴展為 6,這相當於: >>> b = b.repeat(6,axis=0) >>> b array([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]) # 數組a 同理,作相同的操作: >>> a = a.repeat(5, axis=1) >>> a array([[ 0, 0, 0, 0, 0], [10, 10, 10, 10, 10], [20, 20, 20, 20, 20], [30, 30, 30, 30, 30], [40, 40, 40, 40, 40], [50, 50, 50, 50, 50]]) #經過上述處理之后,數組 a 和 b 就可以按對應元素進行相加運算了。
另外, np.ogrid提供了可以快速產生能夠進行廣播運算的數組。 np.mgrid對象與ogrid類似,它的返回值是進行廣播擴展之后的數組。其切片下標有兩種形式:
● 開始值:結束值:步長,和“np.arange(開始值, 結束值, 步長)”類似。
● 開始值:結束值:長度 j,當第三個參數為虛數時,它表示所返回數組的長度,其和“np.linspace(開始值, 結束值, 長度)”類似
例如:
>> x, y = np.ogrid[:4, :4] >>> x array([[0], [1], [2], [3]]) >>> y array([[0, 1, 2, 3]]) >>> x, y = np.mgrid[:4, :4] >>> x array([[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]) >>> y array([[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]])
(以上內容寫於:2017年11月3日)