[Python] Numpy 學習筆記: ufunc[np-003]

ChunJen Wang
jimmy-wang
Published in
7 min readApr 12, 2021

本篇文章將介紹我在學習Numpy應用的筆記學。

內容架構結合了w3school以及我在工研院上AI課程的筆記,以下皆有透過colab連結,以.ipynb檔讀取,可以登入Google帳戶查看。架構如:

  1. 入門: numpy獨有的ndarray操作(陣列重塑、合併、切割、搜尋)。
  2. Random: 透過隨機模組生成模擬資料。
  3. ufunc: 通用函數,numpy的加減乘除,與廣播功能(Broadcasting)。
  4. 練習題: 就是更多的練習。
Source: https://towardsdatascience.com/how-to-create-numpy-arrays-from-scratch-3e0341f9ffea

二、ufunc

是numpy中的通用函數,包含了基本的運算邏輯,是實際上在計算資料時,非常重要的一個部份,特別是當我們想要用手刻方式來建立模型,那麼,資料的運算過程就變得非常重要。

全名 “Universal Functions” ,用來處理 “ndarray”物件。

在進入運算之前,網路上也有許多關於為什麼要使用numpy的討論,所有的答案的指引到了效率問題。在numpy有很特別的設計,讓array都是以向量化來進行加減乘除,大大降低了透過迴圈或其他方式的運算時間。

什麼是向量化 (Vectorization)?

以vec(A)的m × n矩陣A的向量化來說,
是通過將矩陣A的各列彼此堆疊而獲得的mn ×1列向量。

向量化示意圖。

但光說不練,實在難信服。我們就用範例一決勝負吧。

給定一個 1,000,000 筆A資料集合,以及1,000,000 筆B資料集合,求每一對 ai 和 bi 相乘的結果的總和c?

每次起步都一樣,我們先引入套件,但在這裡為了計算運算效率,我們需要time這個套件,幫我們算出終點時間點減去起始時間點=耗費時間。

import numpy as np
import time as t

生成資料集合。

a = np.random.rand(1000000)
b = np.random.rand(1000000)
c = 0

方法一:用傳統的for迴圈逐一運算。

StartTime = t.time()for i in range(100000):
  c += a[i]*b[i]
DeltaTime = t.time() — StartTime
print(‘結果=’+str(c)+’耗時:’+str(1000*DeltaTime)+’ ms’)

結果=24958.2096,耗時:62.1336 ms

方法二:用numpy進行運算。

StartTime = t.time()c = np.dot(a, b)DeltaTime = t.time() — StartTime
print(‘結果=’+str(c)+’耗時:’+str(1000*DeltaTime)+’ ms’)

結果=24958.2096,耗時:1.5506 ms

勝負很明顯了,numpy大大超越了for迴圈的運算效率。且我們在此還只是一維的運算,若拓展到高維度,或是資料筆數再向上延伸,都會再將差距大幅拉開。

numpy也有廣播broadcasting或是reduce與accumulate等聰明的運算方式。

廣播功能將會視維度由左至右拓展,若數值符合對應,或數值為1時,皆可以進行運算。

當運算兩個array時, NumPy會根據 shapes進行 element-wise。
It starts with the trailing (i.e. rightmost) dimensions and works its way left.

a = np.array([1,2,3])
b_1 = np.array([2,2,2])
b_2 = 2
print(a * b_1) #[2 4 6]
print(a * b_2) #[2 4 6]
官網numpy廣播概念示意圖。Source: https://numpy.org/devdocs/user/theory.broadcasting.html

官方網站也提出更多的範例:

廣播運算範例。Source: https://numpy.org/devdocs/user/theory.broadcasting.html

四則運算

arr1 = np.array([10, 11, 12, 13, 14, 15])
arr2 = np.array([20, 21, 22, 23, 24, 25])
np.add(arr1, arr2) #[30 32 34 36 38 40]
np.subtract(arr1, arr2) #[-10 -10 -10 -10 -10 -10]
np.multiply(arr1, arr2) #[200 231 264 299 336 375]
np.divide(arr1, arr2) #[0.5 0.5952 0.5455 0.5617 0.5833 0.6]

對比過去我們逐一運算,需要採用zip的方式,numpy提供的向量/矩陣運算是更加便捷的。

x = [1,2,3,4]
y = [4,5,6,7]
z = []
#方法一 採用zip
for i, j in zip(x,y):
z.append(i+j)
print(z) #[5, 7, 9, 11]
#方法二 採用numpy
z = np.add(x,y)
print(z) #[ 5 7 9 11]

同時也有其他的運算方式,譬如:

  • power 次方運算
arr1 = np.array([2, 2, 2, 2, 2])
arr2 = np.array([2, 3, 4, 5, 6])
np.power(arr1, arr2) #[ 4 8 16 32 64]
  • mod 計算餘數
arr1 = np.array([10, 20, 30, 40, 50, 60])
arr2 = np.array([3, 7, 9, 8, 2, 33])
np.mod(arr1, arr2) #[ 1 6 3 0 0 27]
  • divmod 同時印出商數和餘數 (Quotient and Mod)
arr1 = np.array([10, 20, 30, 40, 50, 60])
arr2 = np.array([3, 7, 9, 8, 2, 33])
np.divmod(arr1, arr2)
#(array([ 3, 2, 3, 5, 25, 1]), array([ 1, 6, 3, 0, 0, 27]))

小數位處理

np.fix([-3.1666, 3.6667])      #[-3.  3.]     捨去小數位,功能同trunc()
np.around([-3.1666, 3.6667], 2)#[-3.17 3.67] 設定位數四捨五入
np.floor([-3.1666, 3.6667]) #[-4. 3.] 無條件捨去
np.ceil([-3.1666, 3.6667]) #[-3. 4.] 無條件進位

--

--

ChunJen Wang
jimmy-wang

嗨,歡迎你的到來,我目前在銀行擔任DS。過去曾做過銀行大型專案BA,也曾在轉職科技業DE中踢了鐵板,相信每一個人都有自己要走的路,而努力的過程,可以讓我們離心中理想更接近,如果我的文章能帶給你一些啟發與幫助,別忘了幫我在文章底下按下拍手~^^