Flexible record parsing and demultiplexing to FASTX/SAM/BAM/CRAM, splitcode-style.
Install with mamba, conda, or run directly with pixi:
pixi exec \
-c conda-forge -c bioconda \
unmux --helpThe tool unmux reads multiple FASTX/SAM/BAM/CRAM inputs, identifies and extracts technical sequences (barcodes, UMIs, adapters) with error tolerance using the sassy approximate matcher, and writes FASTX/SAM/BAM/CRAM files with preserved per-record segment qualities, fanning a record pool out into per-sample, and optionally per-sample and per-library, files in a single pass.
It aims to do in one shot what otherwise would take a combination of separate tools.
barQC: extract, error-correct, and determine combinatorial cell barcodes and UMIs into uBAMfgbio FastqToBam: stitch multiple FASTQs into one unmapped BAM via read structures and SAM tagsfqtk: fast sample demultiplexing driven by per-read barcode structuresPicard FastqToSam: convert raw FASTQs into an unmapped BAMqualrepair: repair the base qualities that splitcode mangles during extractionsamtools split: split one BAM into per-read-group (per-sample) BAMssplitcode: identify, extract, and edit technical sequences from a declarative configsplitseq-toolbox: demultiplex SPLiT-seq combinatorial split-pool cell barcodesUMI-tools extract: pull UMIs out of records and onto the read names
Process a SPLiT-seq run (splitcode's SPLiT-seq example) in one call: cDNA template on R1, 10 bp UMI and three rounds of 8 bp cell barcodes on R2, emitted as an unmapped BAM with CB and RX tags containing the error-corrected/canonical barcodes and their associated quality scores in tags CY and QX:
unmux "R1.fastq.gz" "R2.fastq.gz" \
--group "round1=round1.tags.txt" \
--group "round1::loc=1:78:86" \
--group "round1::dist=1" \
--group "round1::minFindsPerGroup=1" \
--group "round2=round2_3.tags.txt" \
--group "round2::loc=1:48:56" \
--group "round2::dist=1" \
--group "round2::minFindsPerGroup=1" \
--group "round3=round2_3.tags.txt" \
--group "round3::loc=1:10:18" \
--group "round3::dist=1" \
--group "round3::minFindsPerGroup=1" \
--extract "cdna=0:0:end" \
--extract "umi=1:0:10" \
--extract "bc1=@round1" \
--extract "bc2=@round2" \
--extract "bc3=@round3" \
--template "cdna" \
--tag "CB=bc1+bc2+bc3" \
--tag "CB::qual=CY" \
--tag "CB::sep=-" \
--tag "CB::qual-sep=%20" \
--tag "RX=umi" \
--tag "RX::qual=QX" \
--out "splitseq.unmapped.bam"- FASTX/SAM/BAM/CRAM in and out (SAM/BAM/CRAM written unmapped), with per-segment qualities carried through extraction.
- splitcode-style matching: tag groups, variable-length tags, location windows, mismatch/indel tolerance, sequential
next/prevanchoring,@extractspans, and error-correction to canonical barcodes. - Single-pass demultiplexing, optionally nested: split a pool into per-sample outputs and, if you like, each sample into sub-samples (a library of samples, sub-libraries of lysates, and so on), each written as its own FASTX/SAM/BAM/CRAM with
SM/LBread group identifiers. - Configured on the CLI or in sheets with the same grammar: flags repeat and accumulate, so a spec can be built up piece by piece (
--group g1::loc=... --group g1::dist=...), or written compactly with comma lists (--group g1::loc=...,dist=...).
See the contributing guide for more information.
Note
Claude Code was used substantially in the development of unmux.
Claude was used most notably for ideation support, prototyping, and code generation.
Although most of the code in this repository was AI-generated, the author committed to defining nearly all of the API through spec-driven development, reading and reviewing every code comment, line of actual Rust, and all tests (unit, integration, and narrative).
So, until which time where we are ready to release a v1, treat this project as AI-enabled, well-reviewed, and well-tested by a single discriminating human with modest Rust experience and expert experience in bioinformatics and other programming languages like Scala.
