Project 問題看起來很簡單:依 city、decade、meal type 計算 historical menu 數量。Raw NYPL menu table 卻讓三個維度都不穩定:place 混合 city、address、hotel、ship 與 route;date 有 missing 和 impossible year;event label 使用多種語言與拼法。
目標不是讓每個 field 看起來整齊,而是讓一個分析問題可回答,同時保留 uncertainty 與 source evidence。
先定義 output,再寫 cleaning rule
17,545 筆 input row 全部保留。Raw column 不覆寫,另加 14 個 derived field 保存 normalized value 與 provenance,包括:
canonical_city, city_source, city_rule, is_transit
menu_year, menu_decade, date_status, date_rule
canonical_event, event_source, event_rule
因此 cleaned dataset (D’) 是 (D) 的 extension,不是假裝原始 ambiguity 從未存在的 replacement。
Pipeline 有兩個層次
Outer workflow 負責 profile、clean、validate、audit inference 與 package evidence。Inner workflow 讓每筆 row 經過九個 conceptual stage:
- normalize text;
- classify ship、train、en-route record;
- 從
placeextract city candidate; - tally location evidence,veto ambiguous venue name;
- 套 curated gazetteer 與 learned crosswalk;
- 依固定 precedence 決定 final city;
- parse/repair date;
- 將 event text 映射為 meal type;
- emit provenance 與 change log。
其中一個 step 必須看 dataset-level evidence:同一 hotel name 若出現在不同 city,就不能安全推成單一 city。只看單 row 的 code 發現不了這種 ambiguity。
白話來說: 某個值單獨看很明顯,看到整張 table 後可能就不再安全。
Canonicalization 改變 analytical grain
Pipeline 將 3,708 個 distinct raw place string 收斂成 233 個 canonical city,1,769 個 event string 收斂成八種 meal type;光 DINNER 就吸收 736 個 raw variant。
Cleaning 前,analysis 回傳 7,559 份 menu、5,049 個 group;最大 group 被 ship 占據,同義 meal label 又把 count 切碎。Cleaning 後,結果是 6,268 份可歸屬 menu、757 個 group。
較少的 row 是刻意結果:
7,559 raw answer rows
- 2,858 transit menus
- 1,259 non-city or unresolved place values
+ 2,825 cities recovered from location
+ 1 date repaired into range
= 6,268 cleaned answer rows
Cleaning 沒有最大化 coverage,而是把 population 從「place 有東西的 row」改成「能歸屬 city 的 menu」。
Inference 用量測,不用宣稱
place 不可用時,location 有時能透過 curated venue gazetteer 或 learned majority crosswalk 找回 city。Learned mapping 要求 support 至少 3、confidence 至少 0.75;會指向多個 city 的 generic venue name 會被 veto。
Held-out check 使用兩種信號都存在的 row:
| Inference path | Checked rows | Agreement |
|---|---|---|
| Curated gazetteer | 381 | 99.7% |
| Learned crosswalk,leave-one-out | 455 | 93.6% |
| Combined | 836 | 96.4% |
Caveat 不能省略:test row 有可 parse 的 place,可能比真正需要 inference 的 row 簡單。Measured agreement 是 evidence,不是 target population 的保證。
Constraint 保護 interpretation
八條 denial-style constraint 檢查 transit value 當 city、impossible date、missing ID、meal vocabulary、non-city place 與 derived value 的 provenance;cleaning 後 violation count 全部為零。
Missing date 與 invalid date 分開報告。Cleaning 前有 586 筆 missing date,真正 present 但超出範圍的只有六筆。把兩者混在一起會得到更戲劇化、但不誠實的 improvement。
這個 project 留下的原則
- 保存 raw field,interpretation 放 derived column。
- 相信 local pattern 前先做 global profile。
- Controlled vocabulary 是明確 analytical decision。
- 每筆進入或離開 answer set 的 row 都要 reconcile。
- 用 held-out evidence 測 inference,並說明 sample bias。
- Rule、threshold、test 與 provenance 都是 deliverable。
最後 pipeline 使用 standard-library Python 與 SQLite,配有 44 個 unit test 和 machine-readable audit report。工具選擇不如每個 cleaning decision 都能檢查、能重跑重要。
複習卡
- 從 analytical question 與 required grain 開始。
- 保存 raw data;另加 canonical value 與 provenance。
- Dataset-level ambiguity 可能擊敗 row-local rule。
- 品質變好時 coverage 合理地下降。
- 不只比 total,要 reconcile before/after population。
- Accuracy percentage 必須附 sampling caveat。