owned this note
owned this note
Published
Linked with GitHub
# 資料前處理與分析
## Source
- 大數據分析與資料探勘 W2 圖片
- 大數據分析與資料探勘 W3
- 應⽤機器學習於Python C1
- [Website](https://medium.com/li-ting-liao-tiffany/python-%E5%BF%AB%E9%80%9F%E8%B3%87%E6%96%99%E5%88%86%E6%9E%90-boston-housing%E6%B3%A2%E5%A3%AB%E9%A0%93%E6%88%BF%E5%83%B9-9c535fb7ceb7)
## 練習Dataset
```python=
from sklearn.datasets import fetch_openml
boston = fetch_openml(name='boston', version=1, as_frame=True)
df = boston.frame
```
### Boston Dataset 欄位說明
| 欄位名 | 欄位描述 | 精簡說明 |
| ---------- | -------------------------|---|
| `CRIM` | per capita crime rate by town | 犯罪率 |
| `ZN` | proportion of residential land zoned for lots over 25,000 sq.ft. | 住宅大地比例 |
| `INDUS` | proportion of non-retail business acres per town | 非零售工業用地比例 |
| `CHAS` | Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) | 是否鄰近查爾斯河 |
| `NOX` | nitric oxides concentration (parts per 10 million) | 氮氧化物濃度 |
| `RM` | average number of rooms per dwelling |每戶平均房間數|
| `AGE` | proportion of owner-occupied units built prior to 1940 |老屋比例(1940年前建)|
| `DIS` | weighted distances to five Boston employment centres | 距離市中心加權距離|
| `RAD` | index of accessibility to radial highways | 高速公路通達指數|
| `TAX` | full-value property-tax rate per $10,000 | 房屋稅率(每萬元)|
| `PTRATIO` | pupil-teacher ratio by town | 師生比例|
| `B` | 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town |黑人比例|
| `LSTAT` | % lower status of the population | 低收入人口比例|
| `**MEDV**` | Median value of owner-occupied homes in $1000's | 房價中位數(千美元)|
## 課堂投影片與練習
- [ ] **教學目標**
* 了解資料前處理在機器學習流程中的重要性。
* 掌握處理遺失值、異常值和重複資料的方法。
* 學習資料轉換(如標準化、類別編碼)的技巧。
* 認識基本的資料分析方法,如敘述性統計。
* 了解特徵選取的基本概念和方法。
* 理解為何需要切割資料集以及如何進行。
---
- [ ] **為什麼需要資料前處理?**
* 真實世界的資料往往是「髒」的:
* 可能包含**遺失值** (Missing Values):某些資料點的值是空白或未知。
* 可能包含**異常值** (Outliers):表現明顯與其他資料不一樣的資料點。
* 可能包含**雜訊** (Noise):量測變數中的隨機錯誤或偏差。
* 可能包含**不正確**或**不一致**的資料。
* 可能包含**重複**或**冗餘**的資料。
* 資料的**尺度**不一致,或**格式**不符合模型的要求。
* 不進行前處理,可能會導致:
* 模型訓練困難或失敗。
* 模型效能不佳、不準確。
* 對結果做出錯誤解讀。
* 資料前處理的目標是將原始資料轉換成適合資料探勘或機器學習模型可以處理的格式。
---
- [ ] **資料分析流程概述**
* 機器學習模型的資料分析流程通常包含以下步驟:
1. **載入資料**。
2. **探索資料**:了解資料的結構和基本分佈。
3. **資料前處理**:清理、轉換、整合資料。

4. **特徵選取**:從眾多特徵中選擇與目標最相關的。
5. **切割資料集**:將資料分為訓練集和測試集。
6. **建制模型**:選擇並訓練機器學習模型。
7. **衡量績效**:評估模型的效能。
* 本單元我們將聚焦於流程中的步驟 1 到 5。
---
- [ ] **載入與探索資料 (使用 Pandas 與 scikit-learn)**
* 我們常用 Pandas DataFrame 來處理結構化的資料。
* **載入 CSV 檔案:** 使用 `pd.read_csv()`。
* **載入 scikit-learn 內建資料集:** scikit-learn 提供了一些範例資料集 (如波士頓房價資料集),這些資料集載入後通常是 dictionary 格式。
* 可以透過 `.keys()` 查看包含哪些內容 (如 data, target, feature_names, description)。
* 需要將其轉換為 DataFrame 格式以便操作。
```python=
# 載入必要的套件
import pandas as pd
from sklearn.datasets import fetch_openml
boston = fetch_openml(name='boston', version=1, as_frame=True)
df = boston.frame
df['target'] = boston.target # 加入目標變數
```
---
- [ ] **課堂練習: 探索 DataFrame(上一周上課內容複習)**
* 查看前/後幾筆資料:`df.head()` / `df.tail()` (預設顯示前/後 5 筆,可指定數字)。
* 查看資料的維度 (形狀):`df.shape` (回傳 (列數, 欄位數))。
* 查看資料的基本資訊 (欄位數、資料筆數、是否有缺失值、資料型態):`df.info()`。
* 查看數值欄位的敘述性統計 (平均值、標準差、最小值、最大值、四分位數等):`df.describe()`。
* 查看類別欄位中各類別的出現次數:`df['欄位名稱'].value_counts()`.
```python=
# 查看前三筆資料
df.head(3)
# 查看末三筆資料
df.tail(3)
# 查看資料集大小
df.shape
# 查看資料集的基本資訊
df.info()
# 查看數值欄位的敘述性統計
df.describe()
# 查看類別欄位 'MEDV' 中各類別的出現次數
df['MEDV'].value_counts()
```
---
- [ ] **資料清理 - 處理遺失值 (Missing Values)**
* **如何偵測遺失值?**
* 使用 `df.info()` 可以快速查看每個欄位非空值的數量,從而推算遺失值.
* 使用 `df.isnull()` 或 `df.isna()`:回傳一個與 DataFrame 形狀相同的 boolean DataFrame,True 表示該位置是遺失值 (NaN),False 表示非遺失值.
* 搭配 `.sum()`:`df.isnull().sum()` 可以快速計算每個欄位的遺失值數量.
* 搭配 `.any()`:`df.isnull().any()` 可以快速判斷哪些欄位包含遺失值.
```python=
# 假設使用 NullPractive.csv 資料集,因為波士頓房價資料集沒有遺失值
weather_df = pd.read_csv('NullPractive.csv')
# 偵測每個欄位的遺失值數量
print(weather_df.isnull().sum())
# 判斷哪些欄位有遺失值
print(weather_df.isnull().any())
```
---
- [ ] **如何處理遺失值?**
1. **忽略/刪除 (Drop NA):** 刪除包含遺失值的列或欄.
* `df.dropna()`:刪除任何包含一個或多個遺失值的列 (預設 `axis=0`).
* `df.dropna(axis=1)`:刪除任何包含一個或多個遺失值的欄.
* `df.dropna(thresh=N)`:刪除遺失值數量超過 N 的列.
* 注意:如果遺失值太多 (例如超過 30%),直接刪除可能會損失過多資料.
---
- [ ] **課堂練習:刪除缺失值**
* **任務:** 創建一個新的 DataFrame,其中包含幾列和幾行,並手動將一些值設為 `np.nan`。然後嘗試使用 `df.dropna()` 刪除包含遺失值的列。觀察刪除前後 DataFrame 的形狀 (`.shape`) 變化。
```python=
import pandas as pd
import numpy as np
# 創建範例 DataFrame
data = {
'col1': [1, 2, np.nan, 4],
'col2': [5, np.nan, np.nan, 8],
'col3': [9, 10, 11, 12] # 補上 col3 的資料
}
test_df = pd.DataFrame(data)
# 觀察原始 DataFrame
print("原始 DataFrame:\n", test_df)
print("原始形狀:", test_df.shape)
# 處理缺失值後再次觀察
test_df_dropped = test_df.dropna() # 刪除包含 NaN 的列
print("\n刪除遺失值後的 DataFrame:\n", test_df_dropped)
print("刪除遺失值後的形狀:", test_df_dropped.shape)
```
---
- [ ] **如何處理遺失值?**
2. **填補 (Fill NA):** 用某些值來取代遺失值.
* `df.fillna(value)`:用指定的值填補 (如 `df.fillna(0)` 用 0 填補).
* 用統計量填補 (需確保欄位為數值型態):
* `df.fillna(df.mean())`:用該欄位的平均值填補.
* `df.fillna(df.median())`:用該欄位的中位數填補.
* `df.fillna(df.mode())`:用該欄位的眾數填補.
* 使用內插法 (Interpolation).
* 使用向前填補 (`method='ffill'`) 或向後填補 (`method='bfill'`).
---
- [ ] **課堂練習: 填補缺失值**
```python=
import pandas as pd
import numpy as np
# 建立模擬資料
data = {
'CRIM': [0.1, np.nan, 0.3, 0.2],
'RM': [6.5, 7.1, np.nan, 5.9],
'AGE': [65, 72, np.nan, 65]
}
df = pd.DataFrame(data)
print("原始資料:")
print(df)
# 平均值填補 CRIM 欄位
df['CRIM'] = df['CRIM'].fillna(df['CRIM'].mean())
# 中位數填補 RM 欄位
df['RM'] = df['RM'].fillna(df['RM'].median())
# 眾數填補 AGE 欄位
df['AGE'] = df['AGE'].fillna(df['AGE'].mode()[0])
print("\n填補後的資料:")
print(df)
```
* 執行結果會顯示填補前後的差異,有助於學生理解各種方法的實際效果。
---
- [ ] **資料清理 - 處理異常值 (Outliers)**
* **什麼是異常值?** 表現明顯與其他資料不一樣的資料點.
* **為何需要處理異常值?** 異常值可能會對模型的訓練結果產生不良影響,特別是一些對極端值敏感的模型.
* **如何偵測異常值?**
1. **視覺化方法:** 使用**盒鬚圖 (Box Plot)** 可以很明顯地看出資料分佈以及潛在的異常值(圖形外的點). (盒鬚圖的繪製將在資料視覺化單元介紹)
2. **統計方法:**
* **四分位距 (InterQuartile Range, IQR) 方法:**
* 計算第一四分位數 (Q1) 和第三四分位數 (Q3).
* IQR = Q3 - Q1.
* 設定上下限:上限 = Q3 + 1.5 * IQR,下限 = Q1 - 1.5 * IQR.
* 超出上下限的資料點被視為異常值.
* 這種方法對極端值比較魯棒.
* **三個標準差原則 (Z-score 方法):**
* 計算平均數 (mean) 和標準差 (std).
* 設定上下限:上限 = mean + 3 * std,下限 = mean - 3 * std.
* 超出上下限的資料點被視為異常值.
* 適用於分佈大致對稱的資料.
---
**課堂範例: 處理異常值(接續波士頓房價資料集)**
```python=
# 使用 IQR 方法偵測異常值 (以波士頓房價資料集的 'CRIM' 欄位為例)
# 首先使用 describe() 獲取四分位數
desc = df['CRIM'].describe()
Q1 = desc['25%']
Q3 = desc['75%']
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 找出異常值
outliers = df[(df['CRIM'] < lower_bound) | (df['CRIM'] > upper_bound)]
print("CRIM 欄位的異常值數量:", len(outliers))
```
---
- [ ] **如何處理異常值?**
1. **刪除:** 如果異常值數量很少且是明顯的錯誤,可以直接刪除包含異常值的資料點.
2. **取代:** 用平均值、中位數或其他合理的值來取代異常值.
3. **不處理:** 有時異常值反映了資料的真實特性,如果數量不多且對模型影響不大,可以選擇不處理.
---
**課堂練習:處理異常值(接續波士頓房價資料集)**
1. **任務:** 繼續使用波士頓房價資料集 (df),偵測其他欄位(例如:'TAX' 或 'B')的異常值。
2. **任務:** 使用四分位距 (IQR) 方法,計算該欄位的異常值上下限。
3. **任務:** 找出並列出該欄位中被判定為異常值的資料點數量。
* 以下為範例程式碼
```python=
# 任務 1 & 2
selected_column = 'TAX' # 或其他欄位
col_desc = df[selected_column].describe() #
Q1 = col_desc['25%']
Q3 = col_desc['75%']
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f"{selected_column} 欄位的 IQR 上限: {upper_bound}, 下限: {lower_bound}\n")
# 任務 3
outliers = df[(df[selected_column] < lower_bound) | (df[selected_column] > upper_bound)] #
print(f"{selected_column} 欄位的異常值數量: {len(outliers)}\n")
print("部分異常值資料點:\n", outliers.head())
```
---
- [ ] **資料清理 - 重複資料**
* **什麼是重複資料?** 資料集中存在內容完全相同或高度相似的資料記錄.
* **如何偵測重複資料?**
* `DataFrame.duplicated()`:回傳一個 Series,標示每一列是否為重複(基於所有欄位)。第一次出現的標示為 False,後續重複的標示為 True.
* 可以指定 `subset` 參數,只檢查特定欄位是否重複.
* **如何移除重複資料?**
* `DataFrame.drop_duplicates()`:移除重複的列.
* 預設會保留第一次出現的重複列 (`keep='first'`),可以設定 `keep='last'` 保留最後一次出現的,或 `keep=False` 刪除所有重複的列.
* 可以指定 `subset` 參數,只根據特定欄位來判斷是否重複.
---
- [ ] **課堂範例: 偵測並移除重複資料**
```python=
# 建立含重複資料的範例
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'Bob', 'Alice', 'Eve'],
'Score': [90, 85, 78, 85, 90, 95]
}
df = pd.DataFrame(data)
print("原始資料:")
print(df)
# 偵測重複資料
print("重複資料筆數:", df.duplicated().sum())
print("重複資料:")
print(df[df.duplicated()])
# 移除重複資料(保留第一次出現)
df_cleaned = df.drop_duplicates()
print("移除重複後的資料:")
print(df_cleaned)
```
---
## 下一堂課
- [ ] **資料轉換 - 標準化 (Normalization/Standardization)**
* **為什麼需要標準化?**
* 不同的特徵可能有非常不同的數值範圍或尺度 (例如:年齡 0-100,年薪 0-千萬).
* 某些機器學習模型 (如 KNN, SVM, 回歸模型、主成分分析、分群) 對特徵的尺度很敏感.
* 標準化可以避免數值較大的特徵在模型中佔據過高的權重,確保所有特徵在相同的尺度上,有助於模型更好地學習.
* **常用的標準化方法:**
1. **變異數標準化 (Z-score Standardization):**
* 將資料轉換為平均數為 0,標準差為 1 的分佈.
* 公式:Z = (X - mean) / std.
* 適用於資料分佈大致對稱的情況.
* 可以降低離群值對模型影響.
* 使用 Scikit-learn 的 `StandardScaler` 實現.
2. **最小-最大標準化 (Min-Max Normalization):**
* 將資料等比例縮放到一個固定的範圍,通常是.
* 公式:X_scaled = (X - min) / (max - min).
* 不會改變原始資料的分佈形狀.
* 對極端值敏感,如果有極端值,大部分資料可能被壓縮到範圍的一小部分.
* 最大最小值未知或存在極端值時不適用.
* 使用 Scikit-learn 的 `MinMaxScaler` 實現.
---
**課堂範例: 標準化(接續波士頓房價資料)**
```python=
# 使用 Scikit-learn 進行標準化範例 (以波士頓房價資料集的 'DIS' 欄位為例)
# 注意:標準化通常是對特徵 (X) 進行,不對目標變數 (Y) 進行
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 變異數標準化 (Z-score)
scaler_z = StandardScaler()
df['DIS_z'] = scaler_z.fit_transform(df[['DIS']]) # fit_transform 會 fit 然後 transform
# 最小-最大標準化 (Min-Max)
scaler_mm = MinMaxScaler()
df['DIS_mm'] = scaler_mm.fit_transform(df[['DIS']]) # fit_transform 會 fit 然後 transform
print(df[['DIS', 'DIS_z', 'DIS_mm']].head())
print(df[['DIS_z', 'DIS_mm']].describe()) # 查看標準化後的統計量
```
* **何時需要標準化?** KNN, SVM, 回歸模型等.
* **何時不需要標準化?** 樹模型 (決策樹、隨機森林等), 邏輯斯迴歸.
---
- [ ] **課堂練習:資料轉換(接續波士頓房價資料集)**
1. **任務:** 繼續使用波士頓房價資料集 (df)。選擇一個數值欄位(例如:'LSTAT')。嘗試分別對其應用變異數標準化 (`StandardScaler`) 和最小-最大標準化 (`MinMaxScaler`)。
2. **任務:** 觀察兩種標準化方法後,新欄位的 `.describe()` 結果有何不同。
* 以下為範例程式碼
```python=
# 任務 1 & 2
from sklearn.preprocessing import StandardScaler, MinMaxScaler #
selected_column = 'LSTAT'
scaler_z = StandardScaler()
df[f'{selected_column}_z'] = scaler_z.fit_transform(df[[selected_column]])
scaler_mm = MinMaxScaler()
df[f'{selected_column}_mm'] = scaler_mm.fit_transform(df[[selected_column]])
print(f"{selected_column} 欄位標準化後的敘述性統計:\n")
print("Z-score:\n", df[f'{selected_column}_z'].describe())
print("\nMin-Max:\n", df[f'{selected_column}_mm'].describe())
```
---
- [ ] **資料轉換 - 類別變數編碼 (Categorical Encoding)**
* **為什麼需要編碼?**
* 機器學習模型通常只能處理數值型資料,無法直接處理文字或類別資料.
* 需要將類別資料轉換為數值格式.
* **處理不同尺度的類別變數:**
1. **順序尺度 (Ordinal Scale):** 類別之間有明確的大小或順序關係 (如:滿意度 1-5 等級,產品品質等級 S/M/L).
* 可以直接對應成數值,保留其順序關係.
* 使用 Pandas 的 `.map()` 方法或替換功能實現.
2. **名目尺度 (Nominal Scale):** 類別之間沒有大小或順序關係,僅作為區分 (如:性別 男/女,顏色 紅/藍/綠).
* 不能直接對應成連續數值,否則模型會誤解其有順序關係.
* 常用方法是 **One-hot Encoding**.
* 將一個有 k 個類別的欄位展開為 k 個新的欄位.
* 每個新的欄位對應一個類別,該列屬於該類別則為 1,否則為 0.
* 使用 Pandas 的 `pd.get_dummies()` 函數或 Scikit-learn 的 `OneHotEncoder` 實現.
* 注意:類別種類太多時,One-hot Encoding 可能產生過多特徵,導致「維數災難」.
---
- [ ] **課堂範例: 類別變數編碼**
```python=
# 順序尺度編碼範例 (假設有一個 'size' 欄位: S, M, L)
data_size = {'size': ['S', 'M', 'L', 'S', 'M', 'L']}
df_size = pd.DataFrame(data_size)
size_mapping = {'S': 1, 'M': 2, 'L': 3}
df_size['size_encoded'] = df_size['size'].map(size_mapping)
print(df_size)
# 名目尺度 One-hot Encoding 範例 (假設有一個 'color' 欄位: Red, Blue, Green)
data_color = {'color': ['Red', 'Blue', 'Green', 'Red', 'Green']}
df_color = pd.DataFrame(data_color)
df_color_encoded = pd.get_dummies(df_color['color'])
print(df_color_encoded)
# 將 One-hot Encoding 結果與原 DataFrame 合併
df_combined = pd.concat([df_color, df_color_encoded], axis=1)
print(df_combined)
```
---
- [ ] **特徵選取 (Feature Selection)**
* **什麼是特徵選取?** 從原始特徵集合中選擇出對模型目標最有預測能力的子集.
* **為什麼需要特徵選取?**
* 減少模型的複雜度,避免過度擬合 (Overfitting).
* 提高模型的訓練速度和效率.
* 剔除不相關或冗餘的特徵.
* **常用的特徵選取方法:**
* **基於過濾器 (Filter Methods):** 根據特徵本身的特性或特徵與目標變數之間的關聯性來評估,與模型無關.
* 移除低變異數特徵 (Variance Threshold).
* 使用相關係數 (Correlation Coefficient) 評估特徵與目標或特徵之間的關聯性. (皮爾森相關係數 Pearson Correlation).
* **基於包裝器 (Wrapper Methods):** 使用特定的機器學習模型來評估特徵子集的效能.
* 向前法 (Forward Selection):從空集合開始,逐步加入能提升模型效能的特徵.
* 向後法 (Backward Elimination):從所有特徵開始,逐步移除移除後能提升模型效能的特徵.
* 捷徑式特徵選取法 (Stepwise Selection): 結合向前和向後法.
* **基於嵌入式 (Embedded Methods):** 在模型訓練過程中自動進行特徵選取 (如:Lasso, Ridge 回歸).
---
- [ ] **特徵選取 (Feature Selection)**
* **使用相關係數進行特徵選取範例 (波士頓房價資料集):**
* `df.corr()`:計算 DataFrame 中所有數值欄位之間的皮爾森相關係數矩陣.
* 可以檢視特徵之間的高度相關性 (冗餘特徵),或特徵與目標變數的相關性.
```python=
# 計算相關係數矩陣並查看與目標變數的相關性
correlation_matrix = df.corr()
print("特徵與目標變數的相關係數:\n", correlation_matrix['target'].sort_values(ascending=False))
# 可以考慮移除與目標變數相關性低的特徵,或特徵之間相關性高的特徵 (擇一保留)
```
---
- [ ] **資料切割 (Data Splitting)**
* **為什麼需要切割資料集?**
* 為了評估模型在「新數據」上的真實表現.
* 避免模型在訓練資料上表現很好 (過度擬合),但在未見過的測試資料上表現很差.
* 將資料分為**訓練集 (Training Set)** 和**測試集 (Testing Set)**.
* 訓練集用於訓練模型。
* 測試集用於在模型訓練完成後,獨立評估模型的效能.
* **如何切割資料集?**
* 使用 Scikit-learn 的 `train_test_split` 函數.
* 可以設定測試集佔總資料的比例 (如 0.2, 0.3, 或 0.337).
* 對於分類任務,可以使用 `stratify` 參數確保訓練集和測試集中的類別分佈與原始資料相似.
---
- [ ] **課堂示範 & 練習: 資料切割 (Data Splitting)**
```python=
# 切割資料集範例 (波士頓房價資料集)
from sklearn.model_selection import train_test_split
# 將特徵 (X) 和目標變數 (Y) 分開
# 在進行資料前處理時,通常只對特徵 (X) 進行處理,目標變數 (Y) 不處理標準化等
X = df.drop('target', axis=1) # 移除 target 欄位作為特徵
Y = df['target'] # target 欄位作為目標變數
將資料分割為訓練集和測試集,測試集佔 33%
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.33, random_state=42) # random_state 確保每次分割結果相同
# 學生自行練習: 查看分割後的資料形狀
print("原始資料形狀:", df.shape)
print("訓練集特徵形狀:", X_train.shape)
print("測試集特徵形狀:", X_test.shape)
print("訓練集目標形狀:", Y_train.shape)
print("測試集目標形狀:", Y_test.shape)
```
---
- [ ] **單元總結**
* 本單元我們學習了資料前處理與分析的核心步驟:
* 了解資料分析的基本流程.
* 如何載入和初步探索資料 (使用 Pandas).
* 處理資料中的遺失值 (偵測與填補/刪除).
* 偵測資料中的異常值 (IQR, Z-score 方法).
* 處理重複資料 (偵測與移除).
* 進行資料轉換 (標準化 Z-score/Min-Max, 類別編碼 One-hot Encoding).
* 認識特徵選取的基本方法.
* 如何將資料集切割為訓練集和測試集.
* 這些步驟是構建一個成功機器學習模型的基礎.
* 下一個單元,我們將學習如何利用資料視覺化來更好地理解資料和呈現分析結果.