alignDE 入門:把跑掉的層析峰對回來

零基礎版。只要會開 R、會按 Source,就能跟著做完。 先讓你在 5 分鐘內跑出一張「對位前 vs 對位後」的圖,再用白話解釋每一步在做什麼。 完全不需要先懂小波或最佳化演算法。

初學者版 5 分鐘可跑出結果 程式碼可直接複製
← 第 1 站 · HPLC 觀念入門看懂層析圖、什麼是檢量線、隨堂測驗 完全沒接觸過 HPLC 先讀這份 第 2 站 · 你正在讀 · alignDE 上手安裝與版本、5 分鐘最小範例、名詞辭典、錯誤訊息排解 第 3 站 · 進階原理 →數學推導、11 張分析圖、方法比較、15 篇參考文獻 想知道背後原理再讀

1. 一句話:alignDE 在解決什麼問題

同一台儀器、同一個方法,不同時間跑出來的層析圖, 波峰位置會偏移。alignDE 把它們推回同一個位置。

為什麼會偏移?管柱溫度變了、流動相配得略有不同、幫浦壓力有波動、管柱用久了…… 這些都會讓同一個化合物今天在 5.38 分鐘出來,明天變成 5.29 分鐘。

拿本專案的真實資料舉例:上午跑的樣品,整條層析圖比下午跑的標準品往左偏了 0.083 分鐘。 看起來很小,但已經足以讓儀器軟體的自動判峰失效。

右圖樣品的兩個峰,都明顯落在虛線的左邊——這就是偏移。

左邊是標準品(下午跑),右邊是樣品(上午跑)。虛線標的是標準品的峰位置。
左邊是標準品(下午跑),右邊是樣品(上午跑)。虛線標的是標準品的峰位置。
這批資料的儀器軟體因為「找峰的時間視窗」設得太窄, 完全沒抓到 3 個樣品的己二烯酸峰,報告上濃度直接寫 0。 實際上其中一個含有 42 ppm。峰位置偏移不只是圖不好看,是會漏檢。

2. 安裝(含版本確認)

alignDE 沒有上 CRAN,所以不能用 install.packages("alignDE")。 本專案使用的版本已發布在 GitHub(Tai-ShengYeh/alignDE),用 remotes 套件安裝即可。

步驟一:先裝依賴套件(含 remotes)

install.packages(c("DEoptim", "Matrix", "remotes"))

步驟二:從 GitHub 安裝 alignDE 3.0.1

remotes::install_github("Tai-ShengYeh/alignDE")

步驟三:確認裝到的是 3.0.1(很重要)

packageVersion("alignDE")
#> [1] '3.0.1'

# 想知道裝在哪個資料夾:
find.package("alignDE")
注意不要裝到別的 repo。 GitHub 上還有一個 zmzhang/alignDE,版本是 2.0.1(2012 年), 不是本專案需要的版本。如果你不小心執行 remotes::install_github("zmzhang/alignDE"), 裝到的會是 14 年前的舊版—— 那個版本內建自己的差分演化實作(慢 1~2 個數量級)、 函式介面也不同,本專案的腳本會跑不起來。 請務必安裝 Tai-ShengYeh/alignDE(v3.0.1)。
如果懷疑版本被換掉了,用這段一次檢查完:
cat("版本:", as.character(packageVersion("alignDE")), "\n")
cat("位置:", find.package("alignDE"), "\n")
兩者都應該顯示 3.0.1,且路徑指向 R 套件庫(GitHub 安裝不會留在本機資料夾)。 如果版本比較舊,先移除再重裝:
remove.packages("alignDE")
remotes::install_github("Tai-ShengYeh/alignDE")

附帶好處:套件自帶兩份 vignette

vignette("alignDE-basic",    package = "alignDE")   # 基礎
vignette("alignDE-advanced", package = "alignDE")   # 進階
browseVignettes("alignDE")                          # 全部列出

3. 5 分鐘快速上手

本專案附了一支最小範例 alignDE_quickstart.R。 它只做一件事:把一張偏移的層析圖對回參考圖,印出改善的數字、畫一張前後對照圖。

執行方式(挑一種)

# RStudio:開啟 alignDE_quickstart.R,按右上角 Source

# 終端機:
Rscript alignDE_quickstart.R

# R 主控台:
source("alignDE_quickstart.R")

你應該會看到這樣的輸出

使用中的 alignDE 版本: 3.0.1

參考圖找到的峰 (min): 5.375 5.925
樣品圖找到的峰 (min): 5.292 5.842
→ 兩者相差約 -0.083 分鐘,這就是要修的漂移

對位前 相似度 = 0.5081
對位後 相似度 = 0.8695   (改善 +0.3614)

相似度就是「這張圖和參考圖有多像」,範圍 0 到 1,越接近 1 越像。 從 0.51 提升到 0.87,代表對位成功了。

峰的高度不會變,只有位置被移動——這是 alignDE 最重要的特性。

上圖:對位前,紅線的兩個峰都比黑線(參考)偏左。下圖:對位後,綠線的峰和黑線對齊了。
上圖:對位前,紅線的兩個峰都比黑線(參考)偏左。下圖:對位後,綠線的峰和黑線對齊了。
峰高和峰面積完全沒被改變。 alignDE 只把峰「整段搬過去」,然後把峰與峰之間的空白區段拉長或壓縮來吸收位移。 所以對位不會影響定量結果——這也是它比其他扭曲類方法(如 COW)更適合做定量的原因。

4. 白話版:五個步驟在做什麼

快速上手的那 30 行程式碼,其實就是這五步。這裡完全不用公式解釋一遍。

  1. 找出峰在哪裡 拿一個「長得像峰」的形狀(叫做墨西哥帽小波,就是一個中間高、兩邊低的鐘形) 去掃過整條層析圖。掃到真的峰時,兩者會很像、分數就很高。 再用不同大小的帽子重複掃:真正的峰在各種大小下都會得高分(形成一條連續的「脊線」), 雜訊只有在很小的帽子下才會得高分。 要對位,先得知道有哪些峰、在哪裡。這一步比「找最高點」可靠得多,不會被雜訊騙。
  2. 量出每個峰的起點和終點 換一個階梯形狀的小波(Haar 小波)再掃一次。 訊號在峰的起點和終點斜率變化最劇烈,階梯小波在那裡會有明顯反應。 後面要算峰面積,就必須知道從哪裡積到哪裡。這一步的結果就是積分邊界
  3. 把基線扣掉 偵測器的背景訊號會慢慢漂移,讓整條線微微傾斜或彎曲。 程式擬合出這條背景線再減掉它,讓峰都站在 0 的水平線上。 擬合時只讓「非峰」的點參與,避免基線被峰拉高。 不扣的話,算面積會多算一塊背景,而且每個樣品多算的量還不一樣。
  4. 搜尋每個峰要平移多少 這是 alignDE 名字的來源。它用差分演化——一種「養一群候選答案、 讓它們互相參考彼此、一代一代改進」的搜尋方法——去試各種平移量, 找出讓這張圖和參考圖最像的那組平移量。 位移量沒有公式可以直接算出來,只能搜尋。差分演化的好處是不容易卡在錯的答案上。
  5. 用同一個視窗積分,然後定量 對位完成後,所有圖的峰都在同一個位置, 就可以用同一組積分邊界去量所有樣品的峰面積。 再把標準品的面積和已知濃度畫成一條線(檢量線),樣品的面積代進去就得到濃度。 這才是對位真正的價值:不是讓圖好看,而是讓每個樣品的積分條件完全一致,數字才可以互相比較。

下圖的兩個色塊如果套在上圖,會切到很多峰的一半——這就是不對位不行的原因。

上圖:對位前,11 張圖的峰散落各處。下圖:對位後全部疊合,兩個色塊就是共用的積分視窗。
上圖:對位前,11 張圖的峰散落各處。下圖:對位後全部疊合,兩個色塊就是共用的積分視窗。

濃度越高,填色的面積越大——這就是檢量線的基礎。

五個標準品的積分結果。紅色是苯甲酸、綠色是己二烯酸,填色的面積就是積分出來的值。
五個標準品的積分結果。紅色是苯甲酸、綠色是己二烯酸,填色的面積就是積分出來的值。

紅點(本流程自己積分的)和藍圈(儀器軟體算的)幾乎重合,代表積分是對的。

檢量線:橫軸是已知濃度,縱軸是量到的峰面積,點應該落在一條直線上。
檢量線:橫軸是已知濃度,縱軸是量到的峰面積,點應該落在一條直線上。
最後的答案長這樣:
苯甲酸:Area = 82,386.5 x C + 17,326.6(R² = 0.9971)
己二烯酸:Area = 55,565.7 x C - 48.6(R² = 0.9983)
把樣品的面積代進去,就得到濃度。R² 越接近 1 代表這條線越直、越可靠。

5. 名詞小辭典

看程式碼和文件時會遇到的詞,一句話講完。

RT(retention time)
滯留時間。化合物從進樣到流出偵測器所花的時間,單位分鐘。層析圖的橫軸。
波峰 / peak
層析圖上凸起的那一包。一個峰通常代表一種化合物。
峰面積 / area
峰下方的面積。它才是和濃度成正比的量,不是峰高。
基線 / baseline
沒有化合物流出時偵測器的背景訊號。理想是水平線,實際會漂移。
積分 / integration
算出峰面積這件事。需要先定好「從哪裡積到哪裡」。
檢量線 / calibration curve
用已知濃度的標準品建立「面積 ↔ 濃度」的對應關係,通常是一條直線。
對位 / alignment
把不同次分析的層析圖,波峰位置調整到一致。本文的主題。
CWT
連續小波轉換。用一個小小的波形掃過訊號、找出「哪裡長得像這個波形」的方法。
脊線 / ridge line
同一個峰在各種小波大小下都被偵測到,串起來形成的軌跡。長脊線 = 真的峰。
SNR
訊噪比。峰的高度相對於雜訊有多少倍。越高越確定是真的峰。
DE(differential evolution)
差分演化。一種靠「一群候選解互相參考、逐代改進」來找最佳答案的搜尋方法。
slack
允許峰最多平移幾個取樣點。設成你在資料裡看得到的最大偏移量。
λ (lambda)
基線的「硬度」。越大擬合出的基線越平滑(越接近直線)。
LOD / LOQ
偵測極限 / 定量極限。低於 LOQ 的數字不夠可靠,一般報告成「未檢出」。
N.D.
not detected,未檢出。訊號低到無法可靠定量。
ppm
百萬分之一濃度,此處等於 mg/L(毫克/公升)。

6. 參數怎麼調(先動哪一個)

參數看起來很多,但初學者其實只需要關心三個。按這個順序調:

第一優先:slack(允許平移的最大點數)

這是唯一一定要按你的資料調的參數。做法:看兩張圖同一個峰差了多少分鐘, 除以取樣間隔,再留一點餘裕。

# 本專案:偏移 0.083 分鐘,取樣間隔 0.00833 分鐘
0.083 / 0.00833   # ≈ 10 個點  →  slack 設 25 就很夠
slack 設太小 → 峰推不到位,對位失敗。 設太大 → 搜尋範圍變大、變慢,而且有機會把 A 峰錯推到 B 峰的位置。 寧可略大,但不要大到超過兩個峰的間距。

第二優先:SNR.Th(訊噪比門檻)

決定「多小的峰還算峰」。找不到峰就調低、抓到太多雜訊就調高。

SNR.Th = 3     # 保守,只抓明顯的峰(預設建議值)
SNR.Th = 1     # 本專案用這個,才抓得到比較小的己二烯酸峰
SNR.Th = 0     # 幾乎全抓,會混進雜訊,不建議

第三優先:分析區間(ROI)

不要把整條層析圖丟進去。只取你關心的峰所在的區間, 把注射時的溶劑大擾動排除掉。本專案取 4.5~7.0 分鐘。

其他參數:先用預設值就好

參數預設 / 本例什麼時候才需要動
scales1:31峰特別寬或特別窄時。上限受資料長度限制(見錯誤排解)
ridgeLength5幾乎不用動。5~10 之間
lambda(基線)100基線明顯彎曲時可以加大;但太大會出問題(見下)
n(peakClustering)5峰擠在一起時可以調小
NP / itermax60 / 150對位結果不穩定時把兩者加大
基線的 lambda 不要設太大。 本專案實測:λ 從 100 加到 10000,基線被過度平滑成一條水平線, 無視旁邊大峰的拖尾,結果把拖尾也當成小峰的訊號積進去, 讓一個微量結果從 0.60 ppm 虛胖到 0.91 ppm。那是假訊號,不是量得比較準。

7. 常見錯誤排解

點開看解法。這些都是實際跑這個專案時遇到過的。

Error: scale 32 is too large!
Error in cwt(...) : scale 32 is too large!

原因:小波的尺度不能超過訊號長度的一定比例。程式會先把資料補到 2 的次方長度, 墨西哥帽小波的最大可用尺度大約是補完長度 ÷ 16

解法(挑一個):

# A. 把 scales 上限調小(最簡單)
scales = 1:31        # 301 點 → 補到 512 → 512/16 = 32,所以最大 31

# B. 把分析區間 (ROI) 取寬一點,讓資料點變多
keep <- rt >= 4.0 & rt <= 7.5

快速算法:floor(2^ceiling(log2(資料點數)) / 16) - 1 就是安全上限。

Error: 'peakWidth' contains no peak.
Error in alignDE(...) : 'peakWidth' contains no peak.

原因:這張圖一個峰都沒偵測到,所以沒東西可以對位。

解法:SNR.Th 調低(例如 3 → 1),或確認 ROI 有涵蓋到峰的位置。 先畫圖確認峰真的在你以為的地方:

plot(rt, mv, type = "l", xlim = c(4.5, 7.0))
每次跑出來的結果都不一樣

原因:差分演化是隨機演算法,每次的起始族群不同。

解法:在呼叫 alignDE() 之前固定亂數種子。這是可重現分析的必要動作:

set.seed(1)         # 任何數字都可以,重點是固定
aligned <- alignDE(...)

如果固定種子後差異還是很大,代表搜尋沒收斂,把 NPitermax 加大(例如 100 / 300)。

對位後相似度還是不高,是失敗了嗎?

不一定。相似度衡量的是「整條曲線的形狀」。 如果兩個樣品的成分本來就不同——例如一個只含苯甲酸、另一個只含己二烯酸—— 就算峰位置完全對齊了,相似度也到不了 1。

正確的判斷方式:畫圖看峰位置有沒有對齊,不要只看相似度的數字。

plot(RT, ref_sig, type = "l")
lines(RT, aligned, col = "red")     # 峰對齊了就是成功
there is no package called 'alignDE' / 'DEoptim'
Error in library(alignDE) : there is no package called 'alignDE'

解法:回到第 2 節重裝。注意順序:先 DEoptimMatrixremotes,再 alignDE。

install.packages(c("DEoptim", "Matrix", "remotes"))
remotes::install_github("Tai-ShengYeh/alignDE")
packageVersion("alignDE")    # 應該是 3.0.1
裝完卻是舊版(例如 2.0.1)

原因:幾乎都是因為裝到 zmzhang/alignDE(GitHub 上另一個舊 repo), 或 R 從別的 library 路徑載入了舊版。

# 看清楚裝了幾份、各是什麼版本
for (p in .libPaths()) {
  d <- file.path(p, "alignDE")
  if (dir.exists(d))
    cat(d, "->", read.dcf(file.path(d, "DESCRIPTION"))[1, "Version"], "\n")
}

# 全部移除再重裝正確的版本
remove.packages("alignDE")
remotes::install_github("Tai-ShengYeh/alignDE")
中文檔名或中文輸出變成亂碼

原因:儀器匯出檔是 Big5 (CP950) 編碼,不是 UTF-8。

解法:本專案的 lcsolution_io.R 已經處理好了,直接用它的 get_chromatogram() 讀檔就不會有問題。 如果要自己讀,記得指定編碼:

con <- file("檔名.txt", encoding = "CP950")
lines <- readLines(con, warn = FALSE); close(con)

8. 接下來做什麼

自我檢查清單

換成你自己的資料

alignDE_quickstart.R 開頭的兩個檔名換掉,再依第 6 節調三個參數即可:

REF_FILE <- "你的參考層析圖.txt"
SMP_FILE <- "你要對位的層析圖.txt"

如果你的檔案不是 Shimadzu LCsolution 匯出格式,就自己讀成兩個向量 (時間 rt 和訊號 mv)餵進去,其餘都不用改。

跑完整流程

Rscript hplc_alignDE.R          # 對位 + 積分 + 檢量線 + 定量,輸出 11 張圖
python  build_teaching_html.py  # 產生進階版教學網頁

三份教學文件的分工

文件範圍什麼時候讀
teaching_beginner.html HPLC 數據處理觀念:看懂層析圖、檢量線是什麼、怎麼判斷做得對不對、隨堂測驗 完全沒接觸過 HPLC
本文 alignDE 實際操作:安裝與版本確認、5 分鐘最小範例、名詞辭典、參數調整順序、錯誤訊息排解 要動手跑 alignDE
teaching_alignDE.html 原理與驗證:CWT / Whittaker / 加權迴歸的推導、CWT 尺度圖與脊線、基線方法比較實驗、15 篇參考文獻 想知道為什麼這樣做有效
本專案還有另一套流程 hplc_calib.R / hplc_calib.py, 直接採用儀器軟體算好的峰面積來建檢量線,不做對位與自行積分。 兩套流程的結果差異在 5% 以內,可以互相驗證。