模組 11 · LongPhase core
LongPhase:核心 phasing 流程
介紹 phase graph、phase set 與 haplotagging,以及主要失效情境與診斷方法。
本模組學習目標
- 說出 LongPhase 的輸入與輸出各是什麼
- 說明四種變異為什麼放進同一張圖一起定相,而不是各自分開處理
- 描述圖的節點與邊各自代表什麼,並用四個計數說明一條邊的方向與去留怎麼決定
- 說明加權投票如何決定每個變異,以及 PS 為什麼會換號
- 說出兩道信心門檻分別對應輸出裡的哪個現象
- 跑一個最小的 LongPhase 範例並解讀輸出
為什麼重要
LongPhaseLongPhase實驗室開發的長 read phasing 工具,是後續所有工具的共同基礎。輸出 germline haplotype。The lab's long-read phasing tool and the common foundation for everything else. Produces germline haplotypes.完整條目 → 是本教材後續工具共用的基礎,處理的是一般(非腫瘤)樣本的 phasing。 開始分析腫瘤資料前,先掌握這個基本流程。
它值得看清楚內部機制,原因是:後面 LongPhase-S 與 LongPhase-TO 的腫瘤專用處理, 都是接在這張圖與這套投票之上。不知道圖長什麼樣,就看不出腫瘤版改了什麼。
概念與互動
輸入與輸出
| 子命令 | 輸入 | 輸出 |
|---|---|---|
phase | germline SNP VCF + BAM + reference;可另外加 SV VCF、甲基化 VCF | 含以 | 表示相位的 GT 與 PSPS tag phase set標示某群 variants 屬於同一 phase block 的編號。同一 PS 內的相位方向可相互比較;不同 PS 的 HP1/HP2 方向彼此獨立。Phase set identifier grouping variants phased together. Haplotype orientation is only consistent within one PS.完整條目 → 的 phased VCF(每種變異各自一份) |
haplotag | 已 phase 的 VCF + BAM + reference | 每條 read 帶 HPHP tagBAM 裡標示某條 read 屬於哪一條 haplotype 的 tag。LongPhase-S 的 germline haplotag 寫成整數 tag 的 BAM |
modcall | 帶甲基化的 BAM + reference | 甲基化位點的 VCF,可再餵回 phase 的 --mod-file |
注意這個順序:先 call variant,再 phase,最後 haplotag。 LongPhase 本身不做 variant calling —— 它需要別人先給它 het 位點當錨點。
四種變異,一張圖
LongPhase 最關鍵的設計選擇,是把四種變異放進同一張圖一起定相, 官方文件稱為 co-phasing。理由來自長讀本身:一條 read 可能同時跨過一個 SNP、 一個甲基化位點、一個小的 indel 與一個 SV —— 這四件事被同一個分子綁在一起, 分開 phasing 就把這個證據丟掉了。
程式內部給每個節點記一個型別,原始碼裡的註解就寫著
0=SNP 1=SV 2=MOD 3=INDEL 4=tandem repeat INDEL。
型別不只是標籤,它會改變後面的判斷 —— 例如串聯重複區的 indel(型別 4)投票權重被降到 0.1,
因為那類位置的定序錯誤率特別高。
一條邊怎麼決定方向,又怎麼被丟掉
邊不是「有」或「沒有」,而是四個加權計數。跨過兩個位置的每一條 read, 依它在兩邊看到的 allele 落進 rr、ra、ar、aa 之一:
edgeThreshold(預設 0.7)就代表兩種組合幾乎一樣多,這條邊會被整個丟掉。反過來, 的乾淨邊,它那一票的權重會從 1 拉到 20。兩個細節值得記住,因為它們解釋了很多「為什麼這裡沒接起來」:
- 低品質的鹼基會被打折。邊的任一端 base quality 低於
--baseQuality(預設 12)時, 那條 read 對這條邊只貢獻--edgeWeight(預設 0.1),而不是 1。 - 甲基化的門檻更嚴。SNP 與甲基化位點之間的邊,門檻從 0.7 收緊到 0.3 —— 甲基化訊號比較吵,要求證據更乾淨才採信。
heuristic phasing:加權投票
圖建好之後,LongPhase 不求全域最佳解。它沿著座標從左往右掃,
一次決定一個變異:前面最多 --connectAdjacent(預設 35)個變異各投一票,
票的權重就是上面那個 20 / 1 / 0.1,兩邊加總大的一邊贏。
PS 換號最直接的原因。③ 一個特例保護:如果很多票其實都靠同一條長 read 撐著(每條邊只有一條 read,),那條 read 一旦有系統性錯誤就會一路投錯票;程式偵測到超過三票是這種情況時,會改成只採信乾淨(ESR < 0.2)且非 indel 的票重算。這是一個典型的 heuristic:快、在長讀上通常夠準,但代價是它對兩件事敏感 ——
證據剛好平手,以及單一 read 主導。③ 那個特例保護就是為了後者而存在的。
另外,距離超過 --distance(預設 300000,約一個 centromere 的長度)的兩個變異不互相投票。
最後兩道信心門檻
圖定好相之後還有一步,程式把結果反過來檢查一次:先用變異的相位決定每條 read 屬於哪條 haplotype,再用已標好的 read 回頭重算每個變異。兩步各有一道門檻:
--readConfidence(預設 0.65)且總數大於 1,才寫 HP 標籤;沒過的 read 就沒有 HP。② 再用標好的 read 重算每個變異,兩種指派的支持比例要超過 --snpConfidence(預設 0.75)才保留,沒過的話這個變異的 PS 會被刪掉。沒有 HP、沒有 PS,多半不是錯誤
這兩道門檻各自對應一個你會在輸出裡直接看到的現象:
BAM 裡有 read 沒有 HP,來自 0.65 那道;VCF 裡有 heterozygous 位點沒有 PS,
來自 0.75 那道。兩者都不是程式漏跑,而是它主動判斷證據不足而收回。
所以看到這些空缺時,要問的是「證據為什麼不足」,而不是「程式是不是壞了」。
三種失敗模式
| 失敗 | 對應到上面的哪一步 | 徵兆 |
|---|---|---|
| phase blockphase block一段可建立連續相位關係的區域。read 長度不足、缺少 informative heterozygous 位點或證據不一致時,可能形成不同 phase blocks。A contiguous stretch over which phasing is consistent. It can break when reads are too short, informative heterozygous sites are absent or evidence conflicts.完整條目 → 斷裂 | 投票平手,或沒有 read 跨過(沒有邊) | 同一條染色體上出現很多不同的 PS 值 |
| switch errorswitch errorphasing 結果自某一位置起將兩條 haplotype 的方向對調,是常見的 phasing 錯誤類型。A phasing error in which the two haplotype labels swap from some point onward; a common phasing error mode.完整條目 → | 某條邊的 ESR 偏高卻仍勉強過關,方向選錯 | 與 truth 比對時,錯誤從某一點之後系統性地發生 |
| 大量 read 未標記 | 沒過 --readConfidence(0.65) | haplotag 後很多 read 沒有 HP |
第一項與第三項在腫瘤樣本上可能更明顯,因為 LOHLOH 異型合子性喪失原本 heterozygous 的區域變成只剩一種 allele。LOH 不等於缺失 —— 也可能是一條 haplotype 遺失後另一條被複製(copy-neutral LOH)。Loss of heterozygosity: a formerly het region retains only one allele. Not necessarily a deletion.完整條目 → 與 copy number 異常會減少可用的 phasing 錨點。 LongPhase-S 與 LongPhase-TO 都針對這些腫瘤資料情境提供額外處理。
真實證據
最小可跑範例
# 1. 準備輸入(見 M4)
samtools faidx reference.fasta
minimap2 --MD -ax map-ont -t 10 reference.fasta reads.fastq -o aln.sam
samtools sort -@ 10 aln.sam -o aln.bam && samtools index aln.bam
# 2. germline calling(Clair3 或 PEPPER-DeepVariant,見 M5)
# 產生 SNP.vcf
# 3. phasing
longphase-s phase \
-s SNP.vcf \
-b aln.bam \
-r reference.fasta \
-t 8 \
-o phased \
--ont
# 4. haplotagging
longphase-s haplotag \
-r reference.fasta \
-s phased.vcf \
-b aln.bam \
-t 8 \
-o tagged
co-phasing:把其他三種變異一起帶進去
# 甲基化要先用 modcall 產生 VCF,才能餵給 phase
longphase-s modcall \
-b aln.bam \
-r reference.fasta \
-t 8 \
-o modcall
# 四種一起 co-phase;每種變異會各自輸出一份 phased VCF
longphase-s phase \
-s SNP.vcf \
-b aln.bam \
-r reference.fasta \
--indels \
--sv-file SV.vcf \
--mod-file modcall.vcf \
-t 8 \
-o phased \
--ont
注意:modcall 的 help 顯示 -b, --bam-file,
但選項表註冊的長選項其實是 --methylbamfile;短選項 -b 兩者都通。
同理 phase 的 help 寫 --sv-window 與 --sv-threshold,
選項表註冊的是 --svWindow 與 --svThreshold。
把圖直接印出來看
前面那三張圖不必只當示意圖 —— --dot 會為每一條染色體輸出一個
dot 檔,裡面就是實際建出來的邊:
longphase-s phase -s SNP.vcf -b aln.bam -r reference.fasta --ont --dot -o phased
# 每一行是一條邊:<位置>.<allele> -> <位置>.<allele>
# allele 相同 = 平行連接,相反 = 交叉連接
head -20 phased.chr20.dot
如何驗證輸出
# 在已定相的 heterozygous records 檢查 | 與 PS
grep -v '^##' phased.vcf | head -5
# BAM 裡應該有 HP tag
samtools view tagged.bam | grep -o 'HP:i:[12]' | sort | uniq -c
# 有多少 read 沒被標到?
samtools view -c tagged.bam
samtools view tagged.bam | grep -c 'HP:i:'
最後兩行的差就是沒過 --readConfidence(0.65)那道門檻的 read。
請將差值除以總 read 數,再依資料品質與分析目標判讀;比例偏高時,回到 M6 的排查清單。
參數對應到哪一步
參數很多,但每一個都落在前面某一張圖上。按階段記比按字母記容易得多:
| 階段 | 參數 | 預設 | 它在做什麼 |
|---|---|---|---|
| 讀 BAM | -q, --mappingQuality | 1 | 比對品質低於此值的 alignment 直接丟掉 |
-x, --mismatchRate | 3 | 錯配率過高的 read 標為不可信 | |
-L, --overlapThreshold | 0.2 | 同一條 read 的多段比對若重疊超過此比例,只留較長的那段 | |
| 建邊 | -d, --distance | 300000 | 距離超過此值的兩個變異不連邊 |
-a, --connectAdjacent | 35 | 每個變異往後連接幾個變異(也就是投票的範圍) | |
-p, --baseQuality | 12 | 低於此值的鹼基,其 read 對這條邊只算 --edgeWeight | |
-e, --edgeWeight | 0.1 | 打折後的權重 | |
| 定邊方向 | -1, --edgeThreshold | 0.7 | ESR 超過此值 → 兩種組合太像,丟掉這條邊 |
| 收尾檢查 | -m, --readConfidence | 0.65 | read 要多一致才寫 HP |
-n, --snpConfidence | 0.75 | 變異要多一致才保留 PS | |
| SV 專用 | -w, --svWindow | 20 | 評估斷點周圍 CIGAR 的視窗大小 |
-h, --svThreshold | 0.10 | read 要多支持才算支持這個 SV |
指令提醒:-h 不是 help
在 phase 與 haplotag 的選項表裡,
-h 綁的是 --svThreshold(需要一個參數),
而 help 只有長選項 --help,沒有對應的短選項。
所以想看說明就要寫完整的 --help:
| 想做的事 | 寫法 |
|---|---|
| 看說明 | longphase-s phase --help |
| 調 SV 支持門檻 | -h 0.15 或 --svThreshold 0.15 |
同一張表裡 -1 是 --edgeThreshold、
-L 是 --overlapThreshold —— 短選項的字母跟參數名稱沒有明顯關聯,
所以指令寫長一點反而不容易記錯。
來源提醒:help 與實際參數不一致時
以上差異都是在原始碼裡核對過的:help 字串與 longopts 註冊表本來就不完全一致。
權威是原始碼裡的 longopts 表 —— 在
twolinin/longphase 的 repo 根目錄,
phase 看 Phasing.cpp、haplotag 看 Haplotag.cpp、
modcall 看 ModCall.cpp。使用新參數前先檢索該表確認名稱,並記錄版本。
判讀練習
你在一條染色體上完成 phasing,發現輸出的 VCF 裡有 將近 200 個不同的 PS 值。
這正常嗎?該從哪裡查起?
展開答案
數量偏高,建議檢查。在 read 長度與 heterozygous 位點密度足夠時,長讀通常可形成較大的 phase blocks;實際數量仍取決於資料品質與參數設定。
回到投票那張圖:新的 PS 只在兩邊得票相等時產生。
所以問題一定是「為什麼投票投不出結果」,可能的來源有三個層次:
- 沒有票可投。先看 read 長度分布(
samtools stats的平均與 N50)。 如果 read 只有 2–3 kb,跨不過兩個 het 位點的邊本來就少,block 破碎可能是預期結果。 - 可投票的位點太稀疏。如果 VCF 裡的 het 位點本來就少(樣本雜合度低, 或這條染色體有大段 LOHLOH 異型合子性喪失原本 heterozygous 的區域變成只剩一種 allele。LOH 不等於缺失 —— 也可能是一條 haplotype 遺失後另一條被複製(copy-neutral LOH)。Loss of heterozygosity: a formerly het region retains only one allele. Not necessarily a deletion.完整條目 →),可用錨點就會減少。
- 票都被丟掉了。邊的 ESR 若普遍偏高(兩種組合都差不多),
就會大量觸發
--edgeThreshold而整條邊被丟掉。這通常伴隨較高的錯配率 或比對問題,可用--dot把圖印出來直接看有多少邊真的存在。
再看斷點的位置分布:集中在特定區段多半是該區段有問題(LOH、重複序列、低 coverage);
均勻散布則是全域性的 read 長度或參數問題。-d(預設 300000)與
-a(預設 35)都會限制投票範圍,資料 read 特別長時可依版本文件評估是否調整。
腫瘤樣本中,LOH 是造成這種現象的常見可能性之一:該區域的兩條染色體變得難以區分, 可用錨點可能大幅減少。腫瘤專用工具因此需要額外處理 LOH 區域,否則 phase blocks 可能變得零碎。
實作練習
執行最小流程
# 僅執行小區域測試,以縮短重複測試時間
REGION="chr20:1000000-2000000"
longphase-s phase \
-s SNP.vcf \
-b aln.bam \
-r reference.fasta \
-t 8 \
-o phased \
--ont
longphase-s haplotag \
-r reference.fasta \
-s phased.vcf \
-b aln.bam \
--region "${REGION}" \
-t 8 \
-o tagged
把兩道門檻的效果量出來
這個練習把前面那張「兩道信心門檻」的圖變成實際數字。
調鬆 --readConfidence,看有多少 read 從沒有 HP 變成有:
# 預設 0.65
longphase-s phase -s SNP.vcf -b aln.bam -r reference.fasta --ont -o strict
# 調鬆到 0.55
longphase-s phase -s SNP.vcf -b aln.bam -r reference.fasta --ont -m 0.55 -o loose
# 各自 haplotag 之後比較 HP 標記率
for p in strict loose; do
total=$(samtools view -c ${p}.tagged.bam)
tagged=$(samtools view ${p}.tagged.bam | grep -c 'HP:i:')
echo "${p}: ${tagged} / ${total}"
done
標記率會上升 —— 但那不代表結果更好。放寬門檻是把不確定的 read 也貼上標籤, 標記率與正確率是兩件事,要用有 truth 的資料才分得出來(見 M10)。
確認參數名稱
# 看目前版本接受的長選項
longphase-s phase --help
# help 與實際行為不符時,以選項表為準(檔案在 repo 根目錄)
grep -A40 'static const struct option longopts' Phasing.cpp
學習檢核
原始文獻與程式碼
LongPhase:Lin JH、Chen LC、Yu SQ、Huang YT,
LongPhase: an ultra-fast chromosome-scale phasing algorithm for small and large
variants,Bioinformatics,2022。程式碼在
github.com/twolinin/LongPhase。
本模組術語
- HP tag
- BAM 裡標示某條 read 屬於哪一條 haplotype 的 tag。LongPhase-S 的 germline haplotag 寫成整數
HP:i:1;somatic haplotag 與 LongPhase-TO 則寫成字串HP:Z:1-1。 - LOH(異型合子性喪失)
- 原本 heterozygous 的區域變成只剩一種 allele。LOH 不等於缺失 —— 也可能是一條 haplotype 遺失後另一條被複製(copy-neutral LOH)。
- LongPhase
- 實驗室開發的長 read phasing 工具,是後續所有工具的共同基礎。輸出 germline haplotype。
- PS tag(phase set)
- 標示某群 variants 屬於同一 phase block 的編號。同一 PS 內的相位方向可相互比較;不同 PS 的 HP1/HP2 方向彼此獨立。
- phase block
- 一段可建立連續相位關係的區域。read 長度不足、缺少 informative heterozygous 位點或證據不一致時,可能形成不同 phase blocks。
- switch error
- phasing 結果自某一位置起將兩條 haplotype 的方向對調,是常見的 phasing 錯誤類型。