--- tags: rdm-hub --- # Binder – Snakemake 使用教學 文/王俐晴(中央研究院資訊科學研究所實習生) [Snakemake](https://snakemake.github.io/) 是一個處理工作流程的工具,透過與其他軟體套件 (例如:samtools、BWA 等) 的整合,或單純使用 shell 和 Python 指令,建立規則 (rule) 以實現對資料的系統化分析,並產製結果報告。在功能上與 Makefile 類似,但支援的設定更多,書寫簡單,邏輯易懂,更具使用者友善性。 利用 Snakemake 來讓研究人員將其含有研究方法 (分析順序) 的 Snakefile 和相關資料上傳至資料儲存庫(如 depositar)。使其他使用者在重現研究成果或利用自身資料集進行驗證時,可透過將資料上傳至 Binder 等運算環境,並在終端機輸入簡單指令,自動執行 Snakefile 中定義的工作流程。為了達成不同的需求,Snakemake 支援引用外部資源 (例如 GitHub),或在分析複雜的資料時,將資料的處理分為不同的階段,達到多層次的工作流程。最後,Snakemake 還能產製工作流程圖 (DAG 有向無環圖) 或成果報告 (如 .zip 或 HTML 格式)。 ## 撰寫工作流程檔案 (Snakefile) ### Rule (規則) 如下方示意,可把 rule 想像為一件捆綁在一起的工作,有時希望多個不相關的工作可以同步執行時,可以將它們設為不同的 rule,以分攤工作,平衡流程。 ```makefile= rule all: input: "new_report.html" rule unzip_file: # rule 後接 名稱 input: "resource.zip" output: "resource/tp-marriage.csv", "resource/tp-economy.csv" shell: "unzip {input} -d ./" ``` > [!TIP] > ```rule all``` 是在 ```$ snakemake``` 的時候預設的執行目標,通常代表所有工作都要執行。 ### Rule attribute (規則屬性) 定義工作內容,如要拿取或輸出的資料、欲執行的處理過程、分配給這項工作的資源等。 + input: + 放入這個 rule 會用到的檔案 (可以不用有使用到的檔案都放),形成一個從其他地方拿入資料的概念。相同的檔案會放在前一個 rule 的 output 來強調順序,下一個規則屬性會提到 + 路徑名需加 ```" "``` (例如 ```"resource.zip"```) + 多個輸出以 ```,``` 分開 (例如 ```"report.zip", directory("data")```) + 有放在 input 裡的檔案,在相同 rule 的其他地方使用時,可以使用 ```{input}``` 取代 + 有多個輸入檔案時,在其他地方使用需按照在 input: 中的順序,以 ```{input[0]}```,```{input[1]}```...類推使用。用法類似 output 屬性,可見下方 output 最後一點的說明 + 反之,若沒有在 input: 中書寫,在其他屬性中使用則得打出完整路徑 (如 ```workflow/Snakefile```) + 指定要拿取某 rule 的輸出做為輸入,可使用 ```rules.<欲拿取的 rule 名稱>.output``` + output: + 放入這個 rule 會輸出的檔案 (可以不用會輸出到的檔案都放),形成一個給其他地方資料的概念。(相同的檔案會放在下一個 rule 的 input,使下一個 rule 可以依靠此 rule 的 output 建立順序) + 路徑名需加 ```" "``` (例如 ```"report.zip"```) + 多個輸出以 ```,``` 分開 + 有放在 output 裡的檔案,在相同 rule 的其他屬性使用時,可以使用 ```{output}``` 取代 ; 若是有多個 output 時,則需按照在 output: 的順序以 ```{output[0]}```, ```{output[1]}```...類推使用 (見範例一 shell: 下方的 ```output[0]``` 與 ```output[1]```,分別依順序對應了 output: 中的 ```report.zip``` 與 ```second_file_in_output_example.txt```) ```makefile # 範例一 rule generate_report: input: rules.run_workflow.output output: "report.zip", "second_file_in_output_example.txt" shell: ''' snakemake -s workflow/Snakefile --report {output[0]} rm -rf {input} touch {output[1]} ''' ``` + shell: + 寫入所要執行的 shell 指令 + 單個指令用 ```" "``` (例如 ```"snakemake -s workflow/Snakefile --report {output}"```) + 多個指令用 ```''' '''``` 來包住指令 (例如```''' <..shell command..> '''```) + 指令與 Python 程式碼都要用```""" """```包住全部 + Python 程式碼需另外以 ```python -c " <..Python code..> "``` 包圍 (如下方範例二) ```makefile # 範例二 rule data_update: input: rules.deploy.output[0], rules.deploy.output[1] output: "workflow/new_snakefile" shell:""" python -c " import re # Read the file /* more */ " mv {input[0]} {output} # shell 指令 """ ``` + run: + 直接放入 Python 程式碼,不用 ```" "``` + 加入 shell 指令用 ```shell("<..shell command..>")``` ```makefile # 範例三 run: shell("unzip {input} -d {output}") with open(input.report) as f: report_content = f.read() # 讀取 CSV 文件內容 csv_content1 = open(input.m_csv).read() /* more */ ``` + script: + 放入要執行的 Python 檔案 ```makefile # 範例四 rule ebar_chart: input: "resource/tp-economy.csv" output: "result/bar/e-bar.html" script: "scripts/make_bar_chart.py" ``` + log: + 輸出日誌文件的路徑 + 路徑名需加 ```" "``` + 在 shell 的指令候用 ```… 2> {log}``` 使用 (見範例五) + 執行後可在此檔案看到執行紀錄 + params: + 傳遞額外的參數到 shell 指令或其他地方 + 以 ```{params}``` 使用 (如範例五: ```{params.extra_params}``` 就等同於指定取用 params: 裡面的 ```extra_params```,也就是 ```--option1 value1 --option2 value2```) + threads: + 規則所需的 CPU 核心數量 + resources: + 規則所需的記憶體與磁碟空間等 ```makefile # 範例五 log: "process_data.log" params: extra_params="--option1 value1 --option2 value2" shell: "process_data {input} {params.extra_params} > {output} 2> {log}" ``` ### 設定文件 (config.yaml) 可將需多次使用的路徑或資源放置於設定文件,減少 Snakefile 中不必要的重複書寫。 ```yaml # config.yaml samples: A: data/samples/A.fastq B: data/samples/B.fastq C: data/samples/C.fastq ``` ```makefile # Snakefile def get_bwa_map_input_fastqs(wildcards): return config["samples"][wildcards.sample] rule bwa_map: input: "data/genome.fa", get_bwa_map_input_fastqs # "data/samples/{sample}.fastq" output: "mapped_reads/{sample}.bam" params: rg=r"@RG\tID:{sample}\tSM:{sample}" log: "logs/bwa_mem/{sample}.log" threads: 8 shell: "(bwa mem -R '{params.rg}' -t {threads} {input} | " "samtools view -Sb - > {output}) 2> {log}" configfile: "config.yaml" # config file 的路徑 /..more../ rule bcftools_call: input: fa="data/genome.fa", bam=expand("sorted_reads/{sample}.sorted.bam", sample=config["samples"]), bai=expand("sorted_reads/{sample}.sorted.bam.bai", sample=config["samples"]) /..more../ ``` + ```config["samples"][wildcards.sample]``` 取得 config 檔案裡 samples: 下的路徑。在 shell 中使用 ```{input}``` 時會將三個路徑都當輸入跑一遍 + ```{sample}``` 是萬用字元 ([wildcards](https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#wildcards)),會得到樣本名稱 (不是路徑,所以 ```logs/bwa_mem/``` 裡面是 A, B, C 的 log 檔) + ```bam=expand("sorted_reads/{sample}.sorted.bam, sample=config["samples"])``` 會讓 ```bam = sorted_reads/{A,B,C}.sorted.bam``` 三個檔案,所以在 shell 用 ```{input.bam}``` 時會執行到三個檔案 ### 常用 Snakemake 命令列工具參數 您可使用以下指令執行撰寫的 Snakefile 流程: + ```-n (--dry-run)```:模擬試跑 workflow (除錯用)。如:```$ snakemake -n``` + ```-p```:列出所有紀錄 (除錯用)。如:```$ snakemake -p``` + ```-f```:強制重新跑完整 workflow。如: ```$ snakemake -f``` + ```-s```:指定欲執行的 Snakefile。如: ```$ snakemake -s new_snakefile``` ### 製作 Snakemake 報告 (report) Snakemake 於製作 Snakemake report 時,會加入流程圖 (Workflow)、執行時間 (Statistics)、使用套件等其他相關資訊 (About)。 ![](https://hackmd.io/_uploads/HkM6UCf9Je.png) 您並可在 input: 或 output: 中使用 ```report(...)```,將各流程的結果加入報告。參數如下: + category: 在首頁 Result 顯示,指定文件在報告中的分類 + caption: 指定文件的說明文字 + labels: 文件在報告中的標籤 ```makefile rule ebar_chart: input: report("resource/tp-economy.csv", category=lambda w: "Data Source", caption="report/csv2.rst") output: "result/bar/e-bar.svg" conda: "environment.yaml" script: "scripts/make_bar_chart.py" ``` 將輸出如下圖之報告: ![](https://hackmd.io/_uploads/B1ZJvRzqJx.png) 產製 report 的指令如下: + 輸出工作流程圖 (DAG 有向無環圖):```$ snakemake --dag <目標 Snakefile 名稱> | dot -Tsvg > <輸出 DAG 圖名>.svg``` + 輸出 report (內建 workflow, statistics, about):```$ snakemake -s 目標 Snakefile 名稱> -–report <欲產生的 report 路徑與名稱 (.zip or .html …)>``` ## 於 Binder 環境執行 Snakemake > [!TIP] > 目前若要在 Binder 中使用 Snakemake 需要另行安裝,可將如下內容的 Binder 環境設定檔 (environment.yml) 上傳為 depositar 的資源,以在開啟 Binder 時一併安裝 Snakemake 於 Binder 所提供的 JupyterLab 環境中 (也可以在開啟 Binder 後手動在 terminal 安裝)。 > [!TIP] > 受 xsrf 限制,Binder 環境下無法正常瀏覽 Snakemake HTML 報告,請下載至本機瀏覽。 > [!IMPORTANT] > 副檔名必須為 .yml (而非 .yaml),否則不會安裝該檔案所定義的相依套件。 :::spoiler environment.yml ```yaml= # 放置執行工作需要有的環境資源 name: environment # 環境名稱 channels: - conda-forge - bioconda # 生物資訊套件的 channel dependencies: # 函式庫 - snakemake-minimal >=8.4.4 # 可強調版本 - python - matplotlib - pandas - numpy - plotly - folium - pip # 這裡是 Conda 自身的 pip 套件 - cwltool # 如果有需要從 PyPI 安裝的套件,可以這樣放在 pip 下 (conda沒有的) # - pip: # - python_package_not_in_conda ``` ::: ## 範例 :::info [台北市金價與結婚人數分析](https://pid.depositar.io/ark:37281/k5b8f3n3j)資料集,包含以下內容: + environment.yml: Binder 環境設定檔 + Snakefile: 工作流程檔案 + report.zip: 報告說明文字 + resource.zip: 原始資料 + scripts.zip: 繪製金價與結婚對數長條圖的 Python 程式 :::spoiler 壓縮檔內容 (執行時無需解壓縮) ```bash . ├── report # 來自 report.zip │ ├── csv1.rst │ ├── csv2.rst │ └── workflow.rst ├── resource # 來自 resource.zip │ ├── tp-economy.csv │ └── tp-marriage.csv └── scripts # 來自 scripts.zip └── make_bar_chart.py ``` ::: :::spoiler Snakefile ```makefile= report: "report/workflow.rst" from snakemake.utils import min_version min_version("6.10.0") rule all: input: "report.html" rule unzip_file: input: "resource.zip", "scripts.zip", "report.zip" output: "resource/tp-marriage.csv", "resource/tp-economy.csv", "scripts/make_bar_chart.py" shell: ''' unzip {input[0]} -d ./ unzip {input[1]} -d ./ unzip {input[2]} -d ./ ''' rule mbar_chart: input: report("resource/tp-marriage.csv", category=lambda w: "Data Source", caption="report/csv1.rst") output: "result/bar/m-bar.svg" conda: "environment.yaml" script: "scripts/make_bar_chart.py" rule ebar_chart: input: report("resource/tp-economy.csv", category=lambda w: "Data Source", caption="report/csv2.rst") output: "result/bar/e-bar.svg" conda: "environment.yaml" script: "scripts/make_bar_chart.py" rule custom_report: input: "result/bar/m-bar.svg", "result/bar/e-bar.svg" output: report( "m-bar.svg", category=lambda w: "Marriage Diagram", labels=lambda w, params: {"type": "figure", "num": "1"}, ), report( "e-bar.svg", category=lambda w: "Gold Price Diagram", labels=lambda w, params: {"type": "figure", "num": "1"}, ), "first.txt" shell: ''' sleep $(shuf -i 1-3 -n 1) cp {input[0]} {output[0]} cp {input[1]} {output[1]} touch {output[2]} ''' rule generate_report: input: "first.txt", "m-bar.svg", "e-bar.svg", "resource/tp-marriage.csv", "resource/tp-economy.csv" output: "report.html", shell: ''' snakemake --report {output[0]} rm -rf first.txt ''' ``` ::: [於 Binder 服務執行此資料集](https://binder.depositar.io/v2/ckan/https://data.depositar.io/dataset/b9909),在開啟的 JupyterLab 環境所提供的終端機 (Terminal) 中,輸入 ```snakemake```,即可執行流程。完成的長條圖位於 `result` 資料夾內,報告輸出於 `report.html`。 :::spoiler 參考輸出 ```bash Assuming unrestricted shared filesystem usage. host: jupyter-dataset-b9909-0s6d8kk7 Building DAG of jobs... Using shell: /usr/bin/bash Provided cores: 4 Rules claiming more threads will be scaled down. Conda environments: ignored Job stats: job count --------------- ------- all 1 custom_report 1 ebar_chart 1 generate_report 1 mbar_chart 1 unzip_file 1 total 6 Select jobs to execute... Execute 1 jobs... (中間省略) [Wed Feb 19 09:23:37 2025] localrule all: input: report.html jobid: 0 reason: Input files updated by another job: report.html resources: tmpdir=/tmp [Wed Feb 19 09:23:37 2025] Finished job 0. 6 of 6 steps (100%) done ``` ::: ## 結語 以上為簡單的 Snakemake 使用教學,因為研究過程中主要是為了協助使用者順利在 [depositar](https://data.depositar.io/) 的 Binder 功能上順利使用 Snakemake,所以皆搭配了一些在 Binder 使用時的注意事項。若欲知更多深入的 Snakemake 功能,可以上 [Snakemake 官方網站](https://snakemake.github.io/)閱讀更詳細的說明。