Skip to main content

pymaftools

Documentation PyPI

pymaftools is a Python package for handling and analyzing MAF (Mutation Annotation Format) files and multi-omics cancer genomics data. It provides classes for data manipulation, statistical analysis, machine learning, and visualization.

pymaftools overview

pymaftools provides a unified workflow for multi-omics cancer genomics — from data loading and filtering,
through statistical analysis and machine learning, to publication-ready visualization.

Multi-omics cohort structure

Multiple omics layers (SNV, CNV, expression, etc.) are integrated into a unified Cohort structure.
Each layer shares the same samples but may have different numbers of features.

Features

Core Data Structures

  • MAF — Load, parse, filter, and merge MAF files
  • PivotTable — Gene/feature x sample matrix with synchronized metadata, frequency calculation, statistical testing, and filtering
  • Cohort — Multi-omics container linking multiple PivotTables with shared sample metadata
  • CopyNumberVariationTable — Read GISTIC arm-level and gene-level results
  • ExpressionTable — Gene expression data with clustering support
  • SignatureTable — COSMIC mutational signature data
  • CancerCellFractionTable — Cancer cell fraction (CCF) data from PyClone
  • SmallVariationTable — Specialized PivotTable for SNV/INDEL data
  • SimilarityMatrix — Pairwise similarity analysis (Jaccard, cosine, etc.)

Filtering & Statistical Analysis

  • filter_by_freq — Filter features by mutation frequency
  • filter_by_variance — Filter by variance or median absolute deviation (MAD)
  • filter_by_statistical_test — Filter by statistical test (t-test, Mann-Whitney, Kruskal-Wallis, ANOVA) with FDR correction
  • Chi-squared / Fisher's exact test — Association testing between features and groups
  • TMB calculation — Tumor mutation burden per sample

Visualization

  • OncoPlot — Mutation landscape heatmaps with frequency bars, sample metadata, and legends
  • LollipopPlot — Protein mutation positions with domain annotation
  • PivotStatsPlot — PCA, boxplots with statistical annotations, heatmaps (via pt.plot)
  • ModelPlot — Model performance visualizations
  • MethodsPlot — 3D methodology demonstration plots
  • ColorManager / FontManager — Customizable color and font management

Machine Learning

  • OmicsStackingModel — Multi-omics stacking classifier with feature importance
  • Model utilities — Evaluation, cross-validation, RFECV feature selection, importance heatmaps

Utilities

  • PCA_CCA — Dimensionality reduction utilities
  • Gene set tools — Read GMT files, fetch MSigDB gene sets
  • Gene info — NCBI gene ID lookup; Ensembl BioMart exon/transcript size lookup (get_exon_size, load_gene_sizes) for size-based gene grouping

Requirements

Python 3.10+ with the following dependencies:

  • pandas (>2.0), numpy, matplotlib, seaborn, scipy
  • networkx, scikit-learn, statsmodels, statannotations
  • requests, beautifulsoup4, tqdm, tables (HDF5)

All dependencies are automatically installed.

Installation

uv pip install pymaftools

Using pip

pip install pymaftools

From GitHub (latest development version)

uv pip install git+https://github.com/xu62u4u6/pymaftools.git
# or
pip install git+https://github.com/xu62u4u6/pymaftools.git

Usage

Getting Started

from pymaftools import *

# Load and merge MAF files
maf1 = MAF.read_maf("case1.maf")
maf2 = MAF.read_maf("case2.maf")
merged = MAF.merge_mafs([maf1, maf2])

# Filter to nonsynonymous mutations and convert to a gene-level table
pt = merged.filter_maf(MAF.nonsynonymous_types).to_gene_table()

# Process pivot table
pt = (pt
    .add_freq()
    .sort_features(by="freq")
    .sort_samples_by_mutations()
    .calculate_tmb(default_capture_size=50)
)

# Create an oncoplot from the `table.plot` accessor. Tracks are registered with
# chained add_* calls; a single render() derives the layout and draws the figure
# (required before save()). See "Declarative track API" below for the full set.
op = (pt.head(50).plot.oncoplot(figsize=(15, 10))
    .main()                    # mutation matrix
    .add_freq(side="right")    # per-feature frequency strip
    .add_bar("TMB", side="top")
    .render()
)
op.save("oncoplot.png", dpi=300)

Advanced Filtering

# Filter by variance (keep top 25% most variable features)
filtered = pt.filter_by_variance(quantile=0.75, method="var")

# Filter by statistical test with FDR correction
filtered = pt.filter_by_statistical_test(
    group_col="subtype", method="kruskal", alpha=0.05
)

Mutation Oncoplot with Sample Metadata

# Load and process data
LUAD_maf = MAF.read_csv("data/WES/LUAD_all_case_maf.csv")
LUSC_maf = MAF.read_csv("data/WES/LUSC_all_case_maf.csv")
all_case_maf = MAF.merge_mafs([LUAD_maf, LUSC_maf])

# Filter and convert to table
table = (all_case_maf
    .filter_maf(all_case_maf.nonsynonymous_types)
    .to_gene_table()
)

# Load sample metadata
all_sample_metadata = pd.read_csv("data/all_sample_metadata.csv")
table.sample_metadata[["case_ID", "sample_type"]] = table.columns.to_series().str.rsplit("_", n=1).apply(pd.Series)
table.sample_metadata = pd.merge(
    table.sample_metadata.reset_index(), all_sample_metadata,
    left_on="case_ID", right_on="case_ID"
).set_index(["sample_ID"])

# Add group frequencies
table = table.add_freq(
    groups={"LUAD": table.subset(samples=table.sample_metadata.subtype == "LUAD"),
            "ASC": table.subset(samples=table.sample_metadata.subtype == "ASC"),
            "LUSC": table.subset(samples=table.sample_metadata.subtype == "LUSC")}
)

# Filter and sort
freq = 0.1
table = (table.filter_by_freq(freq)
    .sort_features(by="freq")
    .sort_samples_by_group(group_col="subtype",
                           group_order=["LUAD", "ASC", "LUSC"], top=10)
)

# Create oncoplot. Categorical strips come from add_sample_annotation; pass a
# per-column {category: color} cmap_dict to override colours (see below).
op = (table.plot.oncoplot(figsize=(30, 14))
    .main()                                                       # mutation matrix
    .add_freq(freq_columns=["freq", "LUAD_freq", "ASC_freq", "LUSC_freq"], side="right")
    .add_bar("TMB", side="top")
    .add_sample_annotation(["subtype", "sex", "smoke"], side="bottom")
    .render()  # draws everything + all legends in one pass
)
op.save("mutation_oncoplot.tiff", dpi=300)

image

Numeric CNV Oncoplot

# Continuous main matrix via .main(kind="cnv"); same declarative track API.
op = (CNV_gene_cosmic.plot.oncoplot(figsize=(30, 10))
    .main(kind="cnv", yticklabels=False, cmap="coolwarm", vmin=-2, vmax=2)
    .add_bar("TMB", side="top")
    .add_sample_annotation(["subtype", "sex", "smoke"], side="bottom")
    .render()
)
op.save("cnv_oncoplot.tiff", dpi=600)

image

Declarative track API (canonical)

All the oncoplots above use this API. Oncoplots are composed of tracks: a main matrix (main(), with kind="cnv" for a continuous matrix) plus annotation strips registered by chained add_* calls, drawn in one pass by render(). Beyond what the examples above show, it gives explicit control over each track's side and can draw feature-side annotations (row strips from feature_metadata), which is the one thing the legacy convenience methods (mutation_heatmap / plot_freq / plot_bar / numeric_heatmap, kept for backward compatibility) cannot. It is reachable from the same table.plot accessor as the statistical plots:

op = (table.plot.oncoplot(figsize=(15, 10))
    .main()                                       # mutation matrix
    .add_bar("TMB", side="top")
    .add_freq(side="right")
    .add_sample_annotation(["subtype", "sex"], side="bottom")  # categorical
    .add_sample_annotation(["age"], side="bottom")             # numeric (+ colorbar)
    .add_feature_annotation(["pathway"], side="right")         # row-side strip
    .render(legend_width=3, wspace=0.01)          # tune layout/spacing here
)
op.save("oncoplot.png", dpi=300)

add_sample_annotation / add_feature_annotation infer categorical vs numeric from the column dtype. cmap_dict overrides the colour per column for either dtype — a categorical {category: color} mapping, or (for a numeric column) a cmap name that beats the global cmap; "coolwarm" additionally centres a signed column symmetrically around 0. Use add_feature_annotation (not add_freq) for signed/continuous feature columns — add_freq is for 0–1 frequency proportions on a shared scale:

.add_feature_annotation(["delta_freq"], side="right",
                        cmap_dict={"delta_freq": "coolwarm"}, annotate=True)

For a continuous (CNV) main matrix use .main(kind="cnv").

Grouped oncoplot

Partition rows by a feature_metadata column and/or columns by a sample_metadata column into labelled sections (separator lines across the matrix + aligned tracks, with group titles). Sort first so each group is contiguous:

grouped = (table
    .sort_features(by="pathway")
    .sort_samples_by_group(group_col="subtype", group_order=["LUAD", "ASC", "LUSC"]))

op = (grouped.plot.oncoplot(figsize=(13, 9))
    .main()
    .add_bar("TMB", side="top")
    .add_freq(side="right")
    .group_features(by="pathway")           # row sections + left titles
    .group_samples(by="subtype", freq=True) # column sections + top titles
    .render(show_sample_labels=False)       # None auto-hides labels above 50 samples
)
op.save("grouped_oncoplot.png", dpi=300)

group_samples(by=..., freq=True) draws each sample group its own per-section frequency strip (read from feature_metadata["{label}_freq"], e.g. LUAD_freq) right of that group's heatmap section, alongside the overall add_freq(...) bar. Populate those per-group columns with add_freq(group_col="subtype") (see below). All frequency strips share one comparable 0–1 colour scale. render(show_sample_labels=) controls per-column sample tick labels: None (default) auto-hides them above 50 samples, True/False forces.

Gene annotation and size-based grouping

# Per-group frequencies straight from a sample_metadata column (no manual dict):
table = table.add_freq(group_col="subtype")   # adds LUAD_freq, ASC_freq, ... and freq

# Annotate each gene with its canonical-transcript exon size from Ensembl BioMart,
# then bucket genes by size so large mutation-prone genes (TTN, MUC16) group apart
# from compact drivers. NOTE: add_exon_size / get_exon_size hit Ensembl BioMart and
# require network access.
table = table.add_exon_size()                 # writes feature_metadata["exon_size"]
table.feature_metadata["size_group"] = pd.cut(
    table.feature_metadata["exon_size"],
    bins=[0, 5000, 15000, 1e9], labels=["Small", "Medium", "Large"],
)

# Multi-key sort keeps each size group contiguous, ordered by freq within it:
table = table.sort_features(by=["size_group", "freq"], ascending=[True, False])

op = (table.plot.oncoplot(figsize=(13, 9))
    .main()
    .add_freq(side="right")
    .group_features(by="size_group")
    .render()
)
op.save("size_grouped_oncoplot.png", dpi=300)

Lollipop Plot

maf = MAF.read_csv(YOUR_MAF_PATH)
gene = "EGFR"
AA_length, mutations_data = maf.get_protein_info(gene)
domains_data, refseq_ID = MAF.get_domain_info(gene, AA_length)

plot = LollipopPlot(
    protein_name=gene,
    protein_length=AA_length,
    domains=domains_data,
    mutations=mutations_data
)
plot.plot()

image

Multi-Omics with Cohort

cohort = Cohort("my_cohort")
# add_table(table, table_name) — the PivotTable comes first
cohort.add_table(mutation_pt, "mutations")
cohort.add_table(cnv_table, "cnv")
cohort.add_table(expr_table, "expression")
cohort.add_sample_metadata(clinical_df)

# Save/load with HDF5. Unlike SQLite, this supports high-dimensional tables.
cohort.to_hdf5("cohort.h5")
cohort = Cohort.read_hdf5("cohort.h5")

Machine Learning

from pymaftools import OmicsStackingModel

omics = {
    "mutations": mutation_pt,
    "cnv": cnv_table,
    "expression": expr_table,
}
class_order = sorted(cohort.sample_metadata["subtype"].dropna().unique())
model = OmicsStackingModel(omics, class_order=class_order)
X = model.prepare_features()  # aligns samples and namespaces shared gene names
y = cohort.sample_metadata.loc[X.index, "subtype"]
model.fit(X, y)
preds = model.predict(X)
importance = model.get_omics_feature_importance("mutations")
weights = model.get_omics_weights()

FAQ

1. How to adjust font sizes in OncoPlot?

op = (pivot_table.plot.oncoplot(ytick_fontsize=12)
    .main(ytick_fontsize=10)
    .add_freq(annot_fontsize=10)
    .render()
)

2. How to customize color mappings?

from pymaftools import ColorManager

color_manager = ColorManager()
color_manager.add_cmap("custom_mutations", {
    "Missense_Mutation": "#FF6B6B",
    "Nonsense_Mutation": "#4ECDC4",
    "Frame_Shift_Del": "#45B7D1"
})

mutation_cmap = color_manager.get_cmap("custom_mutations")

op = (pivot_table.plot.oncoplot()
    .main(cmap_dict=mutation_cmap)   # custom mutation colours
    .add_freq()
    .render()
)

3. How to save and load analysis results?

# HDF5 is the canonical persistence format for high-dimensional omics data.
pivot_table.to_h5("results.h5")
loaded = PivotTable.read_h5("results.h5")

cohort.to_hdf5("cohort.h5")
loaded = Cohort.read_hdf5("cohort.h5")

# Save figures
oncoplot.save("oncoplot.png", dpi=300)

SQLite persistence is deprecated as of 0.5.0 because SQLite has a practical limit of roughly 2,000 columns, which is too low for expression and other high-dimensional omics matrices. Existing SQLite files remain readable during the deprecation period.

4. How to read and combine VCF calls?

from pymaftools import VCF

mutect2 = VCF.read_vcf("mutect2.vcf.gz", caller="mutect2")
muse = VCF.read_vcf("muse.vcf.gz", caller="muse")
consensus = VCF.merge_callers([mutect2, muse], min_callers=2)

Multi-allelic records are expanded to one row per alternate allele, with the matching AD and AF values retained for each row.

Development and Testing

Bundled fixture and annotation provenance is recorded in DATA_SOURCES.md, including checksums and known lineage gaps.

# Install with test dependencies
pip install -e .[test]

# Run tests
make test              # All tests
make test-core         # Core functionality
make test-plot         # Plotting tests
make test-fast         # Exclude slow tests
make test-coverage     # With coverage report

Test Categories

  • Core tests (tests/core/): PivotTable, MAF, Cohort
  • Plot tests (tests/plot/): All visualizations
  • Model tests (tests/model/): ML components
  • Integration tests (@pytest.mark.integration): End-to-end workflows

CI

Tests run on GitHub Actions for Python 3.10-3.12 (stable) and 3.13-3.14 (experimental).

License

MIT License - see the LICENSE file for details.

Author

xu62u4u6

Metadata

Release files for pymaftools 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymaftools 0.5.0
File Size Uploaded
pymaftools-0.5.0.tar.gz 2.9 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pymaftools 0.5.0
File Interpreter ABI Platform
pymaftools-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 5.7 MB

Release files / pymaftools-0.5.0.tar.gz

Download URL pymaftools-0.5.0.tar.gz
Size 2.9 MB
Tags Source
SHA-256 checksum
How to use checksums
a95ff7898ab9bd65c0f95e3b8545fba2887a8dae5a2560b59681cda46ca3b325
BLAKE2b-256 checksum
How to use checksums
88f6f5d4141c69cc016d656837cbd5f3f28655d8d1ed723c6c804a78a3c1e75a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.

Transparency log

Release files / pymaftools-0.5.0-py3-none-any.whl

Download URL pymaftools-0.5.0-py3-none-any.whl
Size 2.9 MB
Tags Python 3
SHA-256 checksum
How to use checksums
24bca66eb09d82bad4468792127b45ef414deb23283029ff77463658e76b8d8c
BLAKE2b-256 checksum
How to use checksums
f5ddad47bdea44c6a22bc829652eac5cc29e8b825c5aee0e74ecd686e6ed5ad7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page