資料分析與資料探勘 - 04科學計算

馬一特發表於2020-09-09

一 認識科學計算

在人工智慧的研發中,其本質就是把一切問題轉化為數學問題,所以數學運算非常重要。很多數學運算採用的都是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))  # 求和

相關文章