一 認識科學計算
在人工智慧的研發中,其本質就是把一切問題轉化為數學問題,所以數學運算非常重要。很多數學運算採用的都是numpy這個庫,因為它提供了非常多的科學計算的方法,能讓我們的工作變得非常便利,這一章我將從numpy的基本使用開始,逐漸解決掉那些數學問題,讓Python與數學能夠更緊密的結合在一起。
二 認識numpy
numpy的本質其實還是一個多維陣列,雖然我們之前學習過陣列物件(Python中的list或者tuple)和numpy的資料看似一樣,但是陣列是無法直接參與數值運算的,而numpy物件卻可以。
三 陣列建立
import numpy as np
arr1 = np.array([1, 2, 3, 4, 5, 6])
arr2 = np.array([[1, 2, 3, 4, ], [5, 6, 7, 8, ]])
print(arr1, arr1.shape, arr1.dtype)
print(arr2, arr2.shape, arr1.dtype) # shape獲取陣列形狀2行4列,dtype獲取陣列中元素型別
如果我們建立陣列時,元素型別不一樣,numpy會給我們自動處理成一樣的。
arr3 = np.array([1, 2.5, 3]) # 只要陣列元素中出現float型別,就會全部處理成float
print(arr3, arr3.dtype)
arr4 = np.array(['4', 5, 5.6]) # 只要陣列元素中出現str,就會全部處理成str
print(arr4, arr4.dtype)
四 陣列訪問
numpy的訪問與Python中list或者tuple訪問原理一樣,方法也非常類似,只不過是加了一個緯度的概念。
# 首先我們定義了一個二維陣列
arr5 = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]])
print(arr5[1]) # 第一行 [4 5 6]
print(arr5[1][0]) # 第一行第0個 4
print(arr5[:, 2]) # 所有行第2個 [ 3 6 9 12]
print(arr5[:2]) # 前2行 [[1 2 3] [4 5 6]]
print(arr5[1, :]) # 第1行所有列 [4 5 6]
print(arr5[:, 1:2]) # 所有行第2到第3列 [[ 2] [ 5] [ 8] [11]]
對於二維來說,如果有逗號,逗號前是行篩選,逗號後是列篩選。對於n維來說,第一個逗號前是第一維,後面依次是二維三維等。
五 形狀處理
1 預覽修改與真正修改
numpy物件有一個shape屬性,在Python基礎中,對於形狀並不敏感,而在科學計算中,形狀卻很重要,在後面的演算法模型計算中,我們會使用地很頻繁。
# 定義一個6行3列的numpy陣列物件
arr6 = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
print(arr6.shape) # 注意看每次列印的結果
print(arr6.reshape(2, 9)) # 得到一個新的形狀,原來的物件不變
print(arr6.shape, arr6)
print(arr6.resize(2, 9)) # 無返回值,真正修改
print(arr6.shape, arr6)
我們在這段程式碼中,分別使用了reshape和resize兩種方法來對陣列進行形狀上的改變。其中reshape只是返回改變形狀後的預覽狀態,或者說如果我們要使用這個結果只能把結果賦值給一個單獨的變數,然後再進行使用。resize方法的返回結果為空,但是它卻真正的改變了組數的形狀,仔細看列印結果你就能夠發現這兩種形狀操作方法的區別了。
2 降維操作
降維是人工智慧演算法中非常常用且重要的一個操作,原因是有時我們去描述一個事物的特徵時,會有非常多的維度,但過多的維度會給我們的計算帶來麻煩,這個時候我們就需要去降低它的維度,然後再進行計算。
arr7 = np.array([[1, 10, 100], [2, 20, 200], [3, 30, 300]])
# 按照陣列的行順序降至一維
print(arr7)
print(arr7.ravel())
print(arr7.reshape(-1))
print(arr7.flatten())
# 按照大小順序降至一維
print(arr7.ravel(order="F"))
print(arr7.reshape(-1, order="F"))
print(arr7.flatten(order="F"))
降維後再進行修改
print('ravel:{}'.format(arr7.ravel()))
arr7.ravel()[1] = 1000
print(arr7)
print('reshape: {}'.format(arr7.reshape(-1)))
arr7.reshape(-1)[2] = 3000
print(arr7)
print('flatten: {}'.format(arr7.flatten()))
arr7.flatten()[0] = 2000
print(arr7)
從結果中,我們看到通過flatten方法實現的降維返回的是複製的操作,如果要用,那麼只能把結果賦值給另外的變數了。它並沒有影響原來陣列的結果。通過ravel和reshape兩個方法,返回的則是檢視,也就是通過對檢視的修改,是會直接影響到原陣列中的值的。
3 陣列堆疊
arr8 = np.array([[1, 10, 100], [2, 20, 200], [3, 30, 300]])
arr9 = np.array([1, 2, 3])
arr10 = np.array([[5], [6], [7]])
# 縱向堆疊
print(np.vstack([arr8, arr9]))
print(np.row_stack([arr8, arr9]))
# 橫向合併
print(np.hstack([arr8, arr10]))
print(np.column_stack([arr8, arr10]))
需要注意的是:多個陣列橫向堆疊時,要保證行數相同,縱向合併,則要保證列數相同。
六 基本運算
1 四則運算
在以前,我們如果要對兩個同形狀的陣列進行對應位置的四則運算時,我們必須要對兩個陣列進行迴圈處理,程式碼量上來說並不少,並且容易出錯。有了NumPy之後,這些運算將會變的非常的簡單。
import numpy as np
arr1 = np.array([11, 12, 13])
arr2 = np.array([21, 22, 23])
arr3 = np.array([31, 32, 33])
print(arr1 + arr2)
print(arr1 + arr2 + arr3)
print(arr1 - arr2)
print(arr1 - arr2 - arr3)
print(arr1 * arr2)
print(arr1 * arr2 * arr3)
print(arr1 / arr2)
print(arr1 / arr2 / arr3)
print(arr1 // arr2)
print(arr1 % arr2)
print(arr1 ** arr2)
print(np.add(arr1, arr2))
print(np.add(np.add(arr1, arr2), arr3))
print(np.subtract(arr1, arr2))
print(np.multiply(arr1, arr2))
print(np.divide(arr1, arr2))
從程式碼的執行結果中我們可以看到,當我們使用符號進行四則運算的時候,是可以連續進行操作的。當我們使用物件的方法進行四則運算的時候,不可以連續進行操作,因為這個方法只接收兩個引數。如果我們想要對多個陣列物件進行操作的時候,我們必須使用方法巢狀的方式來進行操作。除了四則運算,在學習Python基礎時,所學習的取餘數、整除、冪運算等都是支援的。
2 比較運算
print(arr1 <= arr2)
print(arr1 == arr2)
print(arr1 != arr2)
print(np.greater(arr1, arr2))
print(np.greater_equal(arr1, arr2))
print(np.less(arr1, arr2))
print(np.less_equal(arr1, arr2))
print(np.equal(arr1, arr2))
print(np.not_equal(arr1, arr2))
從結果上看,運用比較運算子可以返回布林型別的值,也就是True和False。那我們什麼時候會用到這樣的運算呢?第一種情況是從陣列中查詢滿足條件的元素,第二種情況是根據判斷的結果執行不同的操作,示例程式碼如下:
arr3 = np.array([23, 12, 25])
arr4 = np.array([21, 15, 23])
print(arr3[arr3 > arr4]) # 取出arr3中元素大於arr4的
print(arr3[arr3 > 24]) # 取出arr3中元素大於24的
print(np.where(arr3 > 24, 0, arr3)) # 類似三元表示式,把大於24的修改成0,其他不變
print(list(0 if x > 24 else x for x in arr3))
print(np.where(arr4 > 16, 0, arr4))
3 廣播運算
上面我們所有的運算都是基於相同形狀的陣列,那麼當陣列形狀不同時,能夠讓它們之間進行運算嗎?答案是肯定的,但是有相應的規則,不能隨意計算,這種計算就叫做廣播運算。
# 1 廣播運算,末尾的緯度值加上去
arr3 = np.arange(60).reshape(5, 4, 3)
arr4 = np.arange(12).reshape(4, 3)
print(arr3)
print(arr4)
print(arr3 + arr4)
# 2 緯度值有一個為1
arr5 = np.arange(60).reshape(5, 4, 3)
arr6 = np.arange(4).reshape(4, 1)
print(arr5)
print(arr6)
print(arr5 + arr6)
# 3 arr7會自動補齊,類似上面緯度值有一個為1
arr7 = np.arange(12).reshape(4, 3)
arr8 = np.array([1, 2, 3])
print(arr7)
print(arr8)
print(arr7 + arr8)
arr9 = np.arange(60).reshape(5, 4, 3)
arr10 = np.arange(8).reshape(4, 2)
print(arr9)
print(arr10)
# print(arr9 + arr10) # 不在上述三種討論範圍內,無法運算
其實,廣播運算中的廣播就是一對多,它的規律就是兩者有相似的地方可以對應的上就能運算,缺少的部分,會自動用相同的部分補齊。
七 數學函式
numpy提供給我們一些常見的函式,除了np.pi或者np.e這樣的常量函式,numpy也提供給我們很多數學函式供我們直接呼叫。
import numpy as np
arr1 = np.array([1.3, 1.5, -1.8, 2.4, 3.2])
arr2 = np.array([1, 2, 3, 4, 5])
print(np.fabs(arr1)) # 絕對值
print(np.ceil(arr1)) # 向上取整
print(np.floor(arr1)) # 向下取整
print(np.round(arr1)) # 四捨五入
print(np.fmod(arr2, arr1)) # 餘數
print(np.modf(arr1)) # 取小數部分和整數部分
print(np.sqrt(arr2)) # 演算法平方根
print(np.square(arr2)) # 平方
print(np.exp(arr2)) # 以e為底的指數
print(np.power(arr2, 3)) # 各元素的3次方
print(np.log2(arr2)) # 以2為底的對數
print(np.log10(arr2)) # 以10為底的對數
print(np.log(arr2)) # 以e為底的對數
八 軸方向
陣列物件有幾個維度就有幾個軸,對於我們常見的二維陣列來說,軸0是豎直方向,軸1是水平方向。
import numpy as np
arr1 = np.array([[3, 7, 25, 8, 15, 20],
[4, 5, 6, 9, 14, 21]])
print(arr1)
print(np.max(arr1)) # 所有陣列元素最大值
print(np.max(arr1, axis=1)) # 軸1最大值
print(np.max(arr1, axis=0)) # 軸0最大值
numpy提供了很多可以按照軸方向來計算的函式。
print(np.min(arr1, axis=0)) # 最小值
print(np.min(arr1, axis=1))
print(np.mean(arr1, axis=0)) # 平均值
print(np.median(arr1, axis=0)) # 中位數
print(np.sum(arr1, axis=1)) # 求和