數(shù)據(jù)清洗:用Formula與INCHIKEY精準(zhǔn)匹配HMDB ID和Common Name)
代謝組學(xué)數(shù)據(jù)清洗這件事說難不難說簡單也真不簡單。我最早接觸這批需求是在處理一批非靶向代謝組學(xué)的注釋結(jié)果時(shí)手里攥著幾百上千條化合物記錄每條都帶著 Formula分子式和 INCHIKEY但下游分析要用的 HMDB ID 和 Common Name 卻是空的。手工一條條去查不現(xiàn)實(shí)。寫腳本硬匹配又會(huì)撞上一堆坑——分子式對上了但結(jié)構(gòu)不對、INCHIKEY 前 14 位相同卻分屬不同條目、同一個(gè)化合物在不同數(shù)據(jù)庫里叫法五花八門。這篇就圍繞用 Formula 值匹配 INCHIKEY再拿到對應(yīng) HMDB ID 和 Common Name這條鏈路把我踩過的坑、驗(yàn)證過的思路和能直接抄的代碼完整拆一遍適合做代謝組學(xué)、天然產(chǎn)物分析、化合物數(shù)據(jù)庫整合的朋友參考新手能照著跑通老手也能在細(xì)節(jié)里找到幾個(gè)省時(shí)間的點(diǎn)。1. 先搞清楚這條匹配鏈路到底在解決什么問題1.1 Formula、INCHIKEY、HMDB ID 三者各自的角色很多人一上來就想寫 SQL 或者 pandas 的 merge結(jié)果發(fā)現(xiàn)字段根本對不上。問題出在沒理清這三個(gè)字段的語義層級。Formula分子式是最粗的一層它只描述原子組成比如 C6H12O6對應(yīng)的可能是葡萄糖、果糖、半乳糖等一大堆同分異構(gòu)體。INCHIKEY是 InChI 的哈希壓縮形式標(biāo)準(zhǔn)長度 27 位前 14 位編碼的是分子骨架連接性也就是這個(gè)分子長什么樣后 10 位編碼的是立體化學(xué)、同位素、電荷等細(xì)節(jié)最后一位是校驗(yàn)位。HMDB ID則是人類代謝組數(shù)據(jù)庫里的唯一主鍵形如 HMDB0000122它綁定的是一個(gè)具體的數(shù)據(jù)庫條目附帶 Common Name、分類、生物樣本濃度等一堆元數(shù)據(jù)。所以這條鏈路的本質(zhì)是用分子式做粗篩用 INCHIKEY 做精確定位最終落到 HMDB 條目上取名字。分子式只是入口INCHIKEY 才是真正的身份憑證。我見過不少人直接拿 Formula 去 HMDB 里模糊搜結(jié)果一個(gè) C6H12O6 返回幾十條根本沒法自動(dòng)判斷該選哪個(gè)——這就是跳過 INCHIKEY 的代價(jià)。1.2 為什么不能只用 Formula 直接查 Common Name舉個(gè)實(shí)際場景。你有一批數(shù)據(jù)Formula 列寫著 C10H16N5O13P3這是 ATP 的分子式。但如果你只拿這個(gè)分子式去匹配數(shù)據(jù)庫里可能同時(shí)存在 ATP、dATP 的近似條目、各種磷酸化衍生物甚至一些分子式相同但結(jié)構(gòu)完全不同的代謝物。Common Name 一旦匹配錯(cuò)下游的通路富集分析、生物標(biāo)志物篩選全部跟著錯(cuò)而且這種錯(cuò)誤極其隱蔽——名字看起來都像那么回事。INCHIKEY 的價(jià)值就在這里它是結(jié)構(gòu)決定的同一個(gè) INCHIKEY 前 14 位基本鎖定了分子的連接方式能有效排除同分異構(gòu)體的干擾。所以正確的順序永遠(yuǎn)是Formula 輔助校驗(yàn) INCHIKEY 主鍵匹配而不是反過來。我在實(shí)際項(xiàng)目里會(huì)把 Formula 當(dāng)作一道防呆檢查如果某條記錄的 INCHIKEY 匹配到了 HMDB 條目但該條目的分子式和輸入的 Formula 對不上那這條匹配就要打上可疑標(biāo)記人工復(fù)核。這一步能攔下不少因?yàn)?INCHIKEY 錄入錯(cuò)誤或數(shù)據(jù)庫版本差異導(dǎo)致的誤配。1.3 這條鏈路典型的應(yīng)用場景說幾個(gè)我實(shí)際遇到過的用法。第一類是非靶向代謝組學(xué)注釋結(jié)果的下游整理質(zhì)譜軟件輸出的化合物表里往往只有 Formula 和 INCHIKEY缺 HMDB ID而后續(xù)做通路分析、網(wǎng)絡(luò)分析又必須要 HMDB ID 做索引。第二類是多數(shù)據(jù)庫交叉驗(yàn)證你從 PubChem 或 ChemSpider 拿到一批化合物想映射到 HMDB 體系里看它們的生物學(xué)角色。第三類是自建化合物庫的標(biāo)準(zhǔn)化實(shí)驗(yàn)室積累的化合物清單字段混亂需要統(tǒng)一補(bǔ)全標(biāo)準(zhǔn) ID 和通用名。這三類場景的共同點(diǎn)是數(shù)據(jù)量大、人工不可行、對準(zhǔn)確性要求高。所以下面講的方案不是能跑就行而是要考慮可復(fù)現(xiàn)、可審計(jì)、出錯(cuò)能定位。2. 數(shù)據(jù)源怎么選本地庫、在線接口還是現(xiàn)成映射表2.1 三種數(shù)據(jù)獲取路徑的取舍做 INCHIKEY 到 HMDB 的映射數(shù)據(jù)從哪來直接決定了方案的穩(wěn)定性和速度。我梳理了三條常見路徑各有各的脾氣路徑數(shù)據(jù)來源優(yōu)點(diǎn)缺點(diǎn)適用場景本地 HMDB 結(jié)構(gòu)文件HMDB 官方提供的 XML/SDF 全量下載離線、可批量、字段完整文件大、更新需重新下載、解析成本高大批量、重復(fù)性任務(wù)在線化合物接口各類化合物數(shù)據(jù)庫的查詢 API實(shí)時(shí)、無需維護(hù)本地庫有速率限制、網(wǎng)絡(luò)依賴、字段不全小批量、臨時(shí)查詢現(xiàn)成映射表社區(qū)整理的 INCHIKEY-HMDB 對照表開箱即用、輕量覆蓋不全、版本滯后、來源需甄別快速驗(yàn)證、補(bǔ)充我的建議是以本地 HMDB 結(jié)構(gòu)文件為主在線接口為輔。原因很實(shí)在代謝組學(xué)項(xiàng)目動(dòng)輒幾千條記錄走在線接口光是速率限制就能把你卡到懷疑人生而且一旦網(wǎng)絡(luò)抖動(dòng)整個(gè)流程就得重跑。本地庫雖然初次準(zhǔn)備麻煩點(diǎn)但一次建好后面所有項(xiàng)目都能復(fù)用長期看時(shí)間成本低得多。2.2 本地 HMDB 結(jié)構(gòu)文件的字段結(jié)構(gòu)HMDB 官方提供的結(jié)構(gòu)文件里每條代謝物記錄通常包含這些關(guān)鍵字段HMDB ID、Common Name、Chemical Formula、Average Molecular Weight、SMILES、InChI、InChIKey、Synonyms 等。解析的時(shí)候重點(diǎn)抓四個(gè)accession即 HMDB ID、nameCommon Name、chemical_formula、inchikey。這里有個(gè)容易忽略的細(xì)節(jié)同一個(gè) HMDB 條目可能對應(yīng)多個(gè) INCHIKEY尤其是帶立體異構(gòu)的化合物數(shù)據(jù)庫里會(huì)記錄不同立體形式的 INCHIKEY。反過來同一個(gè) INCHIKEY 前 14 位可能對應(yīng)多個(gè) HMDB 條目不同立體化學(xué)或不同鹽形式。所以建索引的時(shí)候我通常建兩層一層用完整 27 位 INCHIKEY 精確匹配一層用前 14 位骨架匹配做兜底。兩層配合既能保證精度又能在完整鍵對不上時(shí)給出候選。2.3 在線接口作為補(bǔ)充時(shí)的注意事項(xiàng)在線接口不是不能用但要清楚它的邊界。第一速率限制必須做請求間隔和重試機(jī)制我一般設(shè) 0.2 到 0.5 秒的間隔遇到限流就指數(shù)退避。第二返回字段可能不全有些接口只給 HMDB ID 不給 Common Name那就得二次查詢。第三結(jié)果要緩存同一個(gè) INCHIKEY 查過一次就落本地避免重復(fù)請求。這三點(diǎn)做到了在線接口作為補(bǔ)充手段是夠用的。提示無論用哪種數(shù)據(jù)源都要記錄數(shù)據(jù)版本和獲取時(shí)間。代謝組學(xué)數(shù)據(jù)庫會(huì)更新今天匹配上的條目明天可能被合并或修訂沒有版本記錄的話幾個(gè)月后你根本說不清當(dāng)時(shí)的結(jié)果是怎么來的。3. 匹配邏輯的核心設(shè)計(jì)從精確到模糊的分層策略3.1 第一層完整 INCHIKEY 精確匹配這是最理想的情況。輸入的 INCHIKEY 是完整 27 位本地庫里也有完全一致的記錄直接一對一映射拿到 HMDB ID 和 Common Name。這一步的代碼邏輯非常簡單本質(zhì)就是一個(gè)字典查找或者 DataFrame 的 merge。但實(shí)際數(shù)據(jù)里完整 27 位能精確命中的比例往往沒有想象中高。原因有幾個(gè)輸入數(shù)據(jù)的 INCHIKEY 可能被截?cái)?、可能帶了多余的空格或大小寫不一致、可能是舊版本算法生成的。所以匹配前必須做標(biāo)準(zhǔn)化清洗去首尾空格、統(tǒng)一轉(zhuǎn)大寫、檢查長度是否為 27、校驗(yàn)最后一位校驗(yàn)位。我見過太多因?yàn)橐粋€(gè)尾隨空格導(dǎo)致整批匹配失敗的案例這種坑完全可以在預(yù)處理階段消滅。3.2 第二層前 14 位骨架匹配做兜底當(dāng)完整鍵對不上時(shí)退而求其次用前 14 位匹配。前 14 位鎖定的是分子連接性能保證骨架相同但立體化學(xué)可能不同。這時(shí)候匹配結(jié)果可能是一對多需要額外邏輯來裁決。我的處理方式是前 14 位匹配到的所有候選再用 Formula 做一次交叉驗(yàn)證。如果候選條目的分子式和輸入 Formula 一致保留不一致的剔除。如果交叉驗(yàn)證后還剩多個(gè)候選就標(biāo)記為多候選待定輸出所有候選的 HMDB ID 和 Common Name 供人工判斷而不是武斷地取第一個(gè)。這一點(diǎn)很重要——自動(dòng)流程最怕的就是看起來成功了但其實(shí)是錯(cuò)的寧可標(biāo)記待定也不要制造假陽性。3.3 第三層Formula 反向校驗(yàn)與異常標(biāo)記前面說過 Formula 是防呆檢查具體怎么落地我的做法是在匹配完成后對每一條成功映射的記錄做一次反向核對拿匹配到的 HMDB 條目的分子式和輸入的 Formula 做字符串比對。這里要注意分子式的書寫規(guī)范問題——元素順序、大小寫、括號使用都可能不一致比如 C6H12O6 和 H12C6O6 在字符串層面不等但化學(xué)上等價(jià)。所以比對前要做分子式規(guī)范化解析成元素計(jì)數(shù)字典再按標(biāo)準(zhǔn)順序通常是 C、H 然后其他元素字母序重新拼接。規(guī)范化之后比對能消除大部分書寫差異帶來的假陰性。對于比對不上的記錄打上formula_mismatch標(biāo)記單獨(dú)輸出一個(gè)異常清單。這個(gè)清單在實(shí)際項(xiàng)目里非常有用往往是數(shù)據(jù)錄入錯(cuò)誤或者數(shù)據(jù)庫版本差異的線索。3.4 匹配優(yōu)先級的完整決策流程把三層邏輯串起來完整的決策流程是這樣的輸入記錄先做 INCHIKEY 標(biāo)準(zhǔn)化去空格、轉(zhuǎn)大寫、長度校驗(yàn)。用完整 27 位鍵查本地索引命中則直接取 HMDB ID 和 Common Name標(biāo)記match_level exact。未命中則用前 14 位查骨架索引得到候選集。候選集用規(guī)范化后的 Formula 過濾剩一條則標(biāo)記match_level skeleton剩多條則標(biāo)記match_level ambiguous并輸出全部候選。所有命中記錄做 Formula 反向校驗(yàn)不一致的打formula_mismatch標(biāo)記。完全未命中的記錄標(biāo)記match_level none進(jìn)入人工或在線接口補(bǔ)充流程。這個(gè)流程的好處是每一步的匹配質(zhì)量都有明確標(biāo)記后續(xù)做統(tǒng)計(jì)分析時(shí)可以按 match_level 分層處理比如只保留 exact 和 skeleton 級別的結(jié)果做嚴(yán)格分析ambiguous 的單獨(dú)討論。4. 用 Python 把整條鏈路跑通可復(fù)現(xiàn)的實(shí)操代碼4.1 環(huán)境準(zhǔn)備與依賴這套流程用 Python 就夠了核心依賴不多pandas處理表格lxml或xml.etree解析 HMDB 的 XML 文件requests用于可選的在線補(bǔ)充。裝依賴這一步?jīng)]什么好省的直接上pip install pandas lxml requests如果你的 HMDB 結(jié)構(gòu)文件是 SDF 格式還需要rdkit來解析但純做 INCHIKEY 映射的話XML 版本字段更規(guī)整我更推薦用 XML。SDF 解析雖然靈活但字段提取要寫不少樣板代碼對只想拿 ID 和名字的場景屬于殺雞用牛刀。4.2 解析 HMDB 結(jié)構(gòu)文件建本地索引假設(shè)你拿到的是 HMDB 的代謝物 XML 文件結(jié)構(gòu)大致是每條metabolite節(jié)點(diǎn)下掛著accession、name、chemical_formula、inchikey等子節(jié)點(diǎn)。解析的核心是把這些字段抽出來建成兩個(gè)字典索引import xml.etree.ElementTree as ET import pandas as pd def build_hmdb_index(xml_path): tree ET.parse(xml_path) root tree.getroot() records [] for metab in root.iter(metabolite): accession metab.findtext(accession) name metab.findtext(name) formula metab.findtext(chemical_formula) inchikey metab.findtext(inchikey) if inchikey: inchikey inchikey.strip().upper() records.append({ hmdb_id: accession, common_name: name, formula: formula, inchikey: inchikey }) df pd.DataFrame(records) # 完整鍵索引 exact_index {} for _, row in df.iterrows(): if row[inchikey] and len(row[inchikey]) 27: exact_index.setdefault(row[inchikey], []).append(row) # 骨架索引前14位 skeleton_index {} for _, row in df.iterrows(): if row[inchikey] and len(row[inchikey]) 14: skeleton_index.setdefault(row[inchikey][:14], []).append(row) return df, exact_index, skeleton_index這里用setdefault配合列表是因?yàn)橐粋€(gè)鍵可能對應(yīng)多條記錄必須保留全部候選而不是覆蓋。這一點(diǎn)在骨架索引上尤其關(guān)鍵前 14 位一對多是常態(tài)。4.3 分子式規(guī)范化函數(shù)前面強(qiáng)調(diào)過分子式比對前要規(guī)范化具體實(shí)現(xiàn)是把分子式解析成元素計(jì)數(shù)字典再重排。這里要處理括號和嵌套的情況比如 Ca(OH)2 這種。寫一個(gè)能處理常見情況的解析器import re from collections import defaultdict def normalize_formula(formula): if not formula: return None formula formula.strip() # 處理括號展開 def expand(f): # 簡單括號處理支持一層嵌套 pattern re.compile(r\(([^()])\)(\d*)) while pattern.search(f): f pattern.sub(lambda m: m.group(1) * int(m.group(2) or 1), f) return f expanded expand(formula) tokens re.findall(r([A-Z][a-z]?)(\d*), expanded) counts defaultdict(int) for elem, num in tokens: counts[elem] int(num) if num else 1 # 標(biāo)準(zhǔn)順序C、H 優(yōu)先其余字母序 ordered [] for e in [C, H]: if e in counts: ordered.append(f{e}{counts[e] if counts[e] 1 else }) for e in sorted(k for k in counts if k not in (C, H)): ordered.append(f{e}{counts[e] if counts[e] 1 else }) return .join(ordered)這個(gè)函數(shù)不是萬能的遇到非常復(fù)雜的嵌套括號或水合物寫法可能出錯(cuò)但對代謝組學(xué)里絕大多數(shù)分子式夠用了。實(shí)際使用時(shí)如果規(guī)范化失敗返回 None 或拋異常就把該條記錄標(biāo)記為formula_parse_error不要讓它靜默通過。4.4 主匹配函數(shù)與結(jié)果輸出把索引和規(guī)范化函數(shù)組合起來寫主匹配邏輯def match_records(input_df, exact_index, skeleton_index): results [] for _, row in input_df.iterrows(): raw_key str(row.get(INCHIKEY, )).strip().upper() input_formula normalize_formula(row.get(Formula, )) result { input_inchikey: raw_key, input_formula: row.get(Formula, ), hmdb_id: None, common_name: None, match_level: none, formula_mismatch: False } if len(raw_key) 27 and raw_key in exact_index: cand exact_index[raw_key][0] result.update({ hmdb_id: cand[hmdb_id], common_name: cand[common_name], match_level: exact }) if input_formula and normalize_formula(cand[formula]) ! input_formula: result[formula_mismatch] True elif len(raw_key) 14 and raw_key[:14] in skeleton_index: cands skeleton_index[raw_key[:14]] if input_formula: filtered [c for c in cands if normalize_formula(c[formula]) input_formula] else: filtered cands if len(filtered) 1: result.update({ hmdb_id: filtered[0][hmdb_id], common_name: filtered[0][common_name], match_level: skeleton }) elif len(filtered) 1: result[match_level] ambiguous result[candidates] ;.join( f{c[hmdb_id]}|{c[common_name]} for c in filtered) results.append(result) return pd.DataFrame(results)輸出結(jié)果里match_level和formula_mismatch兩個(gè)字段是后續(xù)人工復(fù)核的關(guān)鍵抓手。我一般會(huì)把結(jié)果按 match_level 分組導(dǎo)出成多個(gè) sheetexact 和 skeleton 的直接用ambiguous 和 none 的單獨(dú)處理。4.5 結(jié)果驗(yàn)證抽樣回查與統(tǒng)計(jì)代碼跑完不代表結(jié)束必須做驗(yàn)證。我的習(xí)慣是按 match_level 分層抽樣每層隨機(jī)抽 20 到 30 條拿 HMDB ID 去數(shù)據(jù)庫里回查確認(rèn) Common Name 和 Formula 都對得上。同時(shí)統(tǒng)計(jì)各層的占比如果 exact 占比異常低比如低于 30%就要懷疑輸入數(shù)據(jù)的 INCHIKEY 質(zhì)量或者本地庫版本是否匹配。還有一個(gè)統(tǒng)計(jì)值得看formula_mismatch的比例。如果這個(gè)比例超過 5%說明要么輸入 Formula 有系統(tǒng)性問題比如格式不統(tǒng)一要么本地庫和輸入數(shù)據(jù)來自不同版本需要排查。這些統(tǒng)計(jì)數(shù)字不是走形式它們是判斷整批數(shù)據(jù)可信度的直接依據(jù)。5. 那些文檔里不會(huì)寫的坑我踩過的五個(gè)典型問題5.1 INCHIKEY 大小寫與隱藏字符這是最高頻的坑沒有之一。從 Excel 復(fù)制出來的 INCHIKEY 經(jīng)常帶不可見的尾隨空格或者制表符肉眼完全看不出來但字符串比對就是失敗。更隱蔽的是大小寫混用有些軟件輸出小寫有些輸出大寫而字典查找是大小寫敏感的。解決辦法只有一個(gè)匹配前統(tǒng)一strip().upper()并且用正則檢查是否只含合法字符。我現(xiàn)在的習(xí)慣是在預(yù)處理階段就打印出所有長度不等于 27 的記錄逐條看往往能揪出一批臟數(shù)據(jù)。5.2 同分異構(gòu)體導(dǎo)致的骨架匹配歧義前 14 位匹配一對多是設(shè)計(jì)內(nèi)的行為但歧義怎么裁決是個(gè)真問題。我遇到過一批糖類化合物骨架相同、Formula 相同但立體構(gòu)型不同前 14 位完全一樣Formula 也過濾不掉。這種情況下任何自動(dòng)裁決都是猜正確做法是標(biāo)記 ambiguous 并輸出全部候選讓懂化學(xué)的人來判斷。強(qiáng)行取第一個(gè)的后果是引入系統(tǒng)性錯(cuò)誤而且因?yàn)槊挚雌饋砗侠順O難被發(fā)現(xiàn)。5.3 分子式書寫順序不一致引發(fā)的假陰性C6H12O6 和 H12C6O6 化學(xué)上等價(jià)字符串比對卻不等。如果不做規(guī)范化反向校驗(yàn)會(huì)誤報(bào)大量 formula_mismatch把本來正確的匹配標(biāo)記成可疑。這個(gè)坑的教訓(xùn)是任何涉及化學(xué)式比對的環(huán)節(jié)都必須先規(guī)范化。規(guī)范化函數(shù)要能處理元素順序、括號、數(shù)字省略單個(gè)原子不寫 1這幾種常見差異。5.4 數(shù)據(jù)庫版本差異導(dǎo)致的 ID 失效HMDB 會(huì)定期更新條目可能被合并、拆分或廢棄。你三個(gè)月前建的本地索引今天拿來匹配新數(shù)據(jù)可能就有條目對不上了。更麻煩的是有些舊 ID 在新版本里已經(jīng)不存在但輸入數(shù)據(jù)里還帶著。我的應(yīng)對是在索引里保留條目的狀態(tài)字段如果源文件有的話匹配時(shí)如果命中已廢棄條目標(biāo)記出來提醒更新。同時(shí)記錄本地庫的版本號和下載日期寫進(jìn)結(jié)果文件的元數(shù)據(jù)里。5.5 一對多映射時(shí)的輸出設(shè)計(jì)一個(gè)輸入 INCHIKEY 可能對應(yīng)多個(gè) HMDB 條目不同鹽形式、不同立體化學(xué)這時(shí)候輸出格式的設(shè)計(jì)很關(guān)鍵。如果只輸出一個(gè)字段多候選信息就丟了如果拆成多行下游統(tǒng)計(jì)又會(huì)重復(fù)計(jì)數(shù)。我的做法是主表保持一行一條輸入記錄多候選信息壓縮到一個(gè)字段里用分隔符拼接需要展開時(shí)再單獨(dú)解析。這樣既保證主表行數(shù)穩(wěn)定又不丟信息。6. 匹配質(zhì)量的分層處理與人工復(fù)核策略6.1 按 match_level 分層導(dǎo)出與優(yōu)先級跑完匹配后我從不把結(jié)果一股腦丟給下游而是按 match_level 分層導(dǎo)出。exact 級別的結(jié)果可信度最高可以直接進(jìn)入分析流程skeleton 級別的次之建議抽樣驗(yàn)證后使用ambiguous 和 none 的必須人工介入。這種分層不是形式主義它讓整個(gè)流程的可審計(jì)性大大提升——出了問題能快速定位是哪一層出的錯(cuò)。具體操作上我會(huì)生成一個(gè)匯總統(tǒng)計(jì)表列出各 match_level 的記錄數(shù)、占比以及 formula_mismatch 的數(shù)量。這個(gè)表放在結(jié)果文件的第一頁任何人拿到數(shù)據(jù)先看這個(gè)表就能對整體質(zhì)量有個(gè)判斷。6.2 ambiguous 候選的人工裁決輔助對于 ambiguous 的記錄光給候選列表還不夠得給裁決提供依據(jù)。我會(huì)在候選信息里額外附上每個(gè)候選的分子量、SMILES如果有的話方便復(fù)核人員快速判斷。如果候選數(shù)量太多比如超過 5 個(gè)說明前 14 位骨架太通用這時(shí)候建議改用完整鍵或者結(jié)合其他字段如保留時(shí)間、質(zhì)荷比來縮小范圍。人工裁決的結(jié)果要回寫到主表并且記錄裁決人和裁決依據(jù)。這一步在團(tuán)隊(duì)協(xié)作里尤其重要否則過幾個(gè)月沒人記得當(dāng)時(shí)為什么選了某個(gè)候選。6.3 未匹配記錄的補(bǔ)充查詢流程match_level 為 none 的記錄先別急著放棄。我的流程是先用在線接口按 INCHIKEY 查一遍如果在線也查不到再用 Formula 加分子量做模糊查詢看是否有近似條目。在線查詢的結(jié)果同樣要緩存和記錄來源。如果所有途徑都失敗就標(biāo)記為unresolved在報(bào)告里單獨(dú)說明數(shù)量而不是悄悄丟掉——未匹配記錄的比例本身就是一個(gè)數(shù)據(jù)質(zhì)量指標(biāo)。7. 把這套流程用順之后的幾點(diǎn)體會(huì)這套 Formula 加 INCHIKEY 匹配 HMDB 的流程我從最早的手忙腳亂到現(xiàn)在能穩(wěn)定批量跑中間最大的轉(zhuǎn)變不是代碼寫得多漂亮而是對數(shù)據(jù)質(zhì)量的敬畏。剛開始我總想著一步到位全自動(dòng)結(jié)果被各種臟數(shù)據(jù)教做人。后來想明白了自動(dòng)化的目標(biāo)不是消滅人工而是把人工集中在真正需要判斷的地方。exact 匹配交給機(jī)器ambiguous 和異常交給人工各司其職整體效率和準(zhǔn)確性反而都上去了。另外一個(gè)體會(huì)是索引和規(guī)范化這兩個(gè)基礎(chǔ)環(huán)節(jié)值得多花時(shí)間打磨。索引建得好匹配邏輯就簡單規(guī)范化做得全假陰假陽就少。很多人急著寫匹配主邏輯結(jié)果基礎(chǔ)不牢后面全在打補(bǔ)丁。我現(xiàn)在的習(xí)慣是先把數(shù)據(jù)源摸清楚、把字段清洗規(guī)則定死、把索引結(jié)構(gòu)設(shè)計(jì)好再動(dòng)手寫匹配前期多花的兩小時(shí)能省后面兩天的調(diào)試。最后分享一個(gè)小技巧每次跑完批量匹配我都會(huì)把結(jié)果和上一批的統(tǒng)計(jì)數(shù)字對比一下。如果 exact 占比突然下降或者 formula_mismatch 突然上升多半是輸入數(shù)據(jù)或者本地庫出了問題這時(shí)候停下來查比悶頭往下跑劃算得多。數(shù)據(jù)清洗這活兒慢就是快。