Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

第二章:資料格式與表格:從 CSV 到 Parquet 與 NumPy 檔

本章目標
情境與限制
可重現規則

資料格式讓你頭大的真正原因

很多人第一次碰資料問題,卡住的地方其實不是分析,而是資料本身就已經亂掉了。

常見的狀況:

這些問題如果不先整理,後面做再多分析都很容易歪掉。本章的核心不是介紹所有格式,而是建立一套讀入 → 正規化 → 儲存快照的可重現流程。

常見資料格式的選擇時機

不同格式在可讀性、效能與跨系統相容性之間各有取捨。以下是實務上最常遇到的五種格式:

格式適合用途不適合備註
CSV原始資料交換、人工閱讀、小型資料集大型資料集、含複雜型別(如日期)所有欄位讀進來都是字串,需要手動轉型
JSONAPI 回傳資料、巢狀結構資料大型表格資料適合「不規則欄位」,但讀取效能不佳
Excel (.xlsx)人工填寫表單、需要公式或樣式的報表程式自動化流程同一欄可能混合多種格式,需特別處理
Parquet處理後的分析用快照、大型資料集人工直接開啟閱讀欄位型別明確儲存,查詢速度快
NumPy (.npy)數值計算快照、機器學習資料前處理含非數值欄位的一般表格保留結構化陣列格式,適合重複載入

實務原則:原始資料保留 CSV/JSON(保真),處理後存成 Parquet(效能),數值快照用 NumPy(計算用)。不要把三個角色的檔案混放在同一個目錄。

缺值:最常被忽略的問題

缺值(missing value)在不同格式裡長得不一樣:

格式缺值的樣子
CSV空欄位(,)、空字串("")、NAN/A
JSONnull、欄位完全不存在
Excel空白儲存格
Parquet / NumPyNaN(浮點數)、pd.NA(Pandas 擴充型別)

本章使用 pd.to_numeric(errors="coerce")pd.to_datetime(errors="coerce") 的模式,把無法轉型的值自動標記為 NaN,而不是讓程式崩潰。

# "invalid" 日期字串不會拋錯,而是轉成 NaT(Not a Time)
df["joined"] = pd.to_datetime(df["joined"], errors="coerce")

這讓你可以先完成整批資料的型別轉換,再統一決定缺值的處理策略(填補、移除、保留),而不是在讀取階段就卡住。

Schema:先定義欄位應該長什麼樣子

在讀取任何資料之前,最值得養成的習慣是:先定義每個欄位應該是什麼型別

本章用一個 schema 字典來描述:

schema = {
    "user_id": "int",
    "score": "float",
    "joined": "datetime",
    "status": "string",
}

normalize_table() 會依照這份 schema,把每個欄位轉成對應的 Pandas 型別,並在欄位不存在時自動補上 pd.NA,確保輸出的 DataFrame 結構永遠一致。

這個設計的好處是:無論輸入是哪種格式(CSV、JSON、Excel),只要經過 normalize_table(),出來的結構都相同——後續的分析程式碼不需要對每種輸入格式各寫一版。

原始資料、處理中資料、分析用快照的三層分離

一個常見的混亂來源是:把所有版本的資料都放在同一個資料夾,或是直接覆蓋原始資料。

建議明確區分三個角色:

層次路徑內容規則
原始資料data/raw/從外部取得的原始檔案絕對不修改,保留原貌
處理後資料data/processed/經過清理與型別轉換的 Parquet 檔可重新產生,不手動編輯
分析用快照data/synthetic/數值型 NumPy 快照用於後續計算,需與 processed 版本對應

本章的 transform_file() 函式把這個流程串成一個單一入口:

processed_path, snapshot_path = transform_file(
    source_path=raw_csv,
    processed_path=processed_dir / "ch02_data.parquet",
    snapshot_path=processed_dir / "ch02_data.npy",
    schema=schema,
)

只要輸入相同,輸出永遠相同——這就是可重現的資料流程。

實作範例

請參考 examples/ch02/data_transform.py

測試案例示範了一個含缺值與格式錯誤的 CSV 轉換流程:

user_id,score,joined,status
1,10,2026-04-01,active
2,,2026-03-15,inactive      ← score 缺值
3,7,invalid,pending         ← joined 格式錯誤

以下是執行資料轉換的範例程式碼(請在專案根目錄執行):

from examples.ch02.data_transform import transform_file
from pathlib import Path

# 定義預期的資料型態
schema = {
    "user_id": "int",
    "score": "float",
    "joined": "datetime",
    "status": "string",
}

raw_csv = Path("data/raw/ch02_sample.csv")
processed_dir = Path("data/processed")
processed_dir.mkdir(parents=True, exist_ok=True)

# 執行轉換:讀取 CSV -> 依據 Schema 轉型 -> 存成 Parquet 與 NumPy 備份
processed_path, snapshot_path = transform_file(
    source_path=raw_csv,
    processed_path=processed_dir / "ch02_data.parquet",
    snapshot_path=processed_dir / "ch02_data.npy",
    schema=schema,
)

print(f"Data successfully transformed and saved to: {processed_path}")

轉換後:

示範流程包含: