Adapt SigMA2 to your own data
Feed SigMA2 from your own pipeline: build its inputs, know what the reader accepts and refuses, and record what a result depends on.
Before you begin, you need the package installed, the hg19 genome, the repeat-locus files for the indel counts.
Know the pipeline
A run is a fixed sequence of steps on one 96-channel matrix. Samples reads the mutations into the matrix and keeps the indels beside it; the decomposition and the likelihood step compute, per tumor, the exposures, the cosine similarities and the likelihoods of the tumor type's reference clusters; the classifier step scores those features with the cell's model and applies its cutoffs. HRD and MMRD/POLE add their own feature steps in front of their own models. How a classifier scores a panel describes the features, and what a call means what each model was trained on.
Everything a run used is on the object it returns:
curl -LO https://raw.githubusercontent.com/parklab/SigMA/master/inst/extdata/examples/test_mutations_50sample.maf
import sigma2 as sg2
out = sg2.run(
input="test_mutations_50sample.maf",
ref_genome="hg19",
tumor_type="breast",
seq_type="msk",
soi="SBS3",
)
out.model, out.catalog_name, out.snv_floor, out.seq_type, out.soi
| Parameter | Description | Default |
|---|---|---|
| inputpath, list of paths, folder or DataFrame | The mutations: a MAF path, a VCF path, a list of VCF paths, a folder of VCFs, a CSV, or a DataFrame of SBS-96 counts. | Required |
| ref_genomestr or path | The build of the coordinates (hg19, hg38, GRCh37, GRCh38) or a FASTA path; unused for a matrix input. | 'hg19' |
| tumor_typestr | A tumor type of the vocabulary, in any case (the package lists 39 names; msi and pole among them are signature sets, not tumor types); it sets the signature set, the reference spectra and the classifier. | Required |
| seq_typestr | The platform (msk, fo, op, seqcap, tcga_mc3, wgs); "auto" chooses by SNV burden and reports the choice. | 'auto' |
| soistr or CompositeSignature | The signature of interest: a COSMIC name, a composite preset (APOBEC, SBS7, SBS10, SBS17, CLOCK), an alias (UV, POLE), or HRD. | Required |
(GBMModel(breast/SBS3/msk), 'cosmic_v3p2_inhouse', 5, 'msk', SBS3)
Build a matrix in your own pipeline
When your mutations do not come as a MAF or VCF, pass the 96-channel matrix as a DataFrame: one row per tumor, one column per channel, named by trinucleotide context in COSMIC spelling (A[C>A]A) or the package's (a[c>a]a), in any order, as counts. A matrix carries no indels, so MMRD/POLE and HRD need the indel counts as columns beside it; sg2.indel_features computes them from a table of indel records with the columns Chromosome, Position, REF, ALT and Sample, in VCF style (the anchor base included) or MAF style (- for the empty allele, a deletion placed at its first deleted base and an insertion at the base before it). On the example file, the matrix route gives the same indel counts and the same MMRD/POLE calls as passing the MAF itself:
import pandas as pd
maf = pd.read_csv(
"test_mutations_50sample.maf",
sep="\t",
comment="#",
dtype=str,
keep_default_na=False,
)
indels = maf.loc[maf["Variant_Type"].isin(["INS", "DEL"])].rename(columns={
"Tumor_Sample_Barcode": "Sample",
"Start_Position": "Position",
"Reference_Allele": "REF",
"Tumor_Seq_Allele2": "ALT",
})[["Chromosome", "Position", "REF", "ALT", "Sample"]]
features = sg2.indel_features(
indels=indels,
ref_genome="hg19",
samples=maf["Tumor_Sample_Barcode"].unique().tolist(),
)
spectra = sg2.Samples("test_mutations_50sample.maf", ref_genome="hg19").df.iloc[:, :96]
mm = sg2.predict_mmrd_ppd(
input=spectra.join(features),
tumor_type="breast",
seq_type="msk",
)
mm.df["MMRD_category"].value_counts()
| Parameter | Description | Default |
|---|---|---|
| indelsDataFrame or VCF | The indels: a DataFrame with Chromosome, Position, REF, ALT and Sample columns (VCF or MAF style), or a VCF object. | Required |
| ref_genomestr or FastaFile | The build of the coordinates or an opened FASTA. | Required |
| sampleslist of str | The samples to report, in this order; a sample without indels gets a row of zeros. | None |
| Parameter | Description | Default |
|---|---|---|
| ref_genomepath or str | The build of the coordinates (hg19, hg38) or a FASTA path. | 'hg19' |
| Parameter | Description | Default |
|---|---|---|
| inputpath, list of paths, folder or DataFrame | The mutations with their indels: a MAF or VCF path, or a matrix with nins, ndel, nmsi_ins and nmsi_del columns. | Required |
| tumor_typestr | A tumor type of the vocabulary (msi and pole are signature sets, not tumor types); the point-mutation features need its signature set. | Required |
| seq_typestr | The platform: a model exists for msk, tcga_mc3, seqcap, fo and op. | Required |
MMRD_category
MMRP 37
MMRD 1
Name: count, dtype: int64
The indel table's Sample names must be the matrix's row names. An indel whose sample matches no row is counted for no tumor, without a message: that tumor is scored as if it had no indels, and its MMRD/POLE and HRD calls change (renaming the samples in the indel table above turns the MMRD tumor into an MMRP call). An empty set means every name matched:
set(indels["Sample"]) - set(spectra.index)
set()
The same table serves HRD: with its mmej and nhej columns, run(soi="HRD") on the matrix gives the same calls as on the MAF. The repeat loci SigMA2 ships are hg19; on another build nmsi_ins and nmsi_del are left empty unless repeat-locus BED files of that build are passed with beds=, and the MMRD/POLE classifier then scores without them, printing nothing at the default verbosity. Split multi-allelic records before counting: indel_features skips an indel whose ALT lists two alleles, without a message.
Check what the reader accepts
The reader refuses what it cannot score and, in most cases, names the reason. Observed on the current release:
| Input | What happens |
|---|---|
| a matrix with the 96 channels as counts | scored |
| a matrix with channels as rows | detected and flipped |
| a matrix with a channel missing | refused, naming the channel |
| a matrix with a missing value | fails: ValueError: Encountered all NA values for a float matrix or a CSV with a blank cell, TypeError: boolean value of NA is ambiguous for a nullable-integer matrix; fill missing counts with 0 |
| a matrix with non-integer values | accepted without a warning, and total_snvs becomes fractional; pass counts |
a MAF file without Tumor_Sample_Barcode | refused: "Invalid MAF: missing column(s)", naming the columns a MAF needs |
a MAF read into a DataFrame | refused with "no 96-channel columns found": a frame is read as a matrix, so pass the MAF as a path |
| a MAF whose positions are 0-based | scored without a message: the reference allele is not compared with the genome. Shifting the example file's positions by one base changed 16 of its 38 calls; check the reference alleles |
a MAF whose NCBI_Build differs from ref_genome | refused: "Genome build mismatch", with both builds named |
a VCF with chr-prefixed and bare chromosome names | both read |
a VCF record whose FILTER is not PASS | an SNV is counted whatever its filter; an indel only when PASS or . |
| a cohort in which every tumor is below the floor | fails with "Invalid input" and the object's address, not a message about the floor |
| segments without a sample column | refused, naming the four required columns |
| MMRD/POLE on a matrix without indel counts | refused, naming nins, nmsi_ins and nmsi_del; with those three and no ndel, it stops with KeyError: 'ndel' |
indel records whose Sample names match no row of the matrix | counted for no tumor, without a message; MMRD/POLE and HRD score those tumors as if they had no indels |
HRD on a matrix without mmej and nhej | refused: "HRD needs indels", with the three ways to give them |
Filter a VCF to PASS before scoring. 1. Prepare your input files lists the columns of each format and the builds each input accepts.
Run it in a pipeline
The Python call returns the table and writes nothing; a batch step writes what it keeps:
out.df.to_csv("sbs3_full.tsv", sep="\t")
out.to_lite().to_csv("sbs3_lite.tsv", sep="\t")
out.df.shape, out.to_lite().shape
((38, 186), (38, 14))
An HRD run is also a command, sigma2 hrd score, for a workflow manager that calls programs (Command line). Pass mxw="max" on a cohort to use every core.
Record what a result depends on
A result depends on the package release, the classifier and its catalog, the floor, the reference genome build and, for indel features, the repeat loci. Keep sg2.__version__, out.model.name, out.catalog_name and out.snv_floor with every table; the model's card (out.model.config) holds its cutoffs and its training record. Pin the release when you install it, with pip install "sigma2==0.0.16a20".
Know what is public
- Public: the functions on the Python API pages (scoring, models and the store, HRD, training, inputs, the SigMA v1 tools) and the columns of 5. Read the output.
- Internal:
sigma2.api,sigma2.steps,sigma2.core.Pipeline,sigma2.tools,sigma2.utilsand the private members of the model classes.sigma2.__all__listsapi,stepsandtools, so they import like the public names; the reference's Advanced branch lists them as the package has them; nothing promises their names or behavior from one release to the next, and a pipeline that imports them can break on an upgrade.
Extend what the package ships
| Need | Path |
|---|---|
| a signature, tumor type or bundled platform without a classifier | train a cell from whole genomes with known exposures: Train a classifier |
| a panel that is not one of the bundled platforms | the same training, with the panel's BED file for the simulation |
| several signatures scored as one | sg2.CompositeSignature, then a trained cell for it |
| an HRD model on your own truth | sg2.tune_hrd on an hrd_features table with biallelic BRCA1/2 labels: 3. Detect HRD |
| a store of your own models | Add.export(), then root_dir=: Manage models and the store |
Reference: errors and their causes
| Message | Cause | Fix |
|---|---|---|
No classifier for tumor_type=…, soi=…, seq_type=… | no cell for that combination | the message lists the platforms that have one; sg2.describe() lists every cell |
… is EXPERIMENTAL: withheld by the release gate | the cell failed its release gate | force=True returns scores without calls (release status) |
Genome build mismatch | the MAF's NCBI_Build and ref_genome differ | pass the build the coordinates are in |
no 96-channel columns found | a matrix without channel columns, or a MAF read into a DataFrame | name the channels, or pass the MAF as a path |
Invalid MAF: missing column(s) […] | a MAF file without a required column | add it: a MAF needs Tumor_Sample_Barcode, Chromosome, Start_Position, Reference_Allele, and Tumor_Seq_Allele2 or Tumor_Seq_Allele1 |
1 of the 96 channels missing | an incomplete matrix | add the channel as a column of zeros |
MMRD/POLE needs indel counts | a matrix without indel columns | sg2.indel_features from the indel records, joined to the matrix |
KeyError: 'ndel' | an MMRD/POLE matrix with nins, nmsi_ins and nmsi_del but no ndel | the indel_features table carries all six columns |
HRD needs indels | a matrix without mmej and nhej columns | the same indel_features table, joined to the matrix, or indels= |
segments need sample, chromosome, start and end | a single-sample segment file | add a sample column |
catalog … is not the one the classifier was trained on | catalog= differs from the cell's | leave catalog out |
Invalid input: <sigma2.samples.Samples object …> | every tumor is below the floor | check the SNV counts; nothing above the floor is left to score |
Panel simulation requires pybedtools | training without the panel extra | pip install 'sigma2[panel]' with bedtools on PATH |