Snakemake チートシート

[Snakemake 公式](https://snakemake.readthedocs.io/en/stable/index.html) | [Snakemake GitHub](https://github.com/snakemake/snakemake)

Snakemake は、GNU Make のようなタスクランナーの一つです。ファイルの依存関係をシンプルに書きつつ、Python で好きな処理がかけるのが魅力です。

## ルールの書き方

```python
rule all:
    input: "output-A.txt"

rule hoge:
    input: "input-{sample}.txt"
    output: "output-{sample}.txt"
    shell: "myscript.sh {input} > {output}"
```

| パラメータ | 説明 |
|:--|:--|
|input:   |入力ファイルの指定。`{sample}` 等でワイルドカードを指定できる。|
|output:  |出力ファイルの指定。`input:` の指定したワイルドカードを使うことができる。|
|shell:   |実行するコマンド。`{input}`, `{output}` などを利用可。|
|run:     |実行するPythonコード。`shell:` と `run:` は同時に指定できない。|

その他のパラメータ: `params:`, `log:`, `benchmark:`, `message:`, `threads:`, `resources:`, `version:`, 

リファレンス

- [Writing Workflows — Grammar](https://snakemake.readthedocs.io/en/stable/snakefiles/writing_snakefiles.html#grammar)
- [Rules](https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html)

## 定義済み変数

`input:` 以外のパラメータでは `{input}`, `{output}` などとして定義済み変数にアクセスできる。トップレベルのコード部や `run:` パラメータでは、通常の変数としてアクセス可。

| 変数 | 説明 |
|:--|:--|
|{input}|`input:`に対応するファイル、またはファイル群（スペース区切り）|
|{output}|`output:`に対応するファイル、またはファイル群（スペース区切り）|
|{wildcards.*}|`input:`, `output:` で指定したワイルドカードの集まり。|
|{params.*}|`params:` で設定した文字列の集まり。|
|{rules.*}|定義された `rule:` の集まり。|
|{checkpoints.*}|定義された `checkpoint:` の集まり。|
|{config}|`configfile:` で読み込んだファイルの設定。|
|{rule}| ( `shell:`, `run:` のみアクセス可。)現在のルール名(str)|

`{.*}` となっている箇所は、`{wildcards.sample}`等、属性に個々のアイテム名を指定する。

`input:` では `{hoge}` とした部分はすべてワイルドカード定義として扱われる。

## 定義済みトップレベル要素

`rule:` 以外にも、下記の要素を定義することができます。その他、通常の Python コードを記述することが可能です。

| 要素 | 説明 |
|:--|:--|
|rule:|ルール指定（前述）|
|checkpoint:|特殊なルール指定（使い方は後述）|
|configfile:|YAML 形式の外部変数定義を読み込む。|
|include:|他の Snakefile を取り込む|
|workdir:|カレントディレクトリを指定|
|subworkflow:| (未調査) |


## 定義済み関数（抜粋）

<table style="table-layout: fixed">
<tr><th width="500">関数</th><th>説明</th></tr>
<tr><td>expand</td><td>wildcard を展開した結果の str のリストを返す。最終成果物のリストの生成などで頻繁に使うことになる（はず）。</td></tr>
<tr><td>glob_wildcards</td><td>ファイル名探索をする glob のワイルドカード版。得られるのが、ファイル名ではなく、ワイルドカードであることに注意。（普通にファイル名の一覧が必要であれば標準ライブラリ(`glob.glob`等)を普通に使える）</td></tr>
<tr><td>touch</td><td>output: セクションで使用。処理完了後に touch で指定されたファイルの更新時刻を更新する。</td></tr>
<tr><td>directory</td><td>output: セクションで使用。出力先としてディレクトリを指定するときに使用。<a href="https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#directories-as-outputs">参考</a></td></tr>
<tr><td>temp</td><td>output: セクションで使用。引数で指定されたファイルは、ジョブ成功時でも削除される。</td></tr>
<tr><td>protected</td><td>output: セクションで使用。引数で指定されたファイルは、ジョブ失敗時にも削除されない。</td></tr>
<tr><td>remote</td><td>input:, output: セクションで使用。詳細は別ページで説明予定</td></tr>
</table>

## [ワイルドカードの書式](https://snakemake.readthedocs.io/en/stable/snakefiles/rules.html#wildcards)

| 書式 | 説明 |
|:--|:--|
|{sample}|デフォルト。正規表現としては `.+` が指定されたのと同じ|
|{sample,[A-Z]+}|`,`の後ろにワイルドカードがマッチする正規表現を指定可能   |
|{sample:02d}|Python標準のフォーマット指定も利用可   |
|{sample:q}|文字列をクォートする|

## [コマンドラインオプション](https://snakemake.readthedocs.io/en/stable/executing/cli.html#all-options)

| オプション | 説明 |
|:--|:--|
| -n, --dryrun  | dryrun  |
| -p, --printshellcmds  | 実行したコマンドを標準出力に出す（デフォルトでは出ない）  |
| -f, --force  | 依存関係を無視して強制実行する  |
| -R [TARGET, TARGET, ...], --forcerun [TARGET, TARGET, ...] | `TARGET` 以降のルール（`TARGET` と、`TARGET` に依存しているルール）を再実行する|
| -U TARGET[, TARGET, ...], --until TARGET[, TARGET, ...] | `TARGET` 以前のルール （`TARGET` が依存しているルール） を再実行する|
| -s filename, --snakefile filename | Snakefile を指定  |
| -d, --directory | ワーキングディレクトリを指定 |
| -l, --list  | Snakefile の rule 一覧を出力する |
| --lt, --list-target-files  | rule のうち、`snakemake RULE` として指定可能なルール (=ルールにwildcardを含まないもの) を出力する。なお、`-lt` とすると、`-l`, `-t(--touch)` と解釈され、`--lt` とはならないので注意。 |
|-t, --touch|`output`のファイルの更新時間だけを更新し、コマンドを実行しない。`output` のファイルがない場合には何も起こらないことに注意（空ファイルが作成されたりはしない）|
|-k, --keep-going|途中でエラーが発生しても、実行可能なジョブは実行する（デフォルトはエラー発生次第、即座に止まる）|


## 定番の書き方

### default ターゲット

ターゲット指定が省略された場合、Make と同じく一番上の rule がターゲットとして指定された扱いになる。

`input:` に、最終的に生成されるファイルを指定することで、必要な依存関係をたどって生成に必要な `rule:` 群が実行される。

```python
rule all:
    input: "final-result.txt"
```

### 分析の途中から再実行

ワイルドカード付きのルールを再実行したい場合、`-R (--forcerun)` オプションを使います。

```sh
% snakemake -R step2 all
```

最終的にワイルドカードを展開しているルールを指定しつつ、 `-R` オプションで再実行するターゲットを指定します。

単に `-f` オプションで指定すると、ワイルドカードの展開ができずエラーになります。

```sh
% snakemake -f step2
Building DAG of jobs...
WorkflowError:
Target rules may not contain wildcards. Please specify concrete files or a rule without wildcards.
```

参考 [Can SnakeMake be forced to rerun rules when files are missing \- Stack Overflow](https://stackoverflow.com/questions/45989802/can-snakemake-be-forced-to-rerun-rules-when-files-are-missing)

### 分析の特定ステップだけを再実行

先程の例は step2 以降を再実行でしたが、step2 だけを再実行したい場合はどうでしょうか。その場合は `-U` オプションを利用します。

```sh
% snakemake -R step2 -U step2 all
```

ちょうど `-R` で指定したターゲットから、`-U`で指定したターゲットまでが実行されます。両方に同じターゲットを指定すると、そのターゲットだけが実行されるようになります。

### .env の読み込み

`Snakefile` には通常の Python スクリプトを書けますので[^1]、たとえば `pip install python-dotenv` を実行しておいて、`Snakefile` に

```python:Snakefile
from dotenv import load_dotenv

load_dotenv()

# 以後、通常のルール記述
rule all:
    input: ...
```

などと書けばOKです。

<!--
```python
import smutil

smutil.load_env()
```
-->

（逆にいえば、snakemake 標準では .env の読み込み機能は提供されていない模様。）

[^1]: 正確には「ルール記述が通常の Python 関数に置き換えられて、Python スクリプトとして実行される」

#### `input:` パラメータに変数の値を指定する

```
var_a = 'some_text'

rule hoge:
    input: "{var_a}"
```

などと書いても、`{var_a}` というワイルドカードを定義したことになり、変数の値は入りません。変数の値をいれるためには、例えば下記の方法があります。

1. `input: f'{var_a}'` とする。（ワイルドカードを使う場合は `f'{var_a}-{{wc_a}}'` とワイルドカード側を `{{}}` とする。）
1. `input: expand('{var_a}', var_a=var_a)` として、ワイルドカード展開を利用する

<!--
### 出力ファイルが不定の場合の書き方

`output:` がツールの出力結果によって動的に決まる場合、`rule:` の代わりに `checkpoint:` を使う。後につづくルールで、`checkpoint` から出力結果を得ることができる。

```python
checkpoint hoge:
    input: "input-{sample}.txt"
    output: "output-{sample}.txt"
    shell: "myscript.sh {input} > {output}"
```
-->