Build mutational-spectrum catalogs from VCF files.
mSigSpectra is the lean, variant-caller-agnostic successor to
ICAMS — it keeps the numerical
core (VCF reading, annotation, indel justification, and SBS / DBS / ID
catalog construction) and drops everything else. Plotting is now handled
by the separate mSigPlot
package. Shiny, PDF / zip reporting, and per-caller VAF extraction are
intentionally out of scope.
read_vcf() → split_vcf() → annotate_vcf() → vcf_to_catalog()
read_vcf() — reads any VCF file body. Variant-caller-agnostic: it does
not parse FORMAT / sample columns or extract VAF.split_vcf() — partitions a VCF into SBS / DBS / ID sub-tables by
REF/ALT length alone.annotate_vcf() — adds flanking sequence context
(BSgenome::getSeq), transcript strand
(GenomicRanges::findOverlaps against shipped GENCODE tables), and —
for indels — left-justifies and categorizes them
(COSMIC-83 / Koh-89 / Koh-476).vcf_to_catalog() — produces a single catalog of any of these types:
SBS96, SBS192, SBS1536, DBS78, DBS136, DBS144, ID83,
ID89, ID166, ID476.The output is a plain numeric matrix with attributes (type,
counts_or_density, ref_genome, region, abundance). No S3
classes. Functions key off attr(x, "type") via explicit checks.
# install.packages("remotes")
remotes::install_github("steverozen/mSigSpectra")
You also need the BSgenome package(s) for your reference genome(s):
BiocManager::install(c(
"BSgenome.Hsapiens.1000genomes.hs37d5", # GRCh37 / hg19
"BSgenome.Hsapiens.UCSC.hg38", # GRCh38 / hg38
"BSgenome.Mmusculus.UCSC.mm10" # GRCm38 / mm10
))
For plotting, install mSigPlot:
remotes::install_github("steverozen/mSigPlot")
library(mSigSpectra)
library(mSigPlot)
vcf <- read_vcf("my_sample.vcf", filter = "PASS")
parts <- split_vcf(vcf)
ann_sbs <- annotate_vcf(parts$SBS, ref_genome = "GRCh38",
variant_type = "SBS")
sbs96 <- vcf_to_catalog(ann_sbs, type = "SBS96",
ref_genome = "GRCh38", region = "genome",
sample_name = "my_sample")
plot_SBS96(sbs96)
See vignettes/mSigSpectra.Rmd for a full
walk-through that reads a real VCF, generates SBS / DBS / ID catalogs,
writes them to disk, and plots each one with mSigPlot.
| ICAMS | mSigSpectra |
|---|---|
ReadVCFs() / SimpleReadVCF()
|
read_vcf(), read_vcfs()
|
SplitListOfVCFs() |
split_vcf() |
AnnotateSBSVCF() / AnnotateDBSVCF() / AnnotateIDVCF()
|
annotate_vcf(variant_type = ...) |
VCFsToCatalogs() |
vcfs_to_catalogs() |
as.catalog() |
as_catalog() |
TransformCatalog() |
transform_catalog() |
ReadCatalog() / WriteCatalog()
|
read_catalog() / write_catalog()
|
PlotCatalog() |
(now in mSigPlot::plot_SBS96() etc.) |
UpperCamelCase is gone. S3 catalog classes (SBS96Catalog,
IndelCatalog, …) are gone — catalogs are plain matrices with
attributes. The variant.caller argument and the per-caller VAF
extractors (GetStrelkaVAF, GetMutectVAF, etc.) are gone.
mSigSpectra deliberately stays out of several judgment calls that ICAMS made for you. The reader returns the rows the VCF actually contains. You are responsible for sanitizing them before downstream analysis if the caller / VCF source has any of the following quirks:
Some callers (notably Strelka) emit a real DBS as two adjacent SBSs at
positions p and p+1. ICAMS's Strelka-specific code path tried to
detect these by VAF similarity and merged them into a single DBS row.
mSigSpectra never does this — the result is one fewer DBS and two
extra SBSs per pair. If your VCFs come from such a caller and you need
DBSs, merge adjacent SBSs yourself (using your own VAF column) before
calling split_vcf().
ALT = "G,T" styleTri-allelic and tetra-allelic sites can be encoded as a single VCF row
with multiple comma-separated alleles in ALT (A → G,T), or as
multiple rows at the same (CHROM, POS), or with the bcftools norm -m - "split" representation. mSigSpectra:
ALT contains ,) — read_vcf()
passes them through. split_vcf() will not classify them as SBS / DBS
/ ID since nchar(ALT) is 3+. They land silently in
split_vcf()$discarded. If you want to include them, split each
alternate allele into its own row first (e.g. with bcftools norm -m -any).read_vcf(). The
optional check_and_remove_discarded_variants() will discard them
with reason "Variant with same CHROM, POS, REF but different ALT" —
call it explicitly if you want this behavior.A complex change like REF = "AG", ALT = "TC" (two-base substitution
where neither base matches) can equally well be represented as two
adjacent SBSs (A → T and G → C), or as a single DBS, or as one
deletion plus one insertion. There is no community standard.
mSigSpectra:
REF[1] != ALT[1] and lengths differ — discarded by
check_and_remove_discarded_variants() as "Complex indel"; left in
place by the bare read_vcf() + split_vcf() path (where
nchar(REF) != nchar(ALT) flags it as ID even though it does not
conform to the ICAMS / COSMIC indel convention of a shared first base).ACT → TGA — discarded by
check_and_remove_discarded_variants() as "Variant involves three or
more nucleotides"; classified into split_vcf()$discarded by the
split_vcf() path. If you have a curated convention for these, apply
it before catalog construction.Some callers emit REF = "TA", ALT = "TT" (the T at position 1 is
unchanged). These are degenerate DBSs — really single-base
substitutions in disguise. check_and_remove_discarded_variants()
discards them as "Wrong DBS variant"; split_vcf() will classify them
as DBS unless you filter first.
Variants with non-{A,C,G,T} REF bases (e.g. N) cannot be
canonicalized. check_and_remove_discarded_variants() removes them;
vcf_to_catalog() ultimately drops them when the pentanucleotide
context contains N and warns.
The default read_vcf(filter = TRUE) keeps rows where FILTER is in
{"PASS", ".", ""} — the union of common caller defaults. This is
not bit-exact compatible with any single caller — Strelka and Mutect
keep only "PASS", Freebayes uses ".". If you need parity with one
specific caller, pass filter = "PASS" (or your caller's exact
status string) explicitly.
vcf <- read_vcf(file, filter = "PASS")
clean <- check_and_remove_discarded_variants(vcf, name_of_vcf = file)
parts <- split_vcf(clean$df, name_of_vcf = file)
# Inspect clean$discarded.variants and parts$discarded for anything you
# want to recover or re-encode before the annotate / catalog step.
R CMD check: 0 errors, 0 warnings.transform_catalog() (counts ↔ density) and collapse_catalog()
(SBS1536 → SBS96, SBS192 → SBS96, DBS144 → DBS78) implemented.read_catalog() / write_catalog() round-trip ICAMS-native CSV
losslessly; SigProfiler input formats parse for SBS96, SBS1536, ID83.GPL-3.
If you use mSigSpectra in published work, please cite the original ICAMS papers: