研究管線
目標:從乾淨的 checkout 加上原始 data/ 執行 bash pipeline/run_pipeline.sh 即可重建所有處理過的資料集、分析結果、報告與手稿輸出。每個產生後續步驟所需輸出的腳本,都會依相依順序由 pipeline/ 下的某個腳本呼叫。
目錄結構
pipeline/
_common.sh # 共用輔助函式,由每個 run_*.sh 引入(不直接執行)
run_pipeline.sh # 頂層協調器:依序呼叫各階段腳本
run_preprocessing.sh # 階段:src/prepro/*.R -> output/prepro/
run_analyses.sh # 階段:src/analysis/*.R -> output/analysis/
run_reports.sh # 階段:src/reports/*.qmd -> output/reports/
run_manuscript.sh # 階段:src/manuscript/* -> output/manuscript/
run_script.sh # 執行單一 .R / .qmd 並使用相同的日誌機制
階段名稱遵循專案的 src/ 資料夾名稱(例如 noh-survey 使用 src/manu/ 和 run_manu.sh)。僅為實際存在的資料夾新增階段;不要憑空建立不存在的階段。
慣例
- 每個腳本使用
#!/usr/bin/env bash與set -euo pipefail。 - 每個腳本開頭加上標頭註解,說明腳本功能、使用方式(
bash pipeline/<name>.sh)與必須先執行的階段(Requires ...)。 - 腳本透過
_common.sh切換到專案根目錄,因此可從任何目錄執行。所有路徑皆以專案根目錄為相對基準。 - 日誌:
run_pipeline.sh會匯出PIPELINE_LOG_DIR=logs/run_pipeline_<timestamp>。每個階段會建立${PIPELINE_LOG_DIR:-logs}/run_<stage>_<timestamp>/,因此當透過管線執行時,階段日誌會巢狀在管線執行目錄下;單獨執行時則放在logs/下。每個 R 腳本 / Quarto 文件會建立自己的<name>_<HH-MM-SS>.txt日誌檔。 - 失敗時,
run_r/render_qmd會印出失敗的腳本、其日誌路徑以及日檔最後 20 行,然後停止整個管線。 - 在階段內部,依照相依深度將呼叫分組,並在每組前加上
echo "=== Layer N: <說明> ==="標題。對於相依性不明顯的呼叫,在其後方加上註解(# needs output/prepro/redcap-joined.rds)。跨階段的相依性應放在獨立的標籤區塊中(例如,需要 PCA 結果的前處理腳本應放在 PCA 之後的分析階段中)。 - 每個階段會
mkdir -p其要寫入的輸出目錄。 - Quarto 會渲染到來源資料夾,因此
render_qmd會將結果移到output/。當某個資料夾中的所有報告都以相同方式渲染時,使用for qmd in src/reports/*.qmd迴圈(跳過範本)。 - 耗時的步驟前加上
echo "NOTE: ... 可能需要較長時間"。 - 保持呼叫明確,每個腳本一行。不要使用 glob 自動發現 R 腳本:順序即相依圖,且應具有可讀性。
範本
從此技能的 templates/ 目錄複製這些範本並填入內容:
| 範本 | 轉換為 |
|---|---|
_common.sh | pipeline/_common.sh(直接複製) |
run_pipeline.sh | pipeline/run_pipeline.sh |
run_stage.sh | 每個階段一個 pipeline/run_<stage>.sh |
run_script.sh | pipeline/run_script.sh(直接複製) |
_common.sh 提供:
init_log_dir <stage-name>:建立並設定LOG_DIRrun_r <script.R> [args...]:執行 R 腳本,stdout+stderr 寫入其日誌(args 會加入日誌名稱中,用於參數化執行,如 noh-survey 的 group 迴圈)run_r_console <script.R> <stdout-file>:stdout 另存為交付成果(例如output/manuscript/<name>-console.txt),stderr 寫入日誌render_qmd <doc.qmd> <dest-dir-or-file> [quarto args...]:渲染並移動輸出;將檔案路徑作為目標可重新命名(例如每個-P group:...產生的 PDF)find_quarto:回退到 RStudio/Positron 隨附的 Quarto
工作流程
1. 調查專案
- 列出
src/(以及fns/、config/)和任何現有的pipeline/。如果pipeline/已存在,在更改任何內容前先閱讀每個腳本,並在其風格與這些慣例不同時保留其風格(在遷移已建立的管線前先詢問)。 - 閱讀專案的 CLAUDE.md / README 以了解已說明的執行順序。
2. 釐清相依順序
針對 src/ 中的每個 .R / .qmd,找出其讀取和寫入的內容:
grep -nE 'readRDS|read_rds|read_csv|read_excel|read\.csv|load\(|fread|readMat|source\(' <file>
grep -nE 'saveRDS|write_rds|write_csv|ggsave|save\(|writexl|write\.csv|pdf\(|png\(|tiff\(' <file>
將每個讀取對應到寫入該檔案的腳本。沒有生產者的讀取必須是 data/ 中的原始輸入;如果不是,則標記它(這可能是缺少的管線步驟,或是手動製作的輸出)。分配層級:第 0 層僅讀取 data/;第 N 層讀取來自層級 < N 的輸出。在撰寫腳本之前,對於任何不明顯的情況,向使用者顯示結果順序。
跳過 archive/、misc/、範本(*template*)和草稿腳本,除非使用者另有說明,並列出跳過的內容。
3. 撰寫腳本
複製範本,填入階段和層級,執行 chmod +x pipeline/*.sh。
4. 檢查涵蓋率與語法
bash <this-skill-dir>/scripts/check_coverage.sh <project-root>
這會對每個管線腳本執行 bash -n 和 shellcheck(若已安裝),然後列出 src/ 中未被任何管線腳本明確提及的檔案。透過 glob 迴圈渲染的檔案會顯示為未提及,因此需要手動檢查。其餘每個檔案都要加入管線,或確認已刻意排除。
5. 重現性稽核
對 src/ 進行 grep 並報告(不要靜默修復)以下任何情況:
setwd(或絕對路徑(/Users/、C:/、~/):會在其他機器上中斷- 隨機性(
sample(、rnorm(、boot、simr、缺少set.seed)但沒有set.seed() - 僅限互動式的程式碼:
View(、file.choose(、readline(、browser( - 輸出寫在
output/之外(Quarto 的渲染後移動除外) rm(list = ls()):當腳本在同一個 session 中執行時,會隱藏缺少的相依性
6. 執行
完整管線可能需要很長時間且會覆寫 output/,因此請先詢問再執行。提供先執行單一階段或對單一檔案執行 run_script.sh 的選項。若要檢查從頭開始的可重現性,請將 output/ 移到一旁(mv output output.bak),不要刪除,然後執行 run_pipeline.sh 並比較。
7. 撰寫文件
在專案的 CLAUDE.md / README 中新增或更新「執行管線」小節:說明單一指令、每個階段的作用,以及如何執行單一腳本。
將腳本加入現有管線
- 找出新腳本讀取的內容(步驟 2),從而確定其所屬的階段與層級。
- 在該處加入一行
run_r,若相依性不明顯則加上相依性註解。 - 若其輸出供後續腳本使用,請檢查那些腳本是否仍在後面的層級中。
- 執行
scripts/check_coverage.sh。