返回目錄
A
數據洞見:從原始資料到商業決策 - 第 3 章
3. 資料蒐集與清理
發布於 2026-03-07 01:51
# 第三章 資料蒐集與清理
資料科學的成功往往取決於「資料品質」的高低。即使最先進的模型和算法,如果基礎資料存在缺失、異常或結構不一致,也會導致分析結果偏差甚至失效。本章將從資料來源說起,帶你了解 API、網路抓取、資料庫連接等取得方式;接著深入探討資料質量評估、缺失值處理、異常偵測與資料標準化等清理技巧,並以實作範例示範如何將理論落實於實務。最後列出常見陷阱與最佳實踐,確保你在資料處理過程中能維持高效與準確。
---
## 3.1 資料來源與取得方式
| 來源 | 優點 | 缺點 | 常見工具 |
|------|------|------|----------|
| API | 結構化、即時、易於自動化 | 需授權、可能限速 | `requests`, `httpx`, `urllib3` |
| 網路抓取 | 能抓取公開資料、彈性高 | 版權風險、HTML 結構變動 | `BeautifulSoup`, `lxml`, `Scrapy` |
| 資料庫 | 內部資料完整、關聯性高 | 需要權限、連線成本 | `pymysql`, `psycopg2`, `sqlalchemy` |
| 檔案系統 | 低成本、可離線處理 | 可能存放於不同格式 | `pandas.read_csv`, `pandas.read_excel`, `pyarrow` |
### 3.1.1 API 取得
> **實務提醒**:使用 RESTful API 時,請先閱讀「資料使用政策」與「速率限制」規範。
python
import requests
# 取得某零售商每日銷售 API
url = "https://api.retailer.com/v1/sales"
params = {
"date": "2024-02-01",
"store_id": 101
}
headers = {
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
response = requests.get(url, params=params, headers=headers, timeout=10)
if response.ok:
data = response.json()
print(data)
else:
print("Error", response.status_code)
### 3.1.2 網路抓取
> **常見陷阱**:動態載入頁面需使用 Selenium 或 Playwright。
python
from bs4 import BeautifulSoup
import requests
url = "https://www.example.com/products"
resp = requests.get(url, headers={"User-Agent": "Mozilla/5.0"})
bs = BeautifulSoup(resp.text, "lxml")
products = []
for card in bs.select("div.product-card"):
name = card.select_one("h2.name").text.strip()
price = card.select_one("span.price").text.strip()
products.append({"name": name, "price": price})
print(products[:3])
### 3.1.3 資料庫連接
python
import sqlalchemy as sa
engine = sa.create_engine("postgresql+psycopg2://user:pwd@host:5432/dbname")
query = "SELECT * FROM sales WHERE date >= '2024-01-01'"
sales_df = sa.read_sql(query, engine)
print(sales_df.head())
---
## 3.2 資料質量評估
在進行任何清理前,先對資料集做「質量盤點」:
1. **完整性**:檢查缺失值比例。若某欄位缺失率 > 50%,考慮刪除或尋找外部補齊來源。
2. **一致性**:確認欄位類型、命名、時間格式是否統一。
3. **合理性**:檢查邏輯邊界,例如銷售額不可為負數。
4. **重複性**:刪除完全相同的重複紀錄。
python
import pandas as pd
df = pd.read_csv("sales_raw.csv")
# 1. 缺失值統計
missing = df.isnull().mean().sort_values(ascending=False)
print("缺失比例:\n", missing)
# 2. 資料型別檢查
print("資料型別:\n", df.dtypes)
# 3. 合理性檢查
print("負銷售額筆數", (df['sales'] < 0).sum())
# 4. 重複檢查
print("重複筆數", df.duplicated().sum())
---
## 3.3 缺失值處理
| 方法 | 適用情境 | 典型函式 |
|------|----------|----------|
| 刪除 | 缺失比例低、行/列重要度不高 | `df.dropna()` |
| 代入 | 欄位為數值、缺失分佈均勻 | `df.fillna(df.mean())` |
| 迴歸代入 | 欄位與其他變數高度相關 | `sklearn.linear_model.LinearRegression()` |
| 多重插補 | 高維資料、缺失比例中等 | `fancyimpute.IterativeImputer` |
### 3.3.1 代入示範
python
# 以中位數代入缺失
median_sales = df['sales'].median()
df['sales'].fillna(median_sales, inplace=True)
### 3.3.2 多重插補示範(簡化)
python
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer = IterativeImputer(random_state=0)
cols_to_impute = ['sales', 'price', 'quantity']
df[cols_to_impute] = imputer.fit_transform(df[cols_to_impute])
---
## 3.4 異常偵測
> **異常值**:不符合資料分布的極端數值。若不處理,會嚴重影響模型參數估計。
### 3.4.1 盒鬚圖法
python
import matplotlib.pyplot as plt
plt.boxplot(df['sales'])
plt.title('銷售額盒鬚圖')
plt.show()
### 3.4.2 IQR 法
python
Q1 = df['sales'].quantile(0.25)
Q3 = df['sales'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['sales'] < lower) | (df['sales'] > upper)]
print("異常筆數", outliers.shape[0])
### 3.4.3 離群點機器學習法(Isolation Forest)
python
from sklearn.ensemble import IsolationForest
iso = IsolationForest(contamination=0.02, random_state=0)
pred = iso.fit_predict(df[['sales', 'price']])
# -1 表示異常
df['anomaly'] = pred
print("檢測到異常筆數", (df['anomaly'] == -1).sum())
---
## 3.5 資料標準化與編碼
| 需求 | 典型方法 | 典型函式 |
|------|----------|----------|
| 數值型特徵 | 標準化(Z-score) | `StandardScaler()` |
| 數值型特徵 | 最小-最大縮放 | `MinMaxScaler()` |
| 分類型特徵 | One‑Hot 編碼 | `pd.get_dummies()` |
| 分類型特徵 | 標籤編碼 | `LabelEncoder()` |
### 3.5.1 標準化示範
python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
num_cols = ['sales', 'price', 'quantity']
df[num_cols] = scaler.fit_transform(df[num_cols])
### 3.5.2 One‑Hot 編碼示範
python
cat_cols = ['store_id', 'product_category']
df = pd.get_dummies(df, columns=cat_cols, drop_first=True)
---
## 3.6 實作範例:零售業每日銷售資料清理流程
以下示範如何從 API 取得資料、處理缺失值、偵測異常、並輸出乾淨的資料集。
python
import requests, pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
# 1. 取得資料
url = "https://api.retailer.com/v1/daily_sales"
params = {"start_date": "2024-01-01", "end_date": "2024-02-28"}
resp = requests.get(url, params=params, timeout=10)
raw_df = pd.DataFrame(resp.json())
# 2. 資料質量評估
print("原始資料形狀", raw_df.shape)
print(raw_df.isnull().mean().sort_values(ascending=False).head())
# 3. 缺失值處理
imputer = SimpleImputer(strategy='median')
raw_df[['sales', 'price']] = imputer.fit_transform(raw_df[['sales', 'price']])
# 4. 異常偵測(IQR)
Q1, Q3 = raw_df['sales'].quantile([0.25, 0.75])
IQR = Q3 - Q1
mask = (raw_df['sales'] >= Q1 - 1.5 * IQR) & (raw_df['sales'] <= Q3 + 1.5 * IQR)
clean_df = raw_df.loc[mask].reset_index(drop=True)
# 5. 標準化
scaler = StandardScaler()
clean_df[['sales', 'price']] = scaler.fit_transform(clean_df[['sales', 'price']])
# 6. 輸出乾淨資料
clean_df.to_csv("sales_clean.csv", index=False)
print("清理後資料形狀", clean_df.shape)
---
## 3.7 常見陷阱與最佳實踐
| 陷阱 | 影響 | 解決方案 |
|------|------|----------|
| **資料漂移(Data Drift)** | 訓練模型時使用的資料分布與實際輸入資料不一致 | 定期對資料分布做比對,必要時重新訓練或微調模型 |
| **缺失值處理過度** | 代入過度可能掩蓋真實訊息 | 只對缺失比例低於門檻的欄位進行代入,或採用模型預測代入 |
| **異常值過度刪除** | 刪除過多可能造成樣本量不足 | 先做敏感度分析,視業務情境決定是否保留或修正異常 |
| **資料型別錯誤** | 文字被誤判為數值,導致計算錯誤 | 使用 `pandas.api.types` 進行型別檢查與轉換 |
| **編碼不一致** | 兩個同一欄位在不同資料表中使用不同編碼 | 建立統一的編碼字典,並在合併前做映射 |
---
## 3.8 小結
資料蒐集與清理是資料科學流程的基石。透過 API、網路抓取或資料庫連接,你可以取得多樣化且時效性的資料;再利用缺失值處理、異常偵測與資料標準化等技巧,將原始資料轉化為「可供分析」的高品質資料集。這些步驟不僅能提升後續模型的準確度,也能降低資料不一致帶來的決策風險。接下來,我們將進一步探討探索性資料分析(EDA),以挖掘資料中隱藏的洞見與商業機會。