返回目錄
A
數據科學全能指南:從數據到洞察 - 第 4 章
第四章:探索性資料分析(EDA)
發布於 2026-02-22 23:00
# 第四章:探索性資料分析(EDA)
> 探索性資料分析(Exploratory Data Analysis, EDA)是資料科學流程中**首要**且**持續**的步驟。它不僅能幫你快速了解資料的結構、分布與潛在問題,更能為後續特徵工程、模型選擇與假設檢定提供指引。
## 目錄
1. [為何需要 EDA?](#為何需要-eda)
2. [EDA 的工作流程](#eda-的工作流程)
3. [統計摘要與描述性統計](#統計摘要與描述性統計)
4. [資料視覺化技巧](#資料視覺化技巧)
5. [相關性與共變分析](#相關性與共變分析)
6. [離群值與分佈偏差](#離群值與分佈偏差)
7. [假設檢定與統計檢驗](#假設檢定與統計檢驗)
8. [高維資料的降維與視覺化](#高維資料的降維與視覺化)
9. [實務工作流程範例:Titanic 乘客資料](#實務工作流程範例titanic-乘客資料)
10. [最佳實踐與常見陷阱](#最佳實踐與常見陷阱)
11. [常用工具與函式庫](#常用工具與函式庫)
---
## 為何需要 EDA?
| 目的 | 為什麼重要 | 典型工具 |
|------|------------|----------|
| **確認資料完整度** | 缺失值、重複行、資料型別錯誤都會影響模型 | `pandas.isnull()`, `DataFrame.duplicated()` |
| **洞悉分佈** | 了解變數是否偏態或多峰,決定是否需要轉換 | 直方圖、核密度估計 (KDE) |
| **偵測離群值** | 離群值可能扭曲統計量或導致模型過擬合 | IQR、Z‑score、箱型圖 |
| **發現相關性** | 變數間的相互關係可啟發特徵工程 | 相關係數矩陣、熱力圖 |
| **生成假設** | 為後續假設檢定與建模提供基礎 | t‑test、ANOVA、Chi‑square |
> **結論**:EDA 不是「可有可無」的步驟,而是每一次資料科學專案中不可或缺的前置檢查,確保後續工作基於可靠且理解透徹的資料。
## EDA 的工作流程
1. **資料載入**:使用 `pandas.read_csv()` / `read_sql()` 等方式。
2. **基本統計摘要**:`DataFrame.describe()`、`DataFrame.info()`。
3. **缺失值處理**:視情況填補或刪除。
4. **型別轉換**:確保數值型、類別型、時間序列型正確。
5. **視覺化**:直方圖、箱型圖、散佈圖、熱力圖等。
6. **相關性分析**:計算相關係數、進行假設檢定。
7. **離群值偵測**:使用 IQR、Z‑score 或更進階的聚類方法。
8. **統計檢定**:t‑test、ANOVA、非參數檢定等。
9. **文件化結果**:將所有發現記錄於 Jupyter Notebook / 文檔。
10. **重複迭代**:隨著資料更新與問題調整,回到相應步驟。
## 統計摘要與描述性統計
python
import pandas as pd
df = pd.read_csv('data/raw/titanic.csv')
# 基本資料資訊
print(df.info())
# 描述性統計(數值型)
print(df.describe(include='all'))
> **提示**:`include='all'` 可同時顯示類別型資料的頻數。若資料量極大,考慮只取前 1000 行進行摘要。
### 典型統計量
- **平均值**(Mean)
- **中位數**(Median)
- **眾數**(Mode)
- **範圍**(Range)
- **標準差**(Standard Deviation)
- **變異係數**(Coefficient of Variation)
- **四分位數**(Quartiles)
## 資料視覺化技巧
| 視覺化 | 目的 | 典型工具 |
|------|------|----------|
| 直方圖 | 變數分佈 | `sns.histplot`, `plt.hist` |
| KDE | 平滑分佈 | `sns.kdeplot` |
| 箱型圖 | 檢測離群值 | `sns.boxplot` |
| 散佈圖 | 兩變數關係 | `sns.scatterplot` |
| 熱力圖 | 相關性 | `sns.heatmap` |
| Pairplot | 多變數關係 | `sns.pairplot` |
python
import seaborn as sns
import matplotlib.pyplot as plt
# 直方圖 + KDE
sns.histplot(df['Age'].dropna(), kde=True, bins=30)
plt.title('Age Distribution')
plt.show()
# 箱型圖
sns.boxplot(x='Sex', y='Fare', data=df)
plt.title('Fare by Sex')
plt.show()
### 重要注意
- **時間序列資料**:使用 `plot_date` 或 `tsplot`,並考慮季節性。
- **高維資料**:使用 `pairplot` 或 `scatter_matrix`,但避免在 > 10 個變數時過度渲染。
## 相關性與共變分析
### 皮爾遜相關係數
python
corr_matrix = df.corr(method='pearson')
plt.figure(figsize=(10,8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('Pearson Correlation Heatmap')
plt.show()
### 斯皮爾曼等級相關係數(對非線性關係友善)
python
corr_spearman = df.corr(method='spearman')
> **小技巧**:若資料中含有類別型欄位,可先使用 `pd.get_dummies()` 轉換,再進行相關性分析。
## 離群值與分佈偏差
### IQR 方法
python
Q1 = df['Fare'].quantile(0.25)
Q3 = df['Fare'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['Fare'] < lower) | (df['Fare'] > upper)]
print(outliers)
### Z‑score 方法
python
from scipy import stats
df['Fare_z'] = stats.zscore(df['Fare'].dropna())
print(df[df['Fare_z'].abs() > 3])
### 可視化
- **箱型圖**:直觀顯示離群值。
- **點圖**:將離群值標記為紅點,便於快速定位。
## 假設檢定與統計檢驗
| 檢定 | 適用場景 | 典型工具 |
|------|----------|----------|
| t‑test(獨立樣本) | 比較兩組平均值 | `scipy.stats.ttest_ind` |
| ANOVA | 多組平均值差異 | `scipy.stats.f_oneway` |
| Chi‑square | 類別資料的獨立性 | `scipy.stats.chi2_contingency` |
| Mann‑Whitney U | 非參數兩組比較 | `scipy.stats.mannwhitneyu` |
python
# t‑test: Age 之間的差異是否顯著?
from scipy.stats import ttest_ind
male_age = df[df['Sex']=='male']['Age'].dropna()
female_age = df[df['Sex']=='female']['Age'].dropna()
print(ttest_ind(male_age, female_age))
> **注意**:在進行假設檢定前,確認變數符合檢定的假設(如正態分佈、等方差)。若不符,可採用非參數檢定。
## 高維資料的降維與視覺化
### 主成分分析(PCA)
python
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
numeric_cols = df.select_dtypes(include=['float64','int64']).columns
X = df[numeric_cols].dropna()
X_std = StandardScaler().fit_transform(X)
pca = PCA(n_components=2)
principal_components = pca.fit_transform(X_std)
pc_df = pd.DataFrame(data=principal_components, columns=['PC1', 'PC2'])
### t‑SNE(保留局部結構)
python
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, random_state=42)
X_tsne = tsne.fit_transform(X_std)
plt.scatter(X_tsne[:,0], X_tsne[:,1], alpha=0.5)
plt.title('t‑SNE of Titanic Features')
plt.show()
> **備註**:PCA 主要用於**數值型**資料,t‑SNE 則更適合 **非線性** 高維結構。
## 實務工作流程範例:Titanic 乘客資料
> 我們將使用 Titanic 乘客資料示範完整的 EDA 步驟,並將結果儲存於 Notebook。
### 1. 資料載入
python
import pandas as pd
df = pd.read_csv('data/raw/titanic.csv')
### 2. 基本統計摘要
python
print(df.info())
print(df.describe(include='all'))
### 3. 缺失值視覺化
python
import seaborn as sns
import matplotlib.pyplot as plt
sns.heatmap(df.isnull(), cbar=False, yticklabels=False, cmap='viridis')
plt.title('Missing Values Heatmap')
plt.show()
### 4. 分佈與離群值
python
# Age KDE
sns.histplot(df['Age'].dropna(), kde=True, bins=30)
plt.title('Age Distribution')
plt.show()
# Fare 箱型圖
sns.boxplot(y='Fare', data=df)
plt.title('Fare Boxplot')
plt.show()
### 5. 相關性分析
python
corr = df.corr(method='pearson')
plt.figure(figsize=(10,8))
sns.heatmap(corr, annot=True, cmap='Blues', fmt='.2f')
plt.title('Correlation Matrix')
plt.show()
### 6. 離群值偵測
python
Q1 = df['Fare'].quantile(0.25)
Q3 = df['Fare'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['Fare'] < lower) | (df['Fare'] > upper)]
print('Fare 離群值數量:', outliers.shape[0])
### 7. 假設檢定
python
from scipy.stats import ttest_ind
male_fare = df[df['Sex']=='male']['Fare'].dropna()
female_fare = df[df['Sex']=='female']['Fare'].dropna()
print(ttest_ind(male_fare, female_fare))
### 8. 文件化
> 在 Jupyter Notebook 中,將所有 **圖表** 與 **統計量** 插入 Markdown 儲存,並撰寫 **摘要段落**,以供團隊分享與決策。
## 最佳實踐與常見陷阱
| 實踐 | 說明 |
|------|------|
| **版本控制** | 使用 Git 追蹤 Notebook、CSV 與腳本 | `git add`, `git commit` |
| **隨機種子** | 確保圖表與統計結果可重現 | `np.random.seed(42)` |
| **資料分割** | 先對完整資料做 EDA,再進行資料切分 | `train_test_split()` 前不做隨機選樣 |
| **記錄發現** | 用 Markdown 標註關鍵發現與假設 | `# EDA: Age 低於 0 的觀測值` |
| **避免資訊洩漏** | 在 EDA 期間不使用測試集資訊 | `df.sample()` 只用訓練集 |
| **關注類別不平衡** | 可透過 `value_counts()` 與 `barplot` | |
| **處理缺失值策略** | 根據變數類型決定填補方式 | `df['Age'].fillna(df['Age'].median())` |
> **常見陷阱**:
> - **過度依賴自動生成圖表**:雖然 `sns.pairplot` 方便,但對大規模資料會耗時且視覺化過於擁擠。
> - **忽略時間序列型資料的季節性**:直接對時間欄位做描述性統計可能產生誤導。
> - **不恰當的離群值處理**:在某些場景下,離群值代表重要資訊,簡單刪除可能喪失關鍵特徵。
## 常用工具與函式庫
| 函式庫 | 主要功能 | 典型版本 |
|--------|----------|----------|
| **pandas** | 資料載入、清理、摘要 | 1.5+ |
| **seaborn** | 高階統計視覺化 | 0.11+ |
| **matplotlib** | 基礎繪圖與自訂 | 3.3+ |
| **statsmodels** | 迴歸、假設檢定 | 0.12+ |
| **scipy** | 統計檢定、離群值 | 1.7+ |
| **scikit‑learn** | 標準化、降維、模型評估 | 1.1+ |
| **plotly** | 互動式圖表 | 5+ |
---
> **延伸閱讀**:
> - *“Exploratory Data Analysis”* by John Tukey (1977)
> - *“Practical Statistics for Data Scientists”* by Peter Bruce & Andrew Bruce
> - Kaggle 資料科學筆記本(大量實戰案例)
> **結語**:透過 EDA,我們不僅能快速檢測資料問題,更能構築對資料的直覺。下一章將把這些洞察轉化為 **特徵工程** 的具體操作,為模型訓練奠定堅實基礎。