Day 22 資料探索實戰
引言
學會 Pandas、Matplotlib、Seaborn 之後,最關鍵的問題是:「拿到一份陌生的資料,該從哪裡下手?」這就是探索性資料分析(Exploratory Data Analysis,簡稱 EDA)的價值。它不是為了得到最終結論,而是要快速摸清資料的形狀、找出異常、提出假設。EDA 做得好,後續的模型設計、特徵工程才不會走偏方向。
今天會以鐵達尼號乘客資料集為例,從頭到尾走一遍完整的 EDA 流程。我們會先看資料規模與欄位型態,再用統計量與視覺化找出缺失值、異常值與重要特徵,最後把觀察整理成幾個可驗證的假設。學會這套流程,未來不管遇到哪一份新資料,你都能在短時間內掌握它的面貌。這篇會把前三天學的技巧一次用上,是一個很好的綜合練習。
步驟一:載入資料與初步檢視
拿到資料的第一步,永遠是先看它的規模、欄位、型別與前幾筆資料。Pandas 提供一組非常方便的函式:shape、info()、head()、describe(),善用它們可以省下大量摸索時間。這些工具能在幾秒鐘內告訴你「資料有多寬、有多深、有沒有缺漏」。
import pandas as pd
import seaborn as sns
df = sns.load_dataset("titanic")
print("資料形狀:", df.shape)
print()
print(df.info())
print()
print(df.head())
輸出(部分):
資料形狀:(891, 15)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 survived 891 non-null int64
1 pclass 891 non-null int64
2 sex 891 non-null category
3 age 714 non-null float64
4 sibsp 891 non-null int64
5 parch 891 non-null int64
6 fare 891 non-null float64
7 embarked 889 non-null category
8 class 891 non-null category
9 who 891 non-null category
10 adult_male 891 non-null bool
11 deck 203 non-null category
12 embark_town 889 non-null category
13 alive 891 non-null category
14 alone 891 non-null bool
dtypes: bool(2), category(7), float64(2), int64(4)
survived pclass sex age sibsp parch fare ... alone
0 0 3 male 22.0 1 0 7.2500 ... False
1 1 1 female 38.0 1 0 71.2833 ... False
2 1 3 female 26.0 0 0 7.9250 ... True
從 info() 就能立刻看出幾件事:總共有 891 筆、15 個欄位;其中 age 只有 714 筆不為空,代表有 177 筆缺失;deck 缺失更嚴重(只有 203 筆);資料型別包含整數、浮點數、類別(category)與布林值。這些訊息就是後續清理的依據。當一份真實資料有上千個欄位時,這種「先看骨架」的習慣尤其重要,盲目跑分析很容易卡在欄位命名不一致、單位混雜的問題上。
步驟二:缺失值與重複值檢查
資料品質是分析的基礎。第二步要量化每個欄位的缺失比例,並檢查是否有完全重複的列。這個步驟看起來平凡,但往往能提早發現資料蒐集流程的問題,例如某個欄位缺失率高得離譜、某個維度的資料完全沒被記錄下來。
missing = df.isnull().sum()
missing = missing[missing > 0].sort_values(ascending=False)
print("缺失值統計:")
print(missing)
duplicates = df.duplicated().sum()
print(f"\n重複列數:{duplicates}")
輸出:
缺失值統計:
deck 688
age 177
embark_town 2
embarked 2
dtype: int64
重複列數:107
deck 缺失了近 77%,實務上這個欄位幾乎無法使用;age 缺 20%,可以考慮用中位數填補;embarked 與 embark_town 只缺 2 筆,可以直接刪掉那幾列。至於 107 筆完全重複的列,比較可能是資料整理時的雜訊,可以視需求決定是否移除。這個階段也適合順手檢查每個欄位的「合理範圍」,例如票價是不是有負數、年齡是不是有超過 200 歲的怪值。
步驟三:單變數分佈
接著要看每個欄位的分佈。類別型欄位用 value_counts(),數值型欄位則用直方圖。了解每個變數的「典型值」與「離群值」是分析的基礎,因為許多統計方法都假設資料有一定的分佈形態。
import matplotlib.pyplot as plt
print("艙等分佈:")
print(df["pclass"].value_counts())
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.histplot(df["age"].dropna(), bins=30, kde=True, ax=axes[0])
axes[0].set_title("年齡分佈")
sns.histplot(df["fare"], bins=30, kde=True, ax=axes[1])
axes[1].set_title("票價分佈")
plt.show()
從輸出可以觀察到:
- 艙等(pclass):3 等艙最多(491 人),1 等艙最少(216 人)。
- 年齡(age):呈現右偏分佈,20–30 歲最多,少數 70 歲以上的長者屬於極端值。
- 票價(fare):分佈更為偏態,少數高票價把圖拉得很長,要考慮用對數尺度或只看 95 分位以下。
這些觀察會影響後續的視覺化策略,例如票價圖如果用對數軸會更清楚。當某個欄位的分佈極度偏態時,做統計檢定或建模時也應該考慮對數轉換,否則極端值會主導整個模型的行為。
步驟四:探索變數之間的關係
EDA 的重頭戲是找出變數之間的關係。對這份資料,最重要的問題是「哪些因素影響存活率?」我們可以從艙等、性別、年齡三個面向切入。雙變數分析是建立假設的關鍵階段,能告訴我們「接下來該深入看哪些欄位組合」。
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
sns.barplot(data=df, x="pclass", y="survived", ax=axes[0])
axes[0].set_title("各艙等存活率")
sns.barplot(data=df, x="sex", y="survived", ax=axes[1])
axes[1].set_title("不同性別存活率")
sns.boxplot(data=df, x="survived", y="age", ax=axes[2])
axes[2].set_title("存活者與罹難者年齡分佈")
plt.show()
輸出會看到三個關鍵結論:
- 艙等越高,存活率越高:1 等艙存活率約 63%,3 等艙只有 24%。
- 女性存活率遠高於男性:女性約 74%,男性約 19%,差距非常懸殊。
- 年齡中位數差距不大:但孩童(年齡較低)的存活率略高於平均。
這三個變數顯然都是重要的預測特徵,之後在 Day 38 的模型設計章節,會被納入特徵工程。值得注意的是,僅僅看雙變數關係可能會被「辛普森悖論」誤導——某些效應在合併資料時消失,但分組後又出現。所以下一步要做交叉分析。
步驟五:交叉分析與相關性
單變數與雙變數分析之後,更深一層是用交叉表與相關矩陣,看變數彼此之間的關聯強度。交叉表能把兩個類別變數的組合展開,看每個交叉組合的目標值分佈;相關矩陣則把多個數值變數的線性關係量化。
ct = pd.crosstab(df["pclass"], df["sex"], values=df["survived"], aggfunc="mean")
print("各艙等 × 性別 的存活率:")
print(ct.round(3))
numeric = df[["survived", "pclass", "age", "fare"]]
corr = numeric.corr()
fig, ax = plt.subplots(figsize=(6, 5))
sns.heatmap(corr, annot=True, fmt=".2f", cmap="coolwarm",
vmin=-1, vmax=1, ax=ax)
ax.set_title("相關矩陣")
plt.show()
輸出:
各艙等 × 性別 的存活率:
sex female male
pclass
1 0.968 0.340
2 0.921 0.146
3 0.500 0.135
交叉表顯示,1 等艙女性存活率高達 96.8%,3 等艙男性只剩 13.5%——艙等與性別的交互作用非常明顯。相關矩陣則進一步確認 pclass 與 fare 呈負相關(艙等數字越大、票價越低,這符合邏輯),survived 與 pclass 也呈負相關(艙等數字越大、存活率越低)。交叉分析的結論往往比雙變數分析更細緻,能避免「只看總體趨勢」的盲點。
步驟六:彙整觀察與下一步
完成上面的步驟,EDA 的探索階段就結束了。這時應該把觀察整理成幾個可驗證的假設,方便後續建模或進一步驗證。一份好的 EDA 筆記不只是一堆圖,而是要能讓團隊的人 30 秒內看懂「這份資料長什麼樣、有什麼該注意」。
- 假設 1:艙等是強預測因子——1 等艙存活率明顯高於 3 等艙。
- 假設 2:性別的影響大於艙等——同艙等下,女性存活率遠高於男性。
- 假設 3:年齡與存活的關係較弱,但仍值得納入模型。
- 假設 4:
deck缺失太多,不適合直接使用;可以考慮依艙等推估,或直接捨棄。
這些假設之後可以透過卡方檢定、邏輯斯迴歸等統計方法驗證。在 Day 38 設計模型時,這四個假設就會變成特徵工程的指引。EDA 不只是「看圖」,而是建立「可被驗證的假設」。當你能在 30 分鐘內把一份陌生資料整理成 5 條假設,就代表已經具備資料分析師的核心能力了。
結語
今天用鐵達尼號資料集走完了一次完整的 EDA。流程上分成六個步驟:初步檢視、缺失值處理、單變數分佈、雙變數關係、交叉分析、彙整觀察。這套流程可以套用到任何新資料集上,只要把欄位名稱換掉、視覺化依資料特性調整即可。明天開始,我們會暫時離開資料分析的內容,進入深度學習的數學基礎:線性代數。理解向量、矩陣與內積,才能真正看懂神經網路的運作方式。
明天,我們會暫時離開資料分析的內容,進入深度學習的數學基礎:線性代數。理解向量、矩陣與內積,才能真正看懂神經網路的運作方式。
留言
張貼留言