Snakemake
¶
Warning
This notebook is a demo for Python scripting that you could run before and after Snakemake runs. Typically, you would include lamindb directly within your Snakemake workflow.
Snakemake is a popular workflow manager in bioinformatics. This guide is based on the example of the rna-seq-star-deseq2 pipeline.
First we clone the Snakemake pipeline with git. Because the test datasets come with the repo and, for simplicity, we want to avoid moving them into another directory, we initialize a LaminDB instance in the same directory.
# pip install lamindb snakemake
git clone https://github.com/snakemake-workflows/rna-seq-star-deseq2 --single-branch --branch v3.1.0
lamin init --storage ./rna-seq-star-deseq2
Show code cell output
Cloning into 'rna-seq-star-deseq2'...
Note: switching to '5fbe51c94aa1f5c8c8cbdca379eb00436d6491bf'.
You are in 'detached HEAD' state. Yo
u can look around, make experimental
changes and commit them, and you can discard any commits you ma
ke in this
state without impacting any branches by switching back to a branch.
If you want to creat
e a new branch to retain commits you create, you may
do so (now or later) by using -c with the switc
h command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
! using anonymous user (to identify, call: lamin login)
→ initialized lamindb: anonymous/rna-seq-star-deseq2
import lamindb as ln
import subprocess
from pathlib import Path
Show code cell output
→ connected lamindb: anonymous/rna-seq-star-deseq2
Registering inputs¶
root_dir = "rna-seq-star-deseq2"
sample_sheet = ln.Artifact(f"{root_dir}/.test/config_basic/samples.tsv").save()
input_fastqs = ln.Artifact.from_dir(f"{root_dir}/.test/ngs-test-data/reads/")
ln.save(input_fastqs)
Show code cell output
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! no run & transform got linked, call `ln.track()` & re-run
! there are multiple artifact uids with the same hashes, dropping 2 duplicates out of 10 artifacts:
yunAXzGDiQBwCT5y0000
WLYfAcz7UBEl7bpQ0000
Track a Snakemake run¶
Track the Snakemake workflow & run:
transform = ln.Transform(
key="snakemake-rna-seq-star-deseq2",
version="2.0.0",
type="pipeline",
reference="https://github.com/snakemake-workflows/rna-seq-star-deseq2",
)
ln.track(transform)
Show code cell output
/tmp/ipykernel_4884/1213032725.py:1: DeprecationWarning: `type` argument of transform was renamed to `kind` and will be removed in a future release.
transform = ln.Transform(
→ created Transform('5KetorR9DDu80000', key='snakemake-rna-seq-star-deseq2'), started new Run('acrp4BK7sj3x0eXX') at 2026-08-11 08:54:43 UTC
If we call cache() on the input artifacts, they’ll be downloaded into a cache and tracked as run inputs. In this test case however, no download happened because the files are already available locally.
input_sample_sheet_path = sample_sheet.cache()
input_paths = [input_fastq.cache() for input_fastq in input_fastqs]
Let’s run the pipeline.
To make this robust in CI, we target outputs that don’t depend on live Ensembl biomaRt lookups (which can be intermittently unavailable).
subprocess.run(
[
"snakemake",
"--directory",
"rna-seq-star-deseq2/.test",
"--snakefile",
"rna-seq-star-deseq2/workflow/Snakefile",
"--configfile",
"rna-seq-star-deseq2/.test/config_basic/config.yaml",
"--use-conda",
"--show-failed-logs",
"--cores",
"2",
"--conda-frontend",
"conda",
"--conda-cleanup-pkgs",
"cache",
"results/counts/all.tsv",
"results/qc/multiqc_report.html",
],
check=True,
)
Show code cell output
CompletedProcess(args=['snakemake', '--directory', 'rna-seq-star-deseq2/.test', '--snakefile', 'rna-seq-star-deseq2/workflow/Snakefile', '--configfile', 'rna-seq-star-deseq2/.test/config_basic/config.yaml', '--use-conda', '--show-failed-logs', '--cores', '2', '--conda-frontend', 'conda', '--conda-cleanup-pkgs', 'cache', 'results/counts/all.tsv', 'results/qc/multiqc_report.html'], returncode=0)
Registering outputs¶
Quality control.
multiqc_file = ln.Artifact(f"{root_dir}/.test/results/qc/multiqc_report.html").save()
How would I register all QC files?
multiqc_results = ln.Artifact.from_dir(f"{root_dir}/results/qc/multiqc_report_data/")
ln.save(multiqc_results)
Count matrix.
count_matrix_path = Path(root_dir) / ".test/results/counts/all.tsv"
if not count_matrix_path.exists():
raise FileNotFoundError(
f"Expected output not found: {count_matrix_path}. "
"Inspect Snakemake logs under rna-seq-star-deseq2/.test/logs/"
)
count_matrix = ln.Artifact(count_matrix_path).save()
Visualize¶
View data lineage:
count_matrix.view_lineage()
Show code cell output
! calling anonymously, will miss private instances
Appendix¶
Linking biological entities¶
To make the count matrix queryable by biological entities (genes, experimental metadata, etc.), we can now proceed with: Bulk RNA-seq
Linking a Snakemake run ID¶
Snakemake does not have an easily accessible ID that is associated with a run. Therefore, we need to extract it from the log files.
import pathlib
from datetime import datetime
PATH_TO_DOT_SNAKEMAKE_LOG = "rna-seq-star-deseq2/.test/.snakemake/log"
log_files_file_names = list(
map(
lambda lf: str(lf).split("/")[-1],
list(pathlib.Path(PATH_TO_DOT_SNAKEMAKE_LOG).glob("*.snakemake.log")),
)
)
timestamps = [
datetime.strptime(filename.split(".")[0], "%Y-%m-%dT%H%M%S")
for filename in log_files_file_names
]
snakemake_id = log_files_file_names[timestamps.index(max(timestamps))].split(".")[1]
Let us add the information about the session ID to our run record:
run = ln.context.run # let's grab the global run record
run.reference = snakemake_id
run.reference_type = "snakemake_id"
run.save()
Show code cell output
Run(uid='acrp4BK7sj3x0eXX', name=None, description=None, entrypoint=None, started_at=2026-08-11 08:54:43 UTC, finished_at=None, params=None, extra_data=None, reference='234746', reference_type='snakemake_id', cli_args=None, branch_id=1, created_on_id=1, space_id=1, transform_id=1, report_id=None, environment_id=None, plan_id=None, created_by_id=1, initiated_by_run_id=None, created_at=2026-08-11 08:54:43 UTC, is_locked=False)