JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

整齊資料

每列一個觀測值、每欄一個變數——讓後續每一步都變簡單的那個簡單形狀,以及雜亂資料為何難以分析。

「整齊」是什麼意思:三條規則

想像你剛收集好三位學生、三個科目的期中考成績,然後把它們丟進一份試算表。在你能算出哪怕一個班級平均、畫出一張長條圖,或訓練一個模型之前,你的整齊資料(tidy data)必須先排成一個工具真正能處理的形狀。這個形狀有個名字,而定義它的規則少得出乎意料。

整齊資料就是一張遵守三條規則的表格。第一,每個變數(variable)自成一欄。第二,每個觀測值(observation)自成一列。第三,每個值(value)各自佔一個儲存格(第四點作為推論:你量測的每一種事物各自成一張表)。整個概念就這麼多。所謂「變數」,是任何你記錄下來、會變動的東西——科目、分數、學生姓名。所謂「觀測值」,是一個完整的量測單位——在這裡,就是一位學生配上一個科目、以及這個配對對應的那個分數。所謂「值」,則是單一一筆紀錄:85、「數學」、「美如」。

為什麼要對這件事如此執著?因為「整齊」是一個對未來的承諾。一旦表格整齊了,後續每一步——篩選列、分組彙總、畫圖、合併到另一張表、餵給模型——都會變成一行就能完成、可預期的操作。當表格雜亂時,同樣這些步驟就會淪為脆弱的手動擺弄,資料一變就壞。整齊資料是前期的一筆小投資,卻讓這份工作的另外八成都變得輕鬆。

整齊的版面:變數橫向排在最上方成為欄,觀測值縱向堆疊成為列,而每個儲存格恰好裝著一個值。

一張矩形表格的示意圖。標題列把各欄標示為變數(姓名、科目、分數);底下每一列是一個觀測值;其中一個儲存格被標示出來,顯示它只裝著單一一個值。

觀測值、變數與值

這三個詞承擔了所有的重活,所以讓我們用一張具體的表把它們釘清楚。假設美如數學考 85、自然考 92、英文考 80。那個我們一再量測的東西——每個科目量一次——就是叫做「分數」的變數(variable)。數學、自然、英文這些標籤,則是第二個變數「科目」的各個值。而「美如」是第三個變數「姓名」的一個值。一筆完整的量測——美如、數學、85——就是一個觀測值(observation),而在整齊的表格裡,它恰好佔據一列。

變數有幾種型態,替它們命名有助於你之後挑選圖表與方法。數值變數(numerical variable)是一個你可以做算術運算的數字——分數、年齡、營收。類別變數(categorical variable)則是來自一組固定選項的標籤——科目、城市、是/否。完整的分類你會在變數型態詞條裡看到;現在只要注意:「分數」是數值的,而「科目」與「姓名」是類別的,而一張整齊的表格非常樂意同時容納兩者,每欄一種型態。

這裡有個沒人會提醒初學者的微妙之處:什麼算「變數」、什麼算「觀測值」,取決於你問的問題。「科目」是一個變數(於是每個學生—科目的配對是一個觀測值),還是說每位學生是一個觀測值、底下帶著三欄分數?兩者都可能是合理的。正確的整齊形狀,是那個讓「列」——也就是觀測單位——對應到你想分析之物的形狀。如果你要比較各科目,就讓科目成為一欄,每列放一個學生—科目的配對。

常見的雜亂形狀(以及它們為何傷人)

大多數真實的試算表,雜亂的方式不出那幾種可辨識的型態。學會辨認它們已經贏了一半,因為每一種雜亂都對應到後續一個特定、可預期的痛點。最常見的元兇,是那些其實是「值」、而非「變數名稱」的欄標題。想像我們的成績冊存成 name、math、science、english 這幾欄。對人來說看起來沒問題,但 math、science、english 並不是三個不同的變數——它們是同一個變數「科目」的三個值。而變數「分數」被打散到三欄裡,甚至連個名字都沒有。

為什麼這會傷人?當你問「跨所有科目的平均是多少?」的那一刻,你沒辦法寫出一行乾淨的指令,因為你要的值散落在三個不同的欄名底下。下個月加進第四個科目,每一條指名那些欄位的公式與圖表都會壞掉。而一張只有單一「分數」欄的整齊表格,對這個變動只會聳聳肩,毫不受影響。

  1. 欄標題是「值」而非「名稱」。把年份(2023、2024)或類別(數學、自然)拿來當欄標題——真正的變數沒有名字,還被拆散到各欄。
  2. 兩個變數硬塞進同一欄。像「85/A」這種把數字和等第混在一起,或「臺北, 2024」把地點與年份混在一起的儲存格。你沒辦法對其中任一部分排序、篩選或計算。
  3. 變數同時存在於列與欄之中。一張表裡有些列是變數、有些欄也是變數——那種經典的「樞紐到一半」的試算表,對任何操作都頑強抵抗。
  4. 多種事物擠在同一張表。把學生與老師(不同的單位)混在同一張工作表裡,會逼出一堆尷尬的空白儲存格與意義不明的列。
  5. 同一種事物分散在許多張表。一個班一張工作表、一個月一個 CSV——同一種觀測值被切成碎片,於是任何看全貌的問題都得先把它們黏起來。

解法與雜亂一一對應。「欄標題是值」用「寬轉長」的重塑來修復;「一格兩變數」用分隔符號拆開;「列欄混雜」用樞紐處理;「混合單位」用「每個單位一張表」來分離;「一單位多表」則用堆疊後再合併。下一節會把最常見的那一種,從頭到尾走一遍。

左邊是雜亂的寬表,科目名稱藏在欄標題裡。右邊是同樣的資料整理成長格式:新增一欄「科目」替變數命名,一欄「分數」裝著值。

並排的兩張表。左邊的寬表有 name、math、science、english 各欄。一個箭頭指向右邊的長表,欄位為 name、subject、score,原本每個儲存格都變成了獨立的一列。

把一張雜亂的表,一步步整理乾淨

我們來真的做一次。先從寬版成績冊出發——三位學生,math、science、english 各為一欄——正是「欄標題是值」那種雜亂。我們的目標是長格式:欄位為 name、subject、score,每個學生—科目的配對佔一列。把寬轉成長的操作叫做重塑,或樞紐(pivoting);在 pandas 裡「寬轉長」的方向是 melt,在 R 的 tidyr 裡則是 pivot_longer。名字不重要,重要的是這個動作。

import pandas as pd

# Wide gradebook: one column per subject (subjects are VALUES, not variables)
wide = pd.DataFrame({
    'name':    ['Mei', 'Jun', 'Ana'],
    'math':    [85, 90, 78],
    'science': [92, 88, 95],
    'english': [80, 84, 91],
})

# Tidy it: one row per (student, subject) observation
tidy = wide.melt(id_vars='name', var_name='subject', value_name='score')
# tidy now has 9 rows and 3 columns: name | subject | score
在 pandas 用 melt 把寬格式重塑成長格式。R 的 tidyverse 對應寫法是 tidyr::pivot_longer()。id_vars 是要固定保留的欄;其餘全部被收攏成一組「名稱/值」配對。

整齊後的表會變多大?n 位學生中的每一位,都為 k 個被量測的科目各貢獻一列,所以長格式有 n 乘以 k 列。三位學生、三個科目,就是九列——原本每位學生的三個分數儲存格,現在一行一個攤開來。這個小小的乘法值得內化,因為它也說明了:對非常寬的來源資料來說,長格式為何可能會膨脹。

n_{\text{entities}} \times k_{\text{measured variables}} \;=\; \text{rows in tidy (long) form}

長格式的列數。這裡是 3 位學生 × 3 個科目 = 9 個整齊的列。

現在來看回報。有了一欄具名的分數、一欄具名的科目,先前讓我們頭痛的問題——各科目的平均——就塌縮成自我解釋的一行。這就是分組彙總(group-by)的型態:把列分成幾組、對每組套用一個摘要、再把結果合併起來。

# Now an average-per-subject is one short, predictable line
tidy.groupby('subject')['score'].mean()

# math     84.33
# science  91.67
# english  85.00
在整齊的資料框上做分組彙總。因為「科目」是一個真正的欄,依它分組就直接成立——而且就算明天冒出第四個科目,照樣行得通。

整齊資料,與愛它的那些工具

整齊形狀不是一種美學偏好;它是整套現代工具鏈悄悄預期的形狀。資料框(dataframe)——pandas、R 與 SQL 核心的那個表狀物件——會把操作跨列向量化,而唯有當每一欄都是一個乾淨的變數時,這才會又快又簡潔。以「圖形文法」風格打造的繪圖函式庫(ggplot2、plotnine、seaborn)要求你把一欄對應到一個視覺通道:這一欄給 x 軸、那一欄給顏色。這種對應在長格式裡輕而易舉,在寬格式裡卻幾乎做不到。

整齊的表格也能彼此咬合。一旦你的資料做到「每張表一種觀測值」,把資料集組合起來就變成一次合併(join):讓兩張表在一個共用欄上對齊——那個用來辨識列的主鍵(key)——再把它們併起來。假設有第二張小小的對照表,記錄每個科目歸哪個系所管。因為兩張表都很整齊、又共用一個乾淨的「科目」欄,把它們黏起來只需一行呼叫。

# A second tidy lookup table: which department owns each subject
subjects = pd.DataFrame({
    'subject':    ['math', 'science', 'english'],
    'department': ['STEM', 'STEM', 'Humanities'],
})

# Join on the shared key 'subject' — tidy tables snap together
tidy.merge(subjects, on='subject', how='left')
依共用主鍵「科目」合併兩張整齊的表。每張表都維持「一張表一種單位」;正是這個主鍵讓它們能乾淨地結合。
合併會把兩張整齊的表在一個共用主鍵上對齊,逐一比對列、把它們的欄縫合起來——這正是「一張表一種觀測單位」帶來的回報。

兩張並排的小表共用一個「科目」欄。箭頭把每個科目值對應到另一張表中對應的列,產生一張同時擁有分數欄與系所欄的合併表。

什麼時候該打破規則

整齊的(長)格式是分析的最佳預設值,但它是預設值,不是誡命——而好的分析師知道什麼時候該刻意打破它。最常見的理由是「人」。一張有數百筆「姓名—科目—分數」列的長表,讀起來很折磨;而一張寬格式(wide)的表,每位學生一列、每個科目一欄,恰恰是人想瀏覽一張成績單的方式。為了呈現,你會在輸出時把長格式重塑回寬格式。

技術上的例外也有。許多機器學習演算法想要的是一張寬的「設計矩陣」:每個你要預測的單位一列、每個特徵一欄——比較接近寬、而非長。儲存與速度也可能把你推向另一邊:長格式那個「n 乘以 k」的列數膨脹,對非常寬的資料可能很浪費;而某些特化的儲存體(矩陣、影像、密集的時間序列)有著天然的陣列形狀,誰都不該硬把它塞進三條瘦瘦的欄。

整齊度檢查清單

在分析任何一張新表之前,先用這份清單把它過一遍。它花你一分鐘,替你省下好幾個小時。如果某一項不過關,你現在已經知道:是哪一個重塑、拆分、或拆成多張表的動作能修好它。

  1. 每一欄是否恰好是一個變數,且有個真正的名字?如果欄標題本身就是一個值(某個年份、某個類別),就把它寬轉長。
  2. 每一列是否恰好是一個觀測值,且落在你打算分析的那個單位上?在重塑前先決定「一列代表什麼」。
  3. 每個儲存格是否只裝著一個值?把任何「85/A」或「臺北, 2024」這種儲存格拆成各自獨立的欄。
  4. 每張表是否只描述一種事物?如果學生與老師共用一張工作表,就拆成每個單位一張表。
  5. 同一批觀測值是否散落在許多檔案或工作表裡?把它們堆疊成一張表(並保留一個主鍵,方便日後合併)。
  6. 變數型態是否乾淨——數字就是數字、日期就是日期、類別就是標籤——而且遺漏值有被標記、而不是用零或空白假冒?

這就是整齊資料:每欄一個變數、每列一個觀測值、每格一個值、每張表一種事物。它是那個既小又無聊的紀律,卻能讓探索、繪圖、合併與建模各就各位。在這條學習路徑的下一篇,我們要處理整齊結構本身去不掉的髒污——遺漏值、離群值、重複,以及在不遺失、也不重複任何一列的前提下,清理與合併真實世界資料的那門細膩功夫。