- Python 資料科學手冊 (碁峰, 何敏煌譯)
- 高效率資料分析-使用 Python (碁峰, 賴屹民譯)
- Python 資料運算與分析實戰 (旗標, 莊永裕譯)
- Python 資料科學與人工智慧應用實務 (旗標, 陳允傑)
- Python程式設計學習經典:工程分析x資料處理x專案開發 (碁峰, 吳翌禎, 黃立政)
- Data Science from Scratch 中文版 : 用 Python 學資料科學 (碁峰, 籃子軒譯)
# https://zh.wikipedia.org/wiki/标量 (純量)
# https://zh.wikipedia.org/wiki/向量 (矢量, 向量的別名)
但是在線性代數中的向量指的是組成向量空間 (例如一個線性坐標系) 的元素, 一個 n 維向量是以一個有序純量數列例如 (a1, a2, a3, .... an) 來表示 (這也是此向量的終點座標), 其中每一個元素代表向量在該維之分量. 而一個矩陣是二維的有序數列, 可以視為由多個列向量或行向量組成 (默認是列向量). 在程式語言中使用陣列來表示向量 (一維) 或矩陣 (二維), 高於二維的陣列則稱為張量 (Tensor).
陣列是科學計算最常用的資料型態, 不過 Python 內建的資料型態 List (串列) 不是陣列, 它是比陣列更具彈性的序列型資料結構, 它與其他語言如 Java 的陣列不同之處在於 :
- List 的元素可以是任何同質或異質資料 (可混合), 而 Java 的陣列必須是同質性資料. Python 的串列相當於 Java 中的 ArrayList 型態.
- List 的長度是可變的 , 具有 append(), pop() 等函數; 但 Java 的陣列長度宣告後即固定不可變.
import array
然後呼叫建構式 array() 並傳入一個串列以建立 array.array 物件 :
a=array.array('type code', list) #傳入串列 list 將其轉成陣列
其中必要的第一參數 type code 是一個字元, 用來指定陣列元素之資料型態, 數值計算常用的type code 如下 :
- 'i' : signed integer (2 bytes)
- 'I' : unsigned integer (2 bytes)
- 'l' : signed long (4 bytes)
- 'L' : unsigned long (4 bytes)
- 'f' : float (4 bytes)
- 'd' : double (8 bytes)
參考 :
# Python Arrays
陣列物件可用與 List 一樣的索引與 slice 方法存取元素, 例如 :
>>> import array
>>> a=array.array('i', [3, 6, 9, 12])
>>> type(a)
<class 'array.array'>
>>> print(a)
array('i', [3, 6, 9, 12])
>>> a[0]
3
>>> a[1]
6
>>> a[2]
9
>>> a[3]
12
>>> a[-1] #負索引表示從最後面 (-1) 往前定位元素
12
array.array 物件提供如下方法以便操作陣列 :
array.array 物件的方法 | 說明 |
append(element) | 將元素 element 添加到陣列尾端 |
extend(list) | 將串列 list 添加到陣列尾端 |
remove(element) | 將元素 element 從陣列中移除 (一次一個) |
pop(index) | 將索引為 index 之元素從陣列中移除並傳回 |
例如 :
>>> a
array('i', [3, 6, 9, 12])
>>> a.append(99)
>>> a
array('i', [3, 6, 9, 12, 99])
>>> a.extend([6, 12, 18])
>>> a
array('i', [3, 9, 12, 99, 6, 6, 12, 18])
>>> a.remove(6) #移除第一個 6
>>> a
array('i', [3, 9, 12, 99, 6, 12, 18])
>>> a.remove(6) #移除第二個 6
>>> a
array('i', [3, 9, 12, 99, 12, 18])
>>> a.pop(2) #傳回索引 2 元素並刪除之
12
>>> a
array('i', [3, 9, 99, 12, 18])
但 array.array 物件與 List 都不適合直接進行向量運算, 例如向量乘以一個純量會得到另一個向量, 其元素是每一個原向量元素乘以該純量, 但直接用 List 或 array.array 物件乘以一個正整數會使元素倍增; 乘以一個實數則會出現錯誤, 例如 :
串列不是向量 :
>>> list1=[1,2,3]
>>> list1 * 3
[1, 2, 3, 1, 2, 3, 1, 2, 3] #若是向量應該得到 [3, 6, 9]
array 物件不是向量 :
>>> import array
>>> arr1=array.array('i', [1,2,3]) #建立一個整數陣列
>>> arr1 * 3
array('i', [1, 2, 3, 1, 2, 3, 1, 2, 3]) #若是向量應該得到 [3, 6, 9]
向量運算預期 (1, 2, 3) * 3 得到 (3, 6, 9), 但上述範例顯示 List 與 array.array 物件都不是向量, 不能直接進行向量運算, 必須借助自訂函數處理才行. 而且 array.array 只能處理一維陣列, 無法處理矩陣或高維陣列, 傳入 2 維 List 給 array.array() 會出錯, 例如 :
>>> b=array.array('i', [[1, 2, 3], [4, 5, 6]]) #只能傳入一維串列
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
TypeError: an integer is required (got type list)
因此若要做向量, 矩陣或高維陣列運算, 必須用串列與自訂函數來模擬. 在 "Data Science from Scratch 中文版 : 用 Python 學資料科學" 這本書的第四章 "線性代數" 介紹了幾個自訂函數可將串列 List 當成向量來進行向量與矩陣運算 :
Source : 博客來
例如 :
測試 1 : 向量乘以純量
def vector_multiply_scalar(s, v):
""" s=scalar, v=pseudo vector(list)"""
return [s*vi for vi in v]
v1=[1,2,3]
v2=vector_multiply_scalar(3, v1)
print("{} {}".format(type(v2), v2))
v3=vector_multiply_scalar(3.1, v1)
print("{} {}".format(type(v3), v3))
此程式以自訂函數 vector_multiply_scalar(s, v) 模擬串列的向量運算, 第一參數 s 為純量, 可傳入整數或實數, 第二參數為偽向量, 傳入值是串列, 使用串列推導式 for 迴圈計算純量與每一個串列元素的乘積, 然後傳回結果串列, 執行結果如下 :
<class 'list'> [3, 6, 9]
<class 'list'> [3.1, 6.2, 9.3]
兩個相同維度的向量可以相加減, 也可以進行點積 (dot) 運算, 這會用到 Python 內建的好用函數 zip(), 此函數會將傳入的數個可迭代物件之元素一一配對成 tuple 後傳回可迭代之 Zip 物件, 可用 list() 函數轉成串列, 例如 :
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> c=[7,8,9]
>>> d=zip(a, b, c)
>>> type(d)
<class 'zip'>
>>> list(d)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
參考 :
# https://docs.python.org/3.3/library/functions.html#zip
我將書上向量相加減與點積之自訂函數改寫與測試如下 :
測試 2 : 向量相加減與點積運算
def vector_multiply_scalar(s, v):
"""s=scalar, v=pseudo vector(list)"""
return [s*vi for vi in v]
def vector_add(v1, v2):
"""v1, v2=pseudo vector(list)"""
return [v1i + v2i for v1i, v2i in zip(v1, v2)]
def vector_substract(v1, v2):
"""v1, v2=pseudo vector(list)"""
return [v1i - v2i for v1i, v2i in zip(v1, v2)]
def vector_dot(v1, v2):
"""v1, v2=pseudo vector(list)"""
return sum(v1i * v2i for v1i, v2i in zip(v1, v2))
v1=[1,2,3]
v2=[4,5,6]
print("vector_add: {}".format(vector_add(v1, v2)))
print("vector_substract: {}".format(vector_substract(v1, v2)))
print("vector_dot: {}".format(vector_dot(v1, v2)))
執行結果 :
vector_add: [5, 7, 9]
vector_substract: [-3, -3, -3]
vector_dot: 32
以上是以串列模擬向量之操作, 使用二維串列當偽矩陣的做法更麻煩, 更不用說高維陣列了, 這就是發展 Numpy 套件的原因, 它可以更快速更有效率地解決向量, 矩陣, 與張量之運算.
參考 :
# Python Arrays
# numpy: list, array, matrix小结
# Indexing vectors and arrays in Python
# 從零開始學資料科學:Numpy 基礎入門
# Should I use scipy.pi, numpy.pi, or math.pi?
# Quick Tip: The Difference Between a List and an Array in Python