線性代數作為現代數學的重要分支,其核心概念向量與矩陣的運算方法,已經成為資料科學、機器學習與人工智慧領域不可或缺的基礎知識。在實務應用中,無論是處理高維度資料、執行特徵轉換、建構神經網路模型,或是進行影像處理與電腦視覺任務,都需要深入理解並熟練運用向量與矩陣的各種運算技巧。Python 作為資料科學領域最受歡迎的程式語言,透過 NumPy 函式庫提供了高效能的數值計算功能,而 Pandas 則簡化了結構化資料的處理流程。
NumPy(Numerical Python)是 Python 科學運算生態系統的基石,它提供了強大的多維陣列物件與豐富的數學函式,讓開發者能夠以接近數學符號的方式撰寫程式碼,同時享有經過高度最佳化的執行效能。相較於使用純 Python 的迴圈進行數值計算,NumPy 的向量化運算(Vectorized Operations)能夠將運算速度提升數十倍甚至數百倍。這種效能優勢來自於 NumPy 底層使用 C 語言實作,並且充分利用了現代處理器的 SIMD(Single Instruction Multiple Data)指令集。
Pandas 函式庫則建構在 NumPy 之上,提供了 DataFrame 與 Series 等高階資料結構,讓資料分析工作變得更加直觀與便利。DataFrame 可以視為類似試算表或關聯式資料庫表格的二維資料結構,每一欄都可以是不同的資料型態,並且支援豐富的資料操作方法。在實務專案中,我們經常需要在 NumPy 矩陣與 Pandas DataFrame 之間進行轉換,以便運用各自的優勢來處理不同階段的資料處理任務。
本文將從線性代數的基礎概念開始,逐步介紹向量的加法、減法、點積等運算,並說明如何使用 Matplotlib 進行向量運算的視覺化呈現。接著深入探討矩陣的各種運算方法,包含矩陣乘法、轉置、單位矩陣與逆矩陣的計算。每個概念都會搭配完整的 Python 程式碼範例,並附上詳細的註解說明。最後會展示如何將這些數學運算應用於實際的資料分析場景,透過 Pandas DataFrame 處理真實世界的資料集。無論您是正在學習線性代數的學生,還是希望將數學知識應用於資料科學專案的開發者,本文都將提供實用的知識與技巧。
向量運算的基礎概念與 NumPy 實作
向量是線性代數中最基本的數學物件,可以理解為一組有序的數值集合。在幾何意義上,向量代表空間中的一個點或是從原點出發的一個箭頭,具有大小(長度)與方向兩個重要屬性。在資料科學的應用中,向量常用來表示特徵向量,例如一張圖片可以表示為像素值的向量,一段文字可以透過詞嵌入(Word Embedding)技術轉換為高維向量。
在 Python 中使用 NumPy 處理向量運算時,我們通常使用一維陣列(1D Array)來表示向量。NumPy 陣列與 Python 原生的串列(List)不同,它要求所有元素必須是相同的資料型態,這種限制換來了記憶體使用的效率與運算速度的提升。當我們建立一個 NumPy 陣列時,NumPy 會在記憶體中配置一塊連續的空間來儲存資料,這使得處理器可以更有效率地存取與處理這些數值。
向量的加法運算遵循對應元素相加的規則,也就是將兩個向量中相同位置的元素分別相加,得到一個新的向量。這種運算在物理學中可以用來計算力的合成,在機器學習中則常用於梯度累加或是特徵向量的組合。向量減法的原理類似,是將對應位置的元素相減。需要注意的是,只有維度相同的向量才能進行加減法運算,這是線性代數中的基本規則。
# ============================================================
# 向量加法與減法的基礎運算
# ============================================================
# 此程式展示如何使用 NumPy 進行向量的加法與減法運算
# 包含二維向量與三維向量的範例
import numpy as np
def vector_add(a, b):
"""
計算兩個向量的加法
參數說明:
a: numpy.ndarray - 第一個向量
b: numpy.ndarray - 第二個向量
回傳值:
numpy.ndarray - 兩個向量相加的結果
數學原理:
向量加法遵循對應元素相加的規則
[a1, a2, ..., an] + [b1, b2, ..., bn] = [a1+b1, a2+b2, ..., an+bn]
"""
# np.add() 函式會自動對應元素進行相加
# 這種向量化運算比使用迴圈快得多
return np.add(a, b)
def vector_sub(a, b):
"""
計算兩個向量的減法
參數說明:
a: numpy.ndarray - 被減數向量
b: numpy.ndarray - 減數向量
回傳值:
numpy.ndarray - 兩個向量相減的結果
數學原理:
向量減法遵循對應元素相減的規則
[a1, a2, ..., an] - [b1, b2, ..., bn] = [a1-b1, a2-b2, ..., an-bn]
"""
# np.subtract() 函式執行元素對元素的減法
return np.subtract(a, b)
if __name__ == "__main__":
# ============================================================
# 二維向量運算範例
# ============================================================
# 建立兩個二維向量
# 在平面座標系中,這兩個向量分別指向點 (3, -1) 和 (2, 3)
v1 = np.array([3, -1])
v2 = np.array([2, 3])
# 計算向量加法
# 幾何意義:將 v2 的起點移到 v1 的終點,v2 的終點即為結果向量的終點
add_result = vector_add(v1, v2)
# 計算向量減法
# 幾何意義:從 v1 的終點指向 v2 的終點的向量
sub_result = vector_sub(v1, v2)
print('二維向量運算範例:')
print(f'{v1} + {v2} = {add_result}')
print(f'{v1} - {v2} = {sub_result}')
print()
# ============================================================
# 三維向量運算範例
# ============================================================
# 建立兩個三維向量
# 在三維空間中,這兩個向量分別指向點 (1, 3, -5) 和 (2, -1, 3)
v1_3d = np.array([1, 3, -5])
v2_3d = np.array([2, -1, 3])
# 執行相同的加法與減法運算
add_result_3d = vector_add(v1_3d, v2_3d)
sub_result_3d = vector_sub(v1_3d, v2_3d)
print('三維向量運算範例:')
print(f'{v1_3d} + {v2_3d} = {add_result_3d}')
print(f'{v1_3d} - {v2_3d} = {sub_result_3d}')
# ============================================================
# 向量運算的性質驗證
# ============================================================
# 交換律: a + b = b + a
print('\n向量加法交換律驗證:')
print(f'v1 + v2 = {vector_add(v1, v2)}')
print(f'v2 + v1 = {vector_add(v2, v1)}')
print(f'兩者相等: {np.array_equal(vector_add(v1, v2), vector_add(v2, v1))}')
# 結合律: (a + b) + c = a + (b + c)
v3 = np.array([1, 2])
left_assoc = vector_add(vector_add(v1, v2), v3)
right_assoc = vector_add(v1, vector_add(v2, v3))
print('\n向量加法結合律驗證:')
print(f'(v1 + v2) + v3 = {left_assoc}')
print(f'v1 + (v2 + v3) = {right_assoc}')
print(f'兩者相等: {np.array_equal(left_assoc, right_assoc)}')
在理解向量的基本加減運算後,我們可以進一步探討向量的視覺化呈現。視覺化是理解向量運算的重要工具,特別是對於二維與三維向量,透過圖形化的方式可以更直觀地理解向量的幾何意義。Matplotlib 函式庫提供了豐富的繪圖功能,讓我們能夠繪製向量箭頭、標註文字,並且透過不同的顏色與樣式來區分不同的向量。
在視覺化向量加法時,我們可以採用平行四邊形法則或是三角形法則。平行四邊形法則是將兩個向量的起點重合,以這兩個向量為鄰邊作平行四邊形,其對角線即為兩向量的和。三角形法則則是將第二個向量的起點移至第一個向量的終點,從第一個向量的起點指向第二個向量終點的向量即為和向量。以下程式碼展示如何使用 Matplotlib 繪製向量加法的過程:
# ============================================================
# 向量加法的視覺化呈現
# ============================================================
# 此程式使用 Matplotlib 繪製向量加法的幾何意義
# 展示三角形法則的視覺化過程
import matplotlib.pyplot as plt
import numpy as np
# 設定中文字型以正確顯示繁體中文
# 這對於台灣使用者特別重要
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False # 解決負號顯示問題
# 建立包含三個子圖的畫布
# 用於展示向量加法的三個階段
figure, axes = plt.subplots(3, 1, figsize=(8, 12))
# 定義要視覺化的兩個向量
# 向量 a 從原點指向 (3, -1)
# 向量 b 從原點指向 (2, 3)
vector_a = np.array([3, -1])
vector_b = np.array([2, 3])
vector_sum = vector_a + vector_b
# ============================================================
# 子圖 1: 顯示第一個向量 a
# ============================================================
axes[0].set_xlim([-0.5, 6])
axes[0].set_ylim([-1.5, 2.5])
axes[0].set_aspect('equal')
axes[0].grid(True, alpha=0.3)
axes[0].axhline(y=0, color='k', linewidth=0.5)
axes[0].axvline(x=0, color='k', linewidth=0.5)
# 繪製向量 a
# arrow() 函式參數: 起點x, 起點y, 方向x, 方向y
# head_width 控制箭頭寬度, head_length 控制箭頭長度
axes[0].arrow(0, 0, vector_a[0], vector_a[1],
head_width=0.2, head_length=0.3,
fc='blue', ec='blue', linewidth=2)
# 在向量中點附近標註向量名稱
axes[0].text(vector_a[0]/2 - 0.3, vector_a[1]/2 - 0.3,
'向量 a', fontsize=12, color='blue')
axes[0].set_title('步驟 1: 繪製向量 a', fontsize=14, fontweight='bold')
axes[0].set_facecolor('#f0f8ff') # 淺藍色背景
# ============================================================
# 子圖 2: 顯示兩個向量 a 和 b
# ============================================================
axes[1].set_xlim([-0.5, 6])
axes[1].set_ylim([-1.5, 2.5])
axes[1].set_aspect('equal')
axes[1].grid(True, alpha=0.3)
axes[1].axhline(y=0, color='k', linewidth=0.5)
axes[1].axvline(x=0, color='k', linewidth=0.5)
# 繪製向量 a(與子圖 1 相同)
axes[1].arrow(0, 0, vector_a[0], vector_a[1],
head_width=0.2, head_length=0.3,
fc='blue', ec='blue', linewidth=2)
axes[1].text(vector_a[0]/2 - 0.3, vector_a[1]/2 - 0.3,
'向量 a', fontsize=12, color='blue')
# 繪製向量 b,起點在向量 a 的終點
# 這展示了三角形法則的核心概念
axes[1].arrow(vector_a[0], vector_a[1], vector_b[0], vector_b[1],
head_width=0.2, head_length=0.3,
fc='red', ec='red', linewidth=2)
axes[1].text(vector_a[0] + vector_b[0]/2 + 0.2,
vector_a[1] + vector_b[1]/2,
'向量 b', fontsize=12, color='red')
axes[1].set_title('步驟 2: 將向量 b 的起點移至向量 a 的終點',
fontsize=14, fontweight='bold')
axes[1].set_facecolor('#f0f8ff')
# ============================================================
# 子圖 3: 顯示完整的向量加法結果
# ============================================================
axes[2].set_xlim([-0.5, 6])
axes[2].set_ylim([-1.5, 2.5])
axes[2].set_aspect('equal')
axes[2].grid(True, alpha=0.3)
axes[2].axhline(y=0, color='k', linewidth=0.5)
axes[2].axvline(x=0, color='k', linewidth=0.5)
# 繪製向量 a
axes[2].arrow(0, 0, vector_a[0], vector_a[1],
head_width=0.15, head_length=0.25,
fc='blue', ec='blue', linewidth=1.5, alpha=0.6)
axes[2].text(vector_a[0]/2 - 0.3, vector_a[1]/2 - 0.3,
'向量 a', fontsize=11, color='blue')
# 繪製向量 b
axes[2].arrow(vector_a[0], vector_a[1], vector_b[0], vector_b[1],
head_width=0.15, head_length=0.25,
fc='red', ec='red', linewidth=1.5, alpha=0.6)
axes[2].text(vector_a[0] + vector_b[0]/2 + 0.2,
vector_a[1] + vector_b[1]/2,
'向量 b', fontsize=11, color='red')
# 繪製結果向量(從原點到向量 b 的終點)
# 這是向量加法的結果
axes[2].arrow(0, 0, vector_sum[0], vector_sum[1],
head_width=0.2, head_length=0.3,
fc='green', ec='green', linewidth=2.5)
axes[2].text(vector_sum[0]/2 + 0.3, vector_sum[1]/2 + 0.5,
'向量 a + b', fontsize=12, color='green', fontweight='bold')
axes[2].set_title('步驟 3: 結果向量(綠色)即為向量 a 與 b 的和',
fontsize=14, fontweight='bold')
axes[2].set_facecolor('#f0f8ff')
# 調整子圖之間的間距
plt.tight_layout()
# 儲存圖片(選用)
# plt.savefig('vector_addition_visualization.png', dpi=300, bbox_inches='tight')
# 顯示圖形
plt.show()
向量運算的另一個重要概念是點積(Dot Product),也稱為內積(Inner Product)或純量積(Scalar Product)。點積運算將兩個向量映射為一個純量值,其計算方式是將兩個向量對應位置的元素相乘後再全部加總。點積在機器學習中有廣泛的應用,例如計算兩個向量的相似度、計算向量在另一個向量上的投影長度,或是在神經網路中計算加權和。
點積的幾何意義與向量的夾角密切相關。兩個向量的點積等於它們的長度乘積再乘以夾角的餘弦值。當兩個向量互相垂直時,它們的點積為零,這個性質在特徵工程與降維演算法中特別重要。在主成分分析(PCA)中,我們就是利用向量正交的特性來找出資料的主要變異方向。
# ============================================================
# 向量點積運算與幾何意義
# ============================================================
# 此程式展示向量點積的計算方法與其幾何意義
import numpy as np
import math
def dot_product(v, w):
"""
計算兩個向量的點積
參數說明:
v: numpy.ndarray - 第一個向量
w: numpy.ndarray - 第二個向量
回傳值:
float - 點積結果(純量)
數學定義:
v · w = v1*w1 + v2*w2 + ... + vn*wn
v · w = |v| * |w| * cos(θ)
其中 θ 是兩向量的夾角
應用場景:
- 計算向量相似度
- 計算投影長度
- 神經網路中的加權和
- 判斷向量是否正交
"""
return np.dot(v, w)
def vector_magnitude(v):
"""
計算向量的大小(長度、模)
參數說明:
v: numpy.ndarray - 輸入向量
回傳值:
float - 向量的大小
數學定義:
|v| = sqrt(v1^2 + v2^2 + ... + vn^2)
幾何意義:
向量從原點到終點的距離
"""
# np.linalg.norm() 計算向量的 L2 範數(歐幾里得範數)
return np.linalg.norm(v)
def vector_angle(v, w):
"""
計算兩個向量之間的夾角(以度為單位)
參數說明:
v: numpy.ndarray - 第一個向量
w: numpy.ndarray - 第二個向量
回傳值:
float - 夾角(度)
數學公式:
cos(θ) = (v · w) / (|v| * |w|)
θ = arccos((v · w) / (|v| * |w|))
"""
# 計算點積
dot = dot_product(v, w)
# 計算兩個向量的大小
mag_v = vector_magnitude(v)
mag_w = vector_magnitude(w)
# 計算夾角的餘弦值
# 使用 np.clip 確保數值在 [-1, 1] 範圍內,避免數值誤差
cos_angle = np.clip(dot / (mag_v * mag_w), -1.0, 1.0)
# 計算夾角並轉換為度
angle_rad = np.arccos(cos_angle)
angle_deg = np.degrees(angle_rad)
return angle_deg
if __name__ == "__main__":
# ============================================================
# 範例 1: 基本點積計算
# ============================================================
v1 = np.array([1, 7, -4])
v2 = np.array([2, -3, 10])
# 計算點積
dot_result = dot_product(v1, v2)
print('向量點積運算範例:')
print(f'向量 v1: {v1}')
print(f'向量 v2: {v2}')
print(f'點積 v1 · v2 = {dot_result}')
print()
# 手動驗證計算過程
manual_calc = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]
print(f'手動計算: {v1[0]}*{v2[0]} + {v1[1]}*{v2[1]} + {v1[2]}*{v2[2]} = {manual_calc}')
print()
# ============================================================
# 範例 2: 計算向量大小與夾角
# ============================================================
v3 = np.array([3, 4])
v4 = np.array([4, 3])
mag_v3 = vector_magnitude(v3)
mag_v4 = vector_magnitude(v4)
angle = vector_angle(v3, v4)
print('向量大小與夾角計算:')
print(f'向量 v3: {v3}, 大小: {mag_v3:.4f}')
print(f'向量 v4: {v4}, 大小: {mag_v4:.4f}')
print(f'兩向量夾角: {angle:.2f} 度')
print()
# ============================================================
# 範例 3: 正交向量檢測
# ============================================================
# 兩個向量正交的充要條件是它們的點積為零
v5 = np.array([1, 2])
v6 = np.array([2, -1]) # 與 v5 正交
dot_orthogonal = dot_product(v5, v6)
print('正交向量檢測:')
print(f'向量 v5: {v5}')
print(f'向量 v6: {v6}')
print(f'點積: {dot_orthogonal}')
print(f'是否正交: {abs(dot_orthogonal) < 1e-10}') # 考慮浮點數誤差
print()
# ============================================================
# 範例 4: 向量投影計算
# ============================================================
# 計算向量 v 在向量 w 上的投影長度
def projection_length(v, w):
"""計算向量 v 在向量 w 上的投影長度"""
return dot_product(v, w) / vector_magnitude(w)
v7 = np.array([3, 4])
v8 = np.array([1, 0]) # x 軸方向的單位向量
proj_length = projection_length(v7, v8)
print('向量投影計算:')
print(f'向量 v7: {v7}')
print(f'向量 v8: {v8}')
print(f'v7 在 v8 上的投影長度: {proj_length:.4f}')
print()
# ============================================================
# 範例 5: 餘弦相似度計算
# ============================================================
# 餘弦相似度常用於文本分析與推薦系統
def cosine_similarity(v, w):
"""
計算兩個向量的餘弦相似度
餘弦相似度的值域為 [-1, 1]
1 表示完全相同方向
0 表示正交
-1 表示完全相反方向
"""
return dot_product(v, w) / (vector_magnitude(v) * vector_magnitude(w))
# 假設這是兩個文件的詞頻向量
doc1 = np.array([1, 2, 3, 0, 1])
doc2 = np.array([2, 1, 3, 1, 0])
similarity = cosine_similarity(doc1, doc2)
print('餘弦相似度應用:')
print(f'文件 1 的特徵向量: {doc1}')
print(f'文件 2 的特徵向量: {doc2}')
print(f'餘弦相似度: {similarity:.4f}')
print(f'相似程度: {"高" if similarity > 0.7 else "中" if similarity > 0.3 else "低"}')
理解向量的大小與方向對於許多應用場景都很重要。向量的大小(也稱為模或範數)衡量了向量的強度或長度,在物理學中可以代表速度的大小、力的強度等。向量的方向則告訴我們這個量在空間中的指向。在二維平面中,我們通常使用與正 x 軸的夾角來表示方向,而在三維空間中則可能使用方位角與仰角的組合來描述方向。
# ============================================================
# 向量大小與方向的完整分析
# ============================================================
# 此程式深入探討向量的大小與方向屬性
import numpy as np
import math
def magnitude(v):
"""
計算向量的大小(歐幾里得範數)
參數說明:
v: numpy.ndarray - 輸入向量
回傳值:
float - 向量的大小
實務意義:
- 在物理學中代表量的強度(如速度、力)
- 在機器學習中用於特徵縮放
- 在距離計算中作為基礎
"""
return np.linalg.norm(v)
def direction_2d(v):
"""
計算二維向量的方向角(與正 x 軸的夾角)
參數說明:
v: numpy.ndarray - 二維向量 [x, y]
回傳值:
float - 方向角(度),範圍 [-180, 180]
數學公式:
θ = arctan(y / x)
注意事項:
使用 atan2 而非 atan 以正確處理所有象限
"""
# atan2(y, x) 會考慮 x 和 y 的正負號
# 回傳值範圍為 (-π, π]
angle_rad = math.atan2(v[1], v[0])
# 轉換為度
angle_deg = math.degrees(angle_rad)
return angle_deg
def unit_vector(v):
"""
計算向量的單位向量(方向相同但長度為 1 的向量)
參數說明:
v: numpy.ndarray - 輸入向量
回傳值:
numpy.ndarray - 單位向量
數學定義:
û = v / |v|
應用:
- 提取向量的方向資訊
- 正規化特徵向量
- 作為座標系的基底
"""
mag = magnitude(v)
if mag == 0:
raise ValueError("零向量沒有方向,無法計算單位向量")
return v / mag
def decompose_vector(v):
"""
將向量分解為大小與方向
參數說明:
v: numpy.ndarray - 二維向量
回傳值:
tuple - (大小, 方向角度)
"""
mag = magnitude(v)
direction = direction_2d(v)
return mag, direction
if __name__ == "__main__":
# ============================================================
# 範例 1: 經典的 3-4-5 三角形向量
# ============================================================
v1 = np.array([3, 4])
mag = magnitude(v1)
direction = direction_2d(v1)
unit_v = unit_vector(v1)
print('向量分析範例 1:')
print(f'向量: {v1}')
print(f'大小: {mag:.4f}')
print(f'方向: {direction:.2f} 度')
print(f'單位向量: {unit_v}')
print(f'單位向量的大小: {magnitude(unit_v):.4f}') # 應該等於 1
print()
# ============================================================
# 範例 2: 不同象限的向量
# ============================================================
# 測試 atan2 在所有象限的正確性
vectors = {
'第一象限': np.array([3, 4]),
'第二象限': np.array([-3, 4]),
'第三象限': np.array([-3, -4]),
'第四象限': np.array([3, -4])
}
print('不同象限向量的方向角:')
for name, vec in vectors.items():
mag, dir_angle = decompose_vector(vec)
print(f'{name} {vec}: 大小={mag:.2f}, 方向={dir_angle:.2f}°')
print()
# ============================================================
# 範例 3: 特殊方向的向量
# ============================================================
special_vectors = {
'正 x 軸': np.array([1, 0]),
'正 y 軸': np.array([0, 1]),
'負 x 軸': np.array([-1, 0]),
'負 y 軸': np.array([0, -1]),
'東北 45°': np.array([1, 1]),
'西北 135°': np.array([-1, 1])
}
print('特殊方向向量:')
for name, vec in special_vectors.items():
_, dir_angle = decompose_vector(vec)
print(f'{name}: {vec} → {dir_angle:.1f}°')
print()
# ============================================================
# 範例 4: 向量的正規化應用
# ============================================================
# 在機器學習中,特徵正規化是常見的預處理步驟
features = np.array([100, 200, 50])
print('特徵向量正規化:')
print(f'原始特徵向量: {features}')
print(f'原始向量大小: {magnitude(features):.2f}')
# L2 正規化(單位化)
normalized_features = unit_vector(features)
print(f'正規化後: {normalized_features}')
print(f'正規化後大小: {magnitude(normalized_features):.4f}')
print()
# ============================================================
# 範例 5: 從大小與方向重建向量
# ============================================================
def reconstruct_vector(magnitude, angle_deg):
"""
從大小與方向重建二維向量
參數說明:
magnitude: float - 向量大小
angle_deg: float - 方向角(度)
回傳值:
numpy.ndarray - 重建的向量
"""
angle_rad = math.radians(angle_deg)
x = magnitude * math.cos(angle_rad)
y = magnitude * math.sin(angle_rad)
return np.array([x, y])
# 測試重建向量
test_mag = 5.0
test_angle = 53.13 # arctan(4/3) ≈ 53.13°
reconstructed = reconstruct_vector(test_mag, test_angle)
print('向量重建測試:')
print(f'給定大小: {test_mag}, 方向: {test_angle}°')
print(f'重建向量: {reconstructed}')
print(f'驗證大小: {magnitude(reconstructed):.2f}')
print(f'驗證方向: {direction_2d(reconstructed):.2f}°')
矩陣運算的核心概念與實務應用
矩陣是線性代數中比向量更複雜的數學物件,可以視為向量的集合或是二維的數值陣列。在實務應用中,矩陣可以用來表示線性轉換、儲存資料集、表示圖形的鄰接關係等。一個 m×n 的矩陣包含 m 列(row)與 n 行(column),每個元素可以用兩個索引來定位。在 NumPy 中,我們可以使用二維陣列或是專門的 matrix 物件來表示矩陣。
矩陣與純量的乘法是最基本的矩陣運算之一,操作方式是將矩陣中的每個元素都乘以該純量。這種運算在資料縮放、正規化等場景中經常使用。矩陣的列平均值與行平均值則是統計分析中的重要概念,分別代表每一列所有元素的平均值與每一行所有元素的平均值。透過這些統計量,我們可以了解資料的集中趨勢與分布特性。
# ============================================================
# 矩陣基礎運算與統計分析
# ============================================================
# 此程式展示矩陣的基本操作、純量乘法與統計計算
import numpy as np
def scalar_multiply(matrix, scalar):
"""
矩陣與純量相乘
參數說明:
matrix: numpy.ndarray - 輸入矩陣
scalar: float - 純量(標量)
回傳值:
numpy.ndarray - 結果矩陣
數學定義:
如果 A 是矩陣,k 是純量
則 k*A 的每個元素為 k * A[i][j]
應用場景:
- 資料縮放
- 特徵正規化
- 數值計算中的係數調整
"""
# NumPy 支援廣播機制,可以直接用 * 運算子
# 但這裡使用明確的迴圈以展示運算過程
result = np.empty(matrix.shape) # 建立相同大小的空矩陣
rows, cols = matrix.shape
for i in range(rows):
# 對每一列的所有元素乘以純量
row_multiplied = [element * scalar for element in matrix[i]]
result[i] = np.array(row_multiplied)
return result
def display_matrix_details(matrix, name="矩陣"):
"""
詳細顯示矩陣的內容與結構
參數說明:
matrix: numpy.ndarray - 要顯示的矩陣
name: str - 矩陣的名稱
"""
rows, cols = matrix.shape
print(f'\n{name} 詳細資訊:')
print(f'維度: {rows} 列 × {cols} 行')
print(f'總元素數: {rows * cols}')
print(f'資料型態: {matrix.dtype}')
print()
for i, row in enumerate(matrix):
# 顯示每一列的完整資訊
print(f'第 {i+1} 列: {row}')
print(f' 元素明細: ', end='')
for j, element in enumerate(row):
print(f'[{i},{j}]={element:.2f} ', end='')
print()
if __name__ == "__main__":
# ============================================================
# 範例 1: 建立矩陣與基本操作
# ============================================================
# 定義三個列向量
row1 = [1, 7, -4]
row2 = [2, -3, 10]
row3 = [3, 5, 6]
# 使用 np.matrix 建立矩陣
# 注意: np.matrix 已被棄用,建議使用 np.array
# 但在某些線性代數運算中 matrix 仍然便利
A = np.matrix([row1, row2, row3])
print('原始矩陣 A:')
print(A)
print(f'矩陣形狀: {A.shape}')
print()
# ============================================================
# 範例 2: 純量乘法
# ============================================================
scalar_value = 0.5
# 方法 1: 使用自訂函式
B = scalar_multiply(A, scalar_value)
print(f'矩陣 A 乘以純量 {scalar_value}:')
print(B)
print()
# 方法 2: 使用 NumPy 的廣播機制(推薦)
B_broadcast = A * scalar_value
print(f'使用廣播機制的結果(驗證):')
print(B_broadcast)
print(f'兩種方法結果相同: {np.allclose(B, B_broadcast)}')
print()
# ============================================================
# 範例 3: 矩陣統計量計算
# ============================================================
# axis=0 表示沿著列方向計算(每一行的統計量)
# axis=1 表示沿著行方向計算(每一列的統計量)
# 計算每一行的平均值(列平均值)
column_mean = np.mean(A, axis=0, dtype=np.float64)
print('矩陣 A 的行平均值(每一行的平均):')
print(column_mean)
print(f'形狀: {column_mean.shape}')
print()
# 計算每一列的平均值(行平均值)
row_mean = np.mean(A, axis=1, dtype=np.float64)
print('矩陣 A 的列平均值(每一列的平均):')
print(row_mean)
print(f'形狀: {row_mean.shape}')
print()
# ============================================================
# 範例 4: 更多統計量
# ============================================================
print('矩陣 A 的進階統計資訊:')
print(f'總和: {np.sum(A)}')
print(f'最大值: {np.max(A)}')
print(f'最小值: {np.min(A)}')
print(f'全域平均值: {np.mean(A):.2f}')
print(f'標準差: {np.std(A):.2f}')
print(f'變異數: {np.var(A):.2f}')
print()
# 每一行的統計量
print('每一行的統計量:')
print(f'行總和: {np.sum(A, axis=0)}')
print(f'行最大值: {np.max(A, axis=0)}')
print(f'行最小值: {np.min(A, axis=0)}')
print(f'行標準差: {np.std(A, axis=0)}')
print()
# ============================================================
# 範例 5: 使用 numpy.array 建立矩陣(推薦方式)
# ============================================================
# 建立一個新矩陣,是原矩陣 A 的兩倍
C = np.array([[2, 14, -8],
[4, -6, 20],
[6, 10, 12]])
print('矩陣 C(使用 numpy.array):')
print(C)
print()
# 驗證 C 確實是 A 的兩倍
print('驗證 C = 2*A:')
print(f'C 與 2*A 相等: {np.array_equal(C, 2*np.array(A))}')
print()
# 顯示矩陣 C 的詳細資訊
display_matrix_details(C, "矩陣 C")
# ============================================================
# 範例 6: 矩陣切片與索引
# ============================================================
print('\n矩陣切片範例:')
print(f'第 1 列: {C[0, :]}')
print(f'第 2 行: {C[:, 1]}')
print(f'左上角 2×2 子矩陣:\n{C[0:2, 0:2]}')
print(f'對角線元素: {np.diag(C)}')
矩陣乘法是線性代數中最重要的運算之一,其定義與一般的元素對元素相乘不同。兩個矩陣 A 與 B 能夠相乘的前提是 A 的行數等於 B 的列數。矩陣乘法的結果矩陣 C 的第 i 列第 j 行元素,等於 A 的第 i 列向量與 B 的第 j 行向量的點積。這種運算規則讓矩陣乘法可以表示複雜的線性轉換組合。
# ============================================================
# 矩陣乘法的完整實作與應用
# ============================================================
# 此程式深入探討矩陣乘法的運算規則與實務應用
import numpy as np
def matrix_multiply_manual(A, B):
"""
手動實作矩陣乘法(教學用途)
參數說明:
A: numpy.ndarray - 左矩陣 (m × n)
B: numpy.ndarray - 右矩陣 (n × p)
回傳值:
numpy.ndarray - 結果矩陣 (m × p)
數學定義:
C[i][j] = Σ(k=0 to n-1) A[i][k] * B[k][j]
運算規則:
- A 的行數必須等於 B 的列數
- 結果矩陣的大小為 m × p
"""
rows_A, cols_A = A.shape
rows_B, cols_B = B.shape
# 檢查維度是否匹配
if cols_A != rows_B:
raise ValueError(
f"矩陣維度不匹配: A是{rows_A}×{cols_A}, "
f"B是{rows_B}×{cols_B}, 無法相乘"
)
# 建立結果矩陣
C = np.zeros((rows_A, cols_B))
# 三重迴圈計算矩陣乘法
for i in range(rows_A):
for j in range(cols_B):
# 計算 C[i][j] = A 的第 i 列與 B 的第 j 行的點積
for k in range(cols_A):
C[i][j] += A[i][k] * B[k][j]
return C
def visualize_matrix_multiplication(A, B):
"""
視覺化矩陣乘法的過程
展示如何計算結果矩陣的第一個元素
"""
print("矩陣乘法過程視覺化:")
print("\n矩陣 A:")
print(A)
print("\n矩陣 B:")
print(B)
# 計算結果矩陣的第一個元素 C[0][0]
row = A[0, :]
col = B[:, 0]
print(f"\n計算 C[0][0]:")
print(f" A 的第 1 列: {row}")
print(f" B 的第 1 行: {col}")
print(f" 點積計算: ", end="")
terms = []
for i in range(len(row)):
terms.append(f"{row[i]}×{col[i]}")
print(" + ".join(terms))
print(f" 結果: {np.dot(row, col)}")
if __name__ == "__main__":
# ============================================================
# 範例 1: 基本矩陣乘法
# ============================================================
# 建立兩個 3×3 矩陣
v1 = np.array([1, 7, -4])
v2 = np.array([2, -3, 10])
v3 = np.array([3, 5, 6])
A = np.matrix([v1, v2, v3])
v4 = np.array([-2, 5, 4])
v5 = np.array([1, 2, 9])
v6 = np.array([10, -9, 3])
B = np.matrix([v4, v5, v6])
print('矩陣 A:')
print(A)
print(f'維度: {A.shape}\n')
print('矩陣 B:')
print(B)
print(f'維度: {B.shape}\n')
# 方法 1: 使用 NumPy 的 dot 函式
C_dot = np.dot(A, B)
print('方法 1 - 使用 np.dot(A, B):')
print(C_dot)
print()
# 方法 2: 使用矩陣物件的 dot 方法
C_method = A.dot(B)
print('方法 2 - 使用 A.dot(B):')
print(C_method)
print()
# 方法 3: 使用 @ 運算子(Python 3.5+)
C_operator = A @ B
print('方法 3 - 使用 A @ B 運算子:')
print(C_operator)
print()
# 方法 4: 使用手動實作(教學用途)
C_manual = matrix_multiply_manual(np.array(A), np.array(B))
print('方法 4 - 手動實作:')
print(C_manual)
print()
# 驗證所有方法的結果相同
print('驗證所有方法結果一致:')
print(f'np.dot 與 .dot() 相同: {np.allclose(C_dot, C_method)}')
print(f'@ 運算子與 .dot() 相同: {np.allclose(C_operator, C_method)}')
print(f'手動實作與 .dot() 相同: {np.allclose(C_manual, C_method)}')
print()
# ============================================================
# 範例 2: 視覺化矩陣乘法過程
# ============================================================
visualize_matrix_multiplication(np.array(A), np.array(B))
print()
# ============================================================
# 範例 3: 矩陣乘法的非交換性
# ============================================================
print('矩陣乘法的非交換性(A×B ≠ B×A):')
AB = np.dot(A, B)
BA = np.dot(B, A)
print('A × B:')
print(AB)
print('\nB × A:')
print(BA)
print(f'\nA×B 與 B×A 相等: {np.array_equal(AB, BA)}')
print()
# ============================================================
# 範例 4: 不同維度矩陣的乘法
# ============================================================
# 2×3 矩陣乘以 3×4 矩陣,結果是 2×4 矩陣
D = np.array([[1, 2, 3],
[4, 5, 6]])
E = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
F = np.dot(D, E)
print('不同維度矩陣乘法:')
print(f'D (2×3):\n{D}')
print(f'\nE (3×4):\n{E}')
print(f'\nF = D × E (2×4):\n{F}')
print()
# ============================================================
# 範例 5: 矩陣乘法的應用 - 線性轉換
# ============================================================
# 旋轉矩陣: 將向量逆時針旋轉 90 度
rotation_90 = np.array([[0, -1],
[1, 0]])
# 原始向量
point = np.array([[3],
[4]])
# 應用旋轉轉換
rotated_point = np.dot(rotation_90, point)
print('線性轉換應用 - 向量旋轉:')
print(f'原始點: {point.T}')
print(f'旋轉 90° 後: {rotated_point.T}')
print()
# 驗證旋轉後向量的大小保持不變
original_magnitude = np.linalg.norm(point)
rotated_magnitude = np.linalg.norm(rotated_point)
print(f'原始向量大小: {original_magnitude:.4f}')
print(f'旋轉後向量大小: {rotated_magnitude:.4f}')
print(f'大小保持不變: {np.isclose(original_magnitude, rotated_magnitude)}')
單位矩陣(Identity Matrix)是線性代數中的一個特殊矩陣,其主對角線上的元素都是 1,其餘元素都是 0。單位矩陣在矩陣運算中扮演類似數字 1 在乘法中的角色,任何矩陣乘以對應大小的單位矩陣,結果都是原矩陣本身。逆矩陣(Inverse Matrix)則是另一個重要概念,一個矩陣的逆矩陣與原矩陣相乘會得到單位矩陣。並非所有矩陣都有逆矩陣,只有方陣(列數等於行數的矩陣)且行列式不為零時才存在逆矩陣。
以下程式碼展示單位矩陣與逆矩陣的概念,並透過 PlantUML 圖表說明矩陣運算的關係:
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 14
skinparam minClassWidth 100
rectangle "矩陣運算關係圖" {
package "基本矩陣類型" {
rectangle "一般矩陣\n(General Matrix)\nm × n" as GM
rectangle "方陣\n(Square Matrix)\nn × n" as SM
rectangle "單位矩陣\n(Identity Matrix)\nI" as IM
}
package "特殊運算" {
rectangle "矩陣乘法\nA × B = C" as MM
rectangle "純量乘法\nk × A" as SM_OP
rectangle "矩陣加法\nA + B = C" as MA
}
package "進階概念" {
rectangle "逆矩陣\nA^(-1)" as INV
rectangle "轉置矩陣\nA^T" as TRANS
rectangle "行列式\ndet(A)" as DET
}
}
GM -down-> SM : 特殊情況\nm = n
SM -down-> IM : 對角線為1\n其餘為0
SM --> MM : 可進行
GM --> MA : 相同維度
GM --> SM_OP : 任意矩陣
SM -down-> INV : 當 det(A) ≠ 0
SM --> DET : 計算
GM --> TRANS : 轉置
note right of IM
單位矩陣性質
A × I = I × A = A
對任意矩陣 A
end note
note right of INV
逆矩陣性質
A × A^(-1) = I
A^(-1) × A = I
end note
note bottom of MM
矩陣乘法條件
A(m×n) × B(n×p) = C(m×p)
A 的行數 = B 的列數
end note
@enduml# ============================================================
# 單位矩陣與逆矩陣的完整實作
# ============================================================
# 此程式展示單位矩陣的性質與逆矩陣的計算
import numpy as np
def get_row(matrix, row_index):
"""
提取矩陣的指定列
參數說明:
matrix: numpy.ndarray - 輸入矩陣
row_index: int - 列索引(從 0 開始)
回傳值:
numpy.ndarray - 指定的列向量
"""
return matrix[row_index, :]
def get_column(matrix, col_index):
"""
提取矩陣的指定行
參數說明:
matrix: numpy.ndarray - 輸入矩陣
col_index: int - 行索引(從 0 開始)
回傳值:
numpy.ndarray - 指定的行向量
"""
return matrix[:, col_index]
def to_integer(matrix):
"""
將矩陣元素轉換為整數型態
參數說明:
matrix: numpy.ndarray - 輸入矩陣
回傳值:
numpy.ndarray - 整數型態的矩陣
注意事項:
只在確定矩陣元素都是整數時使用
浮點數會被截斷(不是四捨五入)
"""
return matrix.astype(np.int64)
if __name__ == "__main__":
# ============================================================
# 範例 1: 建立測試矩陣
# ============================================================
A = np.matrix([[1, 9, 3, 6, 7],
[4, 8, 6, 2, 1],
[9, 8, 7, 1, 2],
[1, 1, 9, 2, 4],
[9, 1, 1, 3, 5]])
print('矩陣 A (5×5):')
print(A)
print(f'維度: {A.shape}')
print(f'資料型態: {A.dtype}')
print()
# ============================================================
# 範例 2: 矩陣切片操作
# ============================================================
print('矩陣切片範例:')
# 提取第 1 列(索引 0)
first_row = get_row(A, 0)
print(f'第 1 列: {first_row}')
# 提取第 3 行(索引 2)
third_col = get_column(A, 2)
print(f'第 3 行:\n{third_col.T}') # 轉置以橫向顯示
print()
# ============================================================
# 範例 3: 建立單位矩陣
# ============================================================
# 建立與 A 相同大小的單位矩陣
n = A.shape[0] # 取得矩陣的大小
I = np.identity(n)
I = to_integer(I) # 轉換為整數以便閱讀
print(f'單位矩陣 I ({n}×{n}):')
print(I)
print()
# 顯示單位矩陣的特性
print('單位矩陣的特性:')
print('- 主對角線元素都是 1')
print('- 其他元素都是 0')
print(f'- 對角線元素: {np.diag(I)}')
print()
# ============================================================
# 範例 4: 驗證單位矩陣的性質 (A × I = A)
# ============================================================
product = np.dot(A, I)
print('驗證單位矩陣性質 (A × I = A):')
print('A × I =')
print(product)
print()
print(f'A × I 等於 A: {np.array_equal(product, A)}')
print()
# ============================================================
# 範例 5: 計算逆矩陣
# ============================================================
# 檢查矩陣是否可逆(行列式不為零)
determinant = np.linalg.det(A)
print(f'矩陣 A 的行列式: {determinant:.2f}')
if abs(determinant) > 1e-10: # 避免浮點數誤差
print('行列式不為零,矩陣可逆')
# 計算逆矩陣
A_inv = A.I # 或使用 np.linalg.inv(A)
print('\nA 的逆矩陣 A^(-1):')
print(A_inv)
print()
# ============================================================
# 範例 6: 驗證逆矩陣的性質 (A × A^(-1) = I)
# ============================================================
# 計算 A × A^(-1)
A_times_Ainv = np.dot(A, A_inv)
# 四捨五入到整數(因為浮點數運算可能有微小誤差)
A_times_Ainv_rounded = np.round(A_times_Ainv, decimals=0)
A_times_Ainv_int = to_integer(A_times_Ainv_rounded)
print('驗證逆矩陣性質 (A × A^(-1) = I):')
print('A × A^(-1) =')
print(A_times_Ainv_int)
print()
print(f'A × A^(-1) 等於 I: {np.array_equal(A_times_Ainv_int, I)}')
print()
# 驗證 A^(-1) × A = I
Ainv_times_A = np.dot(A_inv, A)
Ainv_times_A_rounded = np.round(Ainv_times_A, decimals=0)
Ainv_times_A_int = to_integer(Ainv_times_A_rounded)
print('驗證 (A^(-1) × A = I):')
print('A^(-1) × A =')
print(Ainv_times_A_int)
print(f'A^(-1) × A 等於 I: {np.array_equal(Ainv_times_A_int, I)}')
print()
else:
print('行列式為零或接近零,矩陣不可逆(奇異矩陣)')
# ============================================================
# 範例 7: 逆矩陣的應用 - 解線性方程組
# ============================================================
# 線性方程組 A × x = b
# 解為 x = A^(-1) × b
# 建立一個簡單的 3×3 系統
A_simple = np.array([[2, 1, 1],
[1, 3, 2],
[1, 0, 0]])
b = np.array([[4],
[5],
[6]])
print('解線性方程組 A × x = b:')
print(f'係數矩陣 A:\n{A_simple}')
print(f'\n常數向量 b:\n{b.T}')
# 使用逆矩陣求解
A_simple_inv = np.linalg.inv(A_simple)
x = np.dot(A_simple_inv, b)
print(f'\n解向量 x = A^(-1) × b:\n{x.T}')
# 驗證解
verification = np.dot(A_simple, x)
print(f'\n驗證 A × x:\n{verification.T}')
print(f'等於 b: {np.allclose(verification, b)}')
Pandas DataFrame 的資料操作與矩陣轉換
Pandas 是 Python 資料分析生態系統中最重要的函式庫之一,它提供的 DataFrame 資料結構讓表格式資料的處理變得直觀且高效。DataFrame 可以視為一個二維的標籤化資料結構,類似於試算表或 SQL 資料表,每一欄可以是不同的資料型態(整數、浮點數、字串等)。相較於 NumPy 的純數值矩陣,DataFrame 提供了更豐富的資料操作功能,包含資料篩選、分組聚合、缺失值處理等。
在實務應用中,我們經常需要在 NumPy 矩陣與 Pandas DataFrame 之間進行轉換。NumPy 適合進行數值計算與線性代數運算,而 Pandas 則更適合資料清理、探索性分析與報表產生。理解如何在這兩種資料結構之間靈活轉換,對於建構完整的資料分析流程非常重要。
以下程式碼展示如何使用 Pandas 進行資料操作,以及如何與 NumPy 矩陣進行轉換:
# ============================================================
# Pandas DataFrame 的建立與基本操作
# ============================================================
# 此程式展示如何使用 Pandas 處理表格式資料
import pandas as pd
import numpy as np
def create_sample_dataframe():
"""
建立範例 DataFrame
回傳值:
pandas.DataFrame - 包含年齡、身高、體重資料的 DataFrame
資料說明:
模擬12個人的生理數據
- 年齡: 歲
- 身高: 公分
- 體重: 公斤
"""
# 原始資料:每一列代表一個人的資料
data = [
[41, 72, 180], [27, 66, 140],
[18, 59, 101], [57, 72, 160],
[21, 59, 112], [29, 77, 250],
[55, 60, 120], [28, 72, 110],
[19, 59, 99], [32, 68, 125],
[31, 79, 322], [36, 69, 111]
]
# 定義欄位名稱
column_names = ['年齡', '身高', '體重']
# 建立 DataFrame
df = pd.DataFrame(data, columns=column_names)
return df
if __name__ == "__main__":
# ============================================================
# 範例 1: 建立與檢視 DataFrame
# ============================================================
df = create_sample_dataframe()
print('完整 DataFrame:')
print(df)
print(f'\n資料形狀: {df.shape}') # (列數, 行數)
print(f'欄位名稱: {list(df.columns)}')
print(f'資料型態:\n{df.dtypes}')
print()
# ============================================================
# 範例 2: 檢視前 n 列資料
# ============================================================
n = 3
print(f'前 {n} 列資料:')
print(df.head(n))
print()
# ============================================================
# 範例 3: 檢視後 n 列資料
# ============================================================
print(f'後 {n} 列資料:')
print(df.tail(n))
print()
# ============================================================
# 範例 4: 使用索引切片選取列
# ============================================================
print('使用索引切片:')
# 選取第 1 列(索引 0)
print('第 1 列(使用切片 [0:1]):')
print(df[0:1])
print()
# 選取第 3 到第 5 列(索引 2 到 4)
print('第 3 至 5 列(使用切片 [2:5]):')
print(df[2:5])
print()
# ============================================================
# 範例 5: 選取特定欄位
# ============================================================
print('選取特定欄位:')
# 選取單一欄位(回傳 Series)
print(f'前 {n} 列的「年齡」欄位:')
print(df['年齡'].head(n))
print(f'資料型態: {type(df["年齡"])}') # pandas.Series
print()
# 選取單一欄位但回傳 DataFrame
print(f'前 {n} 列的「年齡」欄位(DataFrame 格式):')
print(df[['年齡']].head(n))
print(f'資料型態: {type(df[["年齡"]])}') # pandas.DataFrame
print()
# 選取多個欄位
print(f'後 {n} 列的「體重」和「年齡」欄位:')
print(df[['體重', '年齡']].tail(n))
print()
# ============================================================
# 範例 6: 使用 loc 與 iloc 選取資料
# ============================================================
print('使用 loc 與 iloc 選取資料:')
# loc: 使用標籤選取(列索引和欄位名稱)
print('使用 loc 選取第 3-5 列的年齡與體重:')
print(df.loc[2:4, ['年齡', '體重']])
print()
# iloc: 使用整數位置選取
print('使用 iloc 選取前 3 列的前 2 欄:')
print(df.iloc[0:3, 0:2])
print()
# ============================================================
# 範例 7: 條件篩選
# ============================================================
print('條件篩選範例:')
# 篩選年齡大於 30 的資料
age_filter = df['年齡'] > 30
print('年齡大於 30 的人:')
print(df[age_filter])
print()
# 多重條件篩選
multi_filter = (df['年齡'] > 25) & (df['身高'] > 65)
print('年齡大於 25 且身高大於 65 的人:')
print(df[multi_filter])
print()
# ============================================================
# 範例 8: 統計分析
# ============================================================
print('DataFrame 統計摘要:')
print(df.describe())
print()
# 計算特定欄位的統計量
print('各欄位的平均值:')
print(df.mean())
print()
print('各欄位的中位數:')
print(df.median())
print()
print('各欄位的標準差:')
print(df.std())
print()
# ============================================================
# 範例 9: 增加新欄位(計算 BMI)
# ============================================================
# BMI = 體重(kg) / (身高(m))^2
# 注意:這裡身高單位是英吋,需要轉換
# 1 英吋 = 2.54 公分
df['身高_公分'] = df['身高'] * 2.54
df['身高_公尺'] = df['身高_公分'] / 100
df['體重_公斤'] = df['體重'] * 0.453592 # 磅轉公斤
df['BMI'] = df['體重_公斤'] / (df['身高_公尺'] ** 2)
print('新增 BMI 欄位後的 DataFrame:')
print(df[['年齡', '身高_公分', '體重_公斤', 'BMI']].head())
print()
# ============================================================
# 範例 10: 資料排序
# ============================================================
print('依 BMI 降序排序:')
df_sorted = df.sort_values('BMI', ascending=False)
print(df_sorted[['年齡', '身高_公分', '體重_公斤', 'BMI']].head())
在資料分析流程中,我們經常需要將 NumPy 矩陣轉換為 Pandas DataFrame,以便利用 DataFrame 提供的豐富功能。這種轉換非常簡單,只需要將 NumPy 陣列傳遞給 DataFrame 建構函式即可。反向轉換也很容易,使用 DataFrame 的 .values 屬性即可取得底層的 NumPy 陣列。
# ============================================================
# NumPy 矩陣與 Pandas DataFrame 的相互轉換
# ============================================================
# 此程式展示如何在 NumPy 與 Pandas 之間進行資料轉換
import pandas as pd
import numpy as np
def numpy_to_dataframe_demo():
"""
示範 NumPy 矩陣轉換為 Pandas DataFrame
"""
# ============================================================
# 步驟 1: 建立 NumPy 矩陣
# ============================================================
data = [
[41, 72, 180], [27, 66, 140],
[18, 59, 101], [57, 72, 160],
[21, 59, 112], [29, 77, 250],
[55, 60, 120], [28, 72, 110],
[19, 59, 99], [32, 68, 125],
[31, 79, 322], [36, 69, 111]
]
# 使用 np.array 建立矩陣(推薦)
numpy_matrix = np.array(data)
print('NumPy 矩陣:')
print(numpy_matrix)
print(f'型態: {type(numpy_matrix)}')
print(f'維度: {numpy_matrix.shape}')
print(f'資料型態: {numpy_matrix.dtype}')
print()
# ============================================================
# 步驟 2: 轉換為 DataFrame
# ============================================================
# 定義欄位名稱
column_names = ['年齡', '身高', '體重']
# 轉換為 DataFrame
df = pd.DataFrame(numpy_matrix, columns=column_names)
print('轉換後的 DataFrame:')
print(df)
print(f'型態: {type(df)}')
print(f'維度: {df.shape}')
print()
# ============================================================
# 步驟 3: 欄位選取與切片
# ============================================================
print('選取「年齡」和「身高」欄位:')
selected_columns = df[['年齡', '身高']]
print(selected_columns)
print()
# ============================================================
# 步驟 4: DataFrame 轉換回 NumPy 矩陣
# ============================================================
# 方法 1: 使用 .values 屬性
numpy_from_df = df.values
print('從 DataFrame 轉換回 NumPy 矩陣(使用 .values):')
print(numpy_from_df)
print(f'型態: {type(numpy_from_df)}')
print()
# 方法 2: 使用 .to_numpy() 方法(推薦,更明確)
numpy_from_df_2 = df.to_numpy()
print('從 DataFrame 轉換回 NumPy 矩陣(使用 .to_numpy()):')
print(numpy_from_df_2)
print(f'兩種方法結果相同: {np.array_equal(numpy_from_df, numpy_from_df_2)}')
print()
# ============================================================
# 步驟 5: 特定欄位轉換為 NumPy 陣列
# ============================================================
age_array = df['年齡'].to_numpy()
print('將「年齡」欄位轉換為 NumPy 陣列:')
print(age_array)
print(f'型態: {type(age_array)}')
print(f'維度: {age_array.shape}')
print()
# ============================================================
# 步驟 6: 整合應用 - 使用 NumPy 計算後更新 DataFrame
# ============================================================
# 計算正規化的年齡(Z-score標準化)
age_mean = np.mean(age_array)
age_std = np.std(age_array)
age_normalized = (age_array - age_mean) / age_std
# 將結果加入 DataFrame
df['年齡_標準化'] = age_normalized
print('新增標準化年齡後的 DataFrame:')
print(df[['年齡', '年齡_標準化']])
print()
print(f'標準化後的平均值: {np.mean(age_normalized):.10f}') # 應接近 0
print(f'標準化後的標準差: {np.std(age_normalized):.10f}') # 應接近 1
if __name__ == "__main__":
numpy_to_dataframe_demo()
# ============================================================
# 進階範例: 矩陣運算與 DataFrame 的結合
# ============================================================
print('\n' + '='*60)
print('進階範例:矩陣運算與 DataFrame 的結合')
print('='*60 + '\n')
# 建立協方差矩陣
data = np.array([
[41, 72, 180],
[27, 66, 140],
[18, 59, 101],
[57, 72, 160],
[21, 59, 112]
])
df = pd.DataFrame(data, columns=['年齡', '身高', '體重'])
# 使用 NumPy 計算協方差矩陣
# rowvar=False 表示每一欄是一個變數
cov_matrix = np.cov(df.values, rowvar=False)
# 將協方差矩陣轉換為 DataFrame 以便閱讀
cov_df = pd.DataFrame(
cov_matrix,
columns=['年齡', '身高', '體重'],
index=['年齡', '身高', '體重']
)
print('協方差矩陣:')
print(cov_df)
print()
# 使用 NumPy 計算相關係數矩陣
corr_matrix = np.corrcoef(df.values, rowvar=False)
corr_df = pd.DataFrame(
corr_matrix,
columns=['年齡', '身高', '體重'],
index=['年齡', '身高', '體重']
)
print('相關係數矩陣:')
print(corr_df)
print()
# Pandas 也提供直接計算的方法
print('使用 Pandas 計算相關係數(驗證):')
print(df.corr())
實務應用案例與效能考量
在實際的資料科學專案中,向量與矩陣運算是許多演算法的基礎。從簡單的特徵縮放到複雜的深度學習模型,都需要高效的矩陣運算支援。理解如何正確且高效地使用 NumPy 與 Pandas,對於建構可擴展的資料分析系統非常重要。
效能最佳化的關鍵在於充分利用 NumPy 的向量化運算能力。向量化運算是指將迴圈層級的操作替換為陣列層級的操作,讓底層的 C 程式碼可以一次處理整個陣列,而不是逐一處理每個元素。這種方式不僅程式碼更簡潔,執行速度也能提升數十倍甚至上百倍。
# ============================================================
# 效能比較:向量化運算 vs 迴圈運算
# ============================================================
# 此程式比較不同實作方式的效能差異
import numpy as np
import time
def compare_performance():
"""
比較向量化運算與迴圈運算的效能差異
"""
# 建立大型測試資料
size = 1000000
a = np.random.rand(size)
b = np.random.rand(size)
print(f'測試資料大小: {size:,} 個元素\n')
# ============================================================
# 方法 1: 使用 Python 迴圈(最慢)
# ============================================================
start_time = time.time()
result_loop = []
for i in range(size):
result_loop.append(a[i] + b[i])
result_loop = np.array(result_loop)
loop_time = time.time() - start_time
print(f'方法 1 - Python 迴圈: {loop_time:.4f} 秒')
# ============================================================
# 方法 2: 使用列表推導式(稍快)
# ============================================================
start_time = time.time()
result_comprehension = np.array([a[i] + b[i] for i in range(size)])
comprehension_time = time.time() - start_time
print(f'方法 2 - 列表推導式: {comprehension_time:.4f} 秒')
print(f' 相較於方法 1 快 {loop_time/comprehension_time:.2f} 倍')
# ============================================================
# 方法 3: 使用 NumPy 向量化運算(最快)
# ============================================================
start_time = time.time()
result_vectorized = a + b
vectorized_time = time.time() - start_time
print(f'方法 3 - NumPy 向量化: {vectorized_time:.4f} 秒')
print(f' 相較於方法 1 快 {loop_time/vectorized_time:.2f} 倍')
print(f' 相較於方法 2 快 {comprehension_time/vectorized_time:.2f} 倍')
# 驗證所有方法的結果相同
print(f'\n所有方法結果相同: {np.allclose(result_loop, result_vectorized)}')
return {
'loop_time': loop_time,
'comprehension_time': comprehension_time,
'vectorized_time': vectorized_time
}
if __name__ == "__main__":
print('='*60)
print('NumPy 向量化運算效能測試')
print('='*60 + '\n')
results = compare_performance()
print('\n' + '='*60)
print('效能最佳化建議')
print('='*60)
print("""
1. 優先使用 NumPy 向量化運算
- 避免使用 Python 迴圈處理陣列
- 使用內建的 NumPy 函式(如 np.sum, np.mean 等)
2. 預先配置記憶體
- 使用 np.empty, np.zeros, np.ones 預先建立陣列
- 避免動態增加陣列大小
3. 選擇適當的資料型態
- 根據需求選擇 float32 或 float64
- 整數運算使用 int32 或 int64
- 較小的資料型態可減少記憶體使用與提升快取效率
4. 使用 NumPy 的廣播機制
- 避免手動擴展陣列維度
- 讓 NumPy 自動處理維度對齊
5. 批次處理大型資料
- 對於超大資料集,分批處理避免記憶體溢位
- 使用 memmap 處理無法完全載入記憶體的資料
""")
if __name__ == "__main__":
compare_performance()
結論與進階學習建議
透過本文的完整介紹,我們深入探討了線性代數中向量與矩陣運算的核心概念,以及如何使用 Python 的 NumPy 與 Pandas 函式庫來實作這些運算。從基礎的向量加減法、點積計算,到進階的矩陣乘法、單位矩陣與逆矩陣,每個概念都搭配了詳細的程式碼範例與實務應用說明。
NumPy 的向量化運算能力讓我們能夠以接近數學符號的方式撰寫程式碼,同時享有高效能的執行速度。理解如何正確使用 NumPy 的各種函式與運算子,對於資料科學與機器學習專案的成功至關重要。Pandas DataFrame 則提供了更高階的資料操作介面,讓表格式資料的處理變得直觀且便利。掌握 NumPy 與 Pandas 之間的轉換技巧,能夠讓我們靈活運用兩者的優勢來建構完整的資料分析流程。
在實務應用中,效能最佳化是不可忽視的重要課題。充分利用向量化運算、避免不必要的迴圈、選擇適當的資料型態,這些看似簡單的優化技巧,在處理大規模資料時能夠帶來顯著的效能提升。建議讀者在實作專案時,養成使用 NumPy 向量化運算的習慣,並透過效能測試來驗證程式碼的執行效率。
對於希望進一步深入學習的讀者,建議探索以下主題:矩陣分解技術(如 SVD、QR 分解)在降維與特徵提取中的應用,特徵值與特徵向量在主成分分析中的角色,以及稀疏矩陣的處理方法。這些進階概念在機器學習演算法中都扮演重要角色,掌握這些知識將能夠讓您更深入理解各種演算法的運作原理。
台灣的資料科學社群持續成長,越來越多企業開始重視資料驅動的決策模式。掌握線性代數與數值計算的能力,將為您的職涯發展開啟更多可能性。無論您是資料分析師、機器學習工程師,還是研究人員,紮實的數學基礎與程式實作能力都是不可或缺的核心競爭力。持續練習、深入理解背後的數學原理,您將能夠自信地應對各種資料科學挑戰。