Community研究與資料分析github.com

koprjaa/dq-skills

十個 Claude Code 技能,用於稽核與清理任何 SQL 資料庫中的資料品質:設定檔、品質指標、稽核報告、標準化、插補、去重與缺陷預防。

dq-skills 是什麼?

「koprjaa/dq-skills」是一套專為 Claude Code 設計的資料品質技能集,包含十個獨立技能,涵蓋資料品質管理的完整生命週期:從資料剖析、品質指標計算、稽核報告生成,到標準化、插補、去重與缺陷預防。此技能集支援任何 SQL 資料庫(如 MySQL、PostgreSQL 等),並可透過 SQL 指令或 Python (pandas) 腳本執行。每個技能遵循嚴格的原則:保留原始資料、僅對確定無誤的轉換進行處理、測量並記錄每項操作的成效。使用者可以透過這組技能建立自動化的資料管道,從資料剖析開始,依序進行解析、標準化、地址比對、插補與去重,並在最後產生完整的資料品質報告。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add koprjaa/dq-skills

Installed? Explore more 研究與資料分析 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

解析器 — 原子化

修復的第一個步驟。在未原子化之前,切勿進行標準化。 對包含「Na Budíně 854」的欄位 ADDR_STREET 進行標準化是徒勞的 — 地址登記冊會將街道名稱和門牌號碼分開儲存。

管線:稽核 → dq-parser → dq-standardizator → dq-adresar → dq-imputator → dq-deduplikator。

規則

  • 原始資料不可刪除。 解析後的輸出會放入新的欄位(_STD、_NUM1、_NUM2)。 原始資料是前後比對的證據,也是解析器出錯時的救命網。
  • 採用確定性規則,而非啟發式方法。 一個在 3% 的情況下會猜錯的解析器,會產生無聲的資料損毀。若某個規則無法百分之百確定,則保留 NULL 並計算有多少行無法解析。 此規則適用於具有已知模式的結構化欄位 — 地址、姓名、日期。對於沒有模式的自由文字,統計實體辨識 (NER、spaCy) 是合法的工具, 但其輸出是一種推測:必須附帶信心分數與標記,且未經審查不得放入正式欄位。
  • 衡量解析完成率 — 計算成功拆解的行數比例。這是需要納入報告的指標。
  • 在任何其他處理之前先進行解析:標準化、插補與去重都依賴原子化的欄位。

地址

最常見的缺陷:專門用來儲存門牌號碼的欄位 100% 為空,而門牌號碼被直接放在街道名稱欄位中。在原始資料中看起來像 Na Budíně 854、Nádražní 1211/25a、Bezručova 12/3。

將其拆解為三個部分 — 街道名稱、門牌號碼、區段號碼(可能包含字母):

-- MySQL 8: REGEXP_SUBSTR / REGEXP_REPLACE
UPDATE PARTY_ADDRESS SET
  ADDR_NUM1_STD   = REGEXP_SUBSTR(ADDR_STREET, '[0-9]+(?=(/[0-9]+[a-zA-Z]?)?[[:space:]]*$)'),
  ADDR_NUM2_STD   = REGEXP_SUBSTR(ADDR_STREET, '(?<=/)[0-9]+[a-zA-Z]?[[:space:]]*$'),
  ADDR_STREET_STD = TRIM(REGEXP_REPLACE(ADDR_STREET, '[0-9]+(/[0-9]+[a-zA-Z]?)?[[:space:]]*$', ''));

在使用 pandas 管線時的 Python 等效方法:

num  = raw.str.extract(r'(\d+)(?:/\d+\w?)?\s*$', expand=False)      # 門牌號碼
street = raw.str.replace(r'\d+(/\d+\w?)?\s*$', '', regex=True).str.strip()

注意數字的順序:在台灣通常依序記錄「弄/號/之/樓」,但慣例不一定總是被遵循。 當解析器只找到一個數字時,將其儲存為主編號並將次編號保留為 NULL — 不要猜測。

行政區欄位中的複合值

台北市中正區、新竹市東區 — 操作員將整個行政區層級(直轄市/縣市 + 區/鄉鎮市)寫入單一欄位。使得無法與官方登記冊進行比對。

-- 第一個區段(若存在) = 直轄市/縣市,第二個區段 = 區/鄉鎮市
SELECT ADDR_CITY,
  TRIM(SUBSTRING_INDEX(ADDR_CITY, '-', 1))                      city,
  NULLIF(TRIM(SUBSTRING(ADDR_CITY, LOCATE('-',ADDR_CITY)+1)),'') district
FROM PARTY_ADDRESS WHERE ADDR_CITY LIKE '%-%';

開始解析前的候選資料偵測:

SELECT ADDR_CITY, COUNT(*) cnt FROM PARTY_ADDRESS
WHERE ADDR_CITY REGEXP '[0-9]' OR ADDR_CITY LIKE '%-%' OR LENGTH(ADDR_CITY) > 50
GROUP BY 1 ORDER BY cnt DESC LIMIT 20;

注意:城市名稱中的連字號有時是合法的(Frýdek-Místek、Brandýs nad Labem-Stará Boleslav)。因此,拆解結果必須與官方行政區登記冊進行驗證,而非盲目執行 — 如果拆解後的城市名稱不在登記冊中,則保留原始值。

郵政信箱與其他非標準格式

郵政信箱、郵政專用信箱、「公司遞送地址」— 這些是形式上有效但建築物登記冊中無法找到的地址。不要強行解析它們,為它們標記專用旗標,並將其從比對率指標中排除(否則你會將非錯誤的資料標記為錯誤)。

姓名

交錯空格

D A V I D、Ž A N N A — 每個字母之間有空格。當 所有 標記都是單個字母時,或者當單個字母的標記佔多數時(其餘通常是連字號或縮寫),可以確定性地將其合併:

toks = s.split()
if len(toks) > 1 and all(len(t) == 1 for t in toks):
    s = "".join(toks)
elif len(toks) > 1 and sum(len(t) == 1 for t in toks) >= max(2, len(toks) - 1):
    s = "".join(toks)

切勿一般性地合併所有標記 — Anna Marie 是兩個名字,而不是 AnnaMarie。

多部分名字

名字和中間名在同一個欄位中。根據空格進行分割,並將第二個標記單獨儲存;捨棄單個字母的標記(縮寫),否則你會產生無意義的中間名:

UPDATE PART_PARTY
SET PARTY_FNAME_STD = <proper>(SUBSTRING_INDEX(PARTY_FNAME,' ',1)),
    PARTY_MNAME_STD = NULLIF(<proper>(SUBSTRING_INDEX(SUBSTRING_INDEX(PARTY_FNAME,' ',2),' ',-1)),
                             <proper>(SUBSTRING_INDEX(PARTY_FNAME,' ',1)))
WHERE PARTY_TYPE='P' AND LENGTH(SUBSTRING_INDEX(PARTY_FNAME,' ',2)) > 1;

頭銜被寫入姓名欄位

Ing. Jan Novák 位於 PARTY_NAME 欄位。透過比對第一個標記與頭銜字典來偵測,而不是根據句點 — J. Novák 是縮寫,不是頭銜。

字典是頭銜代碼表(REF_*_TITBEF 用於姓名前的頭銜,TITAFT 用於姓名後的頭銜),並且匹配應採用模糊比對,因為資料中 MuDr、MUDr. 和 Mudr 並存。解析器僅負責分離頭銜;將其轉換為標準形式是 dq-standardizator 的任務。

編碼錯誤

兩種不同的情況,兩種不同的解決方案:

錯誤範例解決方案
系統性字元置換ĄUDOVÍT (應為 ĽUDOVÍT)如果模式是明確的,則採用確定性取代 — 例如,單字開頭的 Ą 總是錯誤的轉寫,因為在波蘭語中它永遠不會出現在那裡
遺失字母BE0ŠOVÁ (應為 BENEŠOVÁ)不要在解析器中修正。 標記為 _CORRUPT 旗標,並讓 dq-standardizator(姓名登記冊)或 dq-deduplikator(叢集中的乾淨副本)來處理
s = re.sub(r"(^|(?<=\s))Ą", "Ľ", s)          # 明確、確定性
corrupt = bool(re.search(r"[0-9]", s))       # 姓名中的數字 = 損毀,在其他地方處理

這種劃分是有意為之的:解析器只做有把握的事。重建遺失字母是一種推測,應在可以進行驗證的地方處理。

以文字形式儲存的日期

在解析之前,先驗證格式和日/月:

SELECT SUBSTRING(<col>,1,2) prvni, SUBSTRING(<col>,4,2) druhy, COUNT(*)
FROM <t> GROUP BY 1,2 ORDER BY 3 DESC LIMIT 20;

當第一個位置從未超過 12 而第二個位置超過 12 時,順序為 MM.DD;當兩個位置都超過 12 時,格式是混合的,必須根據第二個來源逐行判斷。盲目的 STR_TO_DATE 無法捕捉到這種無聲的互換。

解析器輸出

  • 新的 _STD 欄位包含原子化值,原始欄位保持不變。
  • 每個部分的解析完成率(具有非空結果的行數百分比)。
  • 錯誤表 列出解析器無法拆解的行,以及每行的原因 — 提供記錄為何某筆記錄未通過的稽核軌跡。這是人工審查或決定「此資料超出範圍」的輸入。

相關技能