聊天視窗

數據洞見:從原始資料到商業決策 - 第 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),以挖掘資料中隱藏的洞見與商業機會。