Skip to main content
genomic-benchmarks-qc logo

CI PyPI Python License Docs

Find the shortcut. Learn the biology.

Automated quality control for genomic machine learning datasets: scores the biases, duplicates and data leakage a classifier could exploit before you train on it.

Everything you need is below. The documentation site adds what a README has no room for: eight worked examples with live reports — a clean dataset, a badly biased one, one whose only flaw is six positions wide — a guide to what to do about each check and why the thresholds sit where they do, and the generated CLI and API reference.

What it catches

When the classes differ in something trivial, a high score no longer tells you what a model learned — the biology, or the shortcut. gb-qc looks for the differences a classifier could exploit without understanding anything:

  • Your negatives are shorter than your positives. A length classifier now beats your model.
  • Your classes differ in GC content, base composition or dinucleotide frequencies. In the enhancers example, GC content alone separates the two classes at AU-ROC 0.66.
  • Position 1 is N in one class only, or every sequence in one class starts with the same adapter — a per-position give-away that no summary statistic would show.
  • The same sequence appears in both classes, or your test set repeats your training set. evaluate-splits catches near-duplicates too, with an MMseqs2 similarity search.

Each check gets a Pass / Warning / Fail flag, an HTML report you can read, and a CSV you can put in CI.

The gb-qc report for the composition-bias example, scrolled from top to bottom: the sidebar's flag summary stays in view with two green ticks, an orange warning and six red failures, while the page moves through the descriptive statistics, the duplicate-sequence tables, the GC-content, nucleotide and dinucleotide plots and the two interactive per-position panels, each headed by the flag it was given

The report for the composition-bias example, scrolled end to end — click it to open the real one. It is the worst case in the gallery, and the one report that carries all three flags at once: six checks Fail — the classes share sequences outright, and GC content, nucleotide and dinucleotide composition and both per-position checks all sit above the 0.7 line, the worst at AU-ROC 0.717 — one Warning, two Pass.

Installation

pip install genomic-benchmarks-qc

Requires Python 3.12 or newer.

Extra step for evaluate-splits: mmseqs2

The leakage check uses mmseqs2, which has to be installed separately — preferably from a precompiled binary or built from source. See the mmseqs2 installation guide.

Conda/bioconda builds are worth avoiding here: above one thread they intermittently report hits on the reverse strand, which evaluate-splits never asks for, and those pairs lose their rendered alignment. The leakage numbers are unaffected, and --threads 1 avoids it — details.

evaluate-classes does not need it.

Quick Start

Point the tool at your dataset and give it somewhere to write. To run the commands below exactly as they stand — and compare what you get against the reports they link to — fetch the two enhancers example files first:

curl -fLO https://raw.githubusercontent.com/genomic-benchmarks/genomic-benchmarks-qc/main/examples/enhancers/data/enhancers_train.csv
curl -fLO https://raw.githubusercontent.com/genomic-benchmarks/genomic-benchmarks-qc/main/examples/enhancers/data/enhancers_test.csv
gb-qc evaluate-classes \
  --input enhancers_train.csv \
  --input enhancers_test.csv \
  --out-folder qc-out

It leaves you a report to open — this one:

qc-out/class/sequence/0_vs_1/
├── gb-qc-report.html   ← open this
├── gb-qc-report.csv
└── plots/

To check whether your test set leaks into your training set:

gb-qc evaluate-splits \
  --train-input enhancers_train.csv \
  --test-input enhancers_test.csv \
  --sequence-column sequence \
  --out-folder qc-out

with a report of its own — this one:

qc-out/split/sequence/enhancers_train_vs_enhancers_test/
├── gb-qc-report.html   ← open this
├── gb-qc-report.csv
└── plots/

Both commands can share one --out-folder; they write into class/ and split/ respectively and never overwrite each other.

Reading the report

The HTML report is a single standalone file — no external assets, so you can mail it or archive it. It opens with a summary table of every check and its flag, followed by the basic descriptive statistics of each class, then one section per check with the numbers and the plot behind it.

What it checks:

  • Nucleotide vocabulary
  • Sequence lengths distribution
  • GC content per sequence
  • Nucleotide composition (per sequence & per position)
  • Dinucleotide frequencies
  • Sequence duplication levels
  • Exact duplicate sequences between classes

Flags come from the AU-ROC of a classifier that sees only that one feature:

Flag AU-ROC Meaning
Pass ≤ 0.6 Classes not distinguishable by this feature
Warning ≤ 0.7 A model could get some traction here
Fail > 0.7 Significant bias detected
Unknown Not enough sequences to score the check

Unknown is not Pass: it says the comparison was not made, not that it came out clean. A check needs at least 250 sequences before it is scored at all. The plots, per-class statistics and descriptive tables are computed from all the data either way, so small datasets can still be compared by eye, and the terminal says which checks were skipped and why.

The per-position plot is interactive

Full walkthrough: the per-position plot.

For long sequences a static plot cannot show much: a single flagged position is lost in a figure spanning hundreds of them. The per-position panels are therefore interactive, so you can zoom in and explore the individual failing positions.

The per-position panel being driven: a drag across the flagged region takes 398 positions down to 48, the tooltip at position 200 reads G at 0.217 in one class against 0.784 in the other with a Fail flag beside it, and one press of Next flag returns to the same window without the drag
  • Drag to zoom, shift-drag to pan, double-click to reset.
  • Hover for the per-class base frequencies and flags at a position.
  • Prev / Next flag jumps to the flagged positions.
  • Save view writes the current window as a PNG.
  • The table underneath lists every flagged position and jumps the plot to it.

With variable-length sequences, the tail positions that too few sequences reach are not scored and are left out of the figures.

The leakage report

Full treatment, including how similarity is computed: train/test leakage.

evaluate-splits searches every test sequence against the training set with MMseqs2 and flags one when it exceeds --similarity-threshold (90% by default). The report gives the percentage of leaked queries and targets, a histogram of the similarity distribution, and a panel listing the leaked pairs — up to the first 100, each expanding into its rendered alignment, so you can see what is actually shared. Every flagged pair, listed or not, is exported to mmseqs/mmseqs2_search_result.tsv beside the report. The enhancers example carries a little real leakage: 0.67% of queries and 0.33% of targets — see the report.

Input Formats

Format Description
.fa / .fasta One or more FASTA files. Multiple files = multiple classes.
.csv / .tsv One or more CSV/TSV files. Multiple will be pooled and evaluated as one.

All of the input formats are supported in .gz version as well.

The commands in this section name files from the repository's examples/ directory to show the option shapes; substitute your own paths.

CSV/TSV needs a sequence column and a label column. By default they are called sequence and label:

sequence,label
GAGTGTATGTGTCGAGGAATGTATCCATTTCTTCTAGATTTTCTAGTTT,1
GGTCACCACCACCAAGTTCATGCCTGAACCCTTCAGTGGTCCTTTGCCC,0

Override the names with --sequence-column and --label-column. Any other columns are ignored.

FASTA files carry no labels, so each file is one class and its filename stem (the name without extensions) becomes the label — coding_seqs.fasta becomes the label coding_seqs:

gb-qc evaluate-classes \
  --input examples/fasta-classes/data/coding_seqs.fasta \
  --input examples/fasta-classes/data/intergenomic_seqs.fasta \
  --out-folder qc-out

Multiple sequence columns, for datasets where one row pairs two sequences: evaluate-classes analyzes each column separately, then concatenates all sequences for a combined merged analysis. evaluate-splits only analyzes concatenated sequences.

gb-qc evaluate-classes \
  --input examples/paired-sequences/data/miRNA_mRNA_pairs_dataset.tsv \
  --sequence-column gene \
  --sequence-column noncodingRNA \
  --label-column label \
  --out-folder qc-out

Continuous labels: --regression splits the label column at its median into high and low classes. Rows whose value is not numeric are dropped with a warning, and if the split leaves only one class — a constant column, or one that is mostly zeros — the run exits with an error.

Repeat the option name for each value. --input a.csv --input b.csv, not --input a.csv b.csv. The same goes for --sequence-column and --label-list.

Output Files

Each command writes into its own sub-directory of --out-folderclass/ for evaluate-classes and split/ for evaluate-splits — then into one directory per sequence column, then into one directory per comparison:

<out-folder>/
├── class/
│   └── sequence/
│       └── negatives_vs_positives/
│           ├── gb-qc-report.csv
│           └── gb-qc-report.html
└── split/
    └── sequence/
        └── train_vs_test/
            ├── gb-qc-report.csv
            └── gb-qc-report.html

If you are checking several datasets, give each one its own --out-folder to keep the results side by side.

How directory names are chosen

Directory names come from your class labels, sequence-column names, and input file names, lowercased and stripped of characters that are unsafe on some filesystems. If two of them would collide, the tool makes them unique and warns you which name it used. Labels shown inside the reports and plots are always the originals.

Classes are always ordered alphabetically by directory name, so the same dataset gives you the same report paths no matter which order you listed the input files or --label-list values in.

evaluate-classes — output layout
<out-folder>/class/
└── <column>/                   # one directory per sequence column, named after
    │                           # it. FASTA inputs have no columns and use
    │                           # `sequence`; if you give several columns, an
    │                           # extra `merged` report joins them all together
    ├── <classA>_vs_<classB>/
    │   ├── gb-qc-report.csv
    │   ├── gb-qc-report.html
    │   ├── gb-qc-duplicates.txt
    │   └── plots/
    └── per-class/
        └── <class>.json
File Description
gb-qc-report.csv Simple comparison report with Pass/Warning/Fail flags.
gb-qc-report.html Standalone HTML report: one file, no external assets.
plots/ Individual plot images (PNG).
gb-qc-duplicates.txt Sequences appearing in both compared classes; written only when there are any.
per-class/<class>.json Per-class statistics (count, GC%, length, base/dinucleotide frequencies); written only when json is in --report-types.
evaluate-splits — output layout
<out-folder>/split/
└── <column>/                   # the sequence column that was searched. FASTA
    └── <train>_vs_<test>/      # inputs use `sequence`, and several columns
        ├── gb-qc-report.csv    # searched together use `merged`
        ├── gb-qc-report.html
        ├── plots/
        └── mmseqs/
File Description
gb-qc-report.csv Simple leakage summary.
gb-qc-report.html Interactive HTML report with alignments.
mmseqs/ Raw MMseqs2 results and filtered FASTA files.
plots/ Similarity distribution plots.

Temporary MMseqs2 files go into a gb-qc-mmseqs-*/ directory inside the comparison directory and are removed at the end unless you pass --keep-tmp-files. Each run gets its own directory, so several runs can share one --out-folder at the same time. With --keep-tmp-files nothing is cleaned up and the path of each directory is written to the log.

CLI Reference

Run gb-qc evaluate-classes --help or gb-qc evaluate-splits --help for the same thing in your terminal.

evaluate-classes options
Option Default Description
--input required Input file(s)
--sequence-column sequence Column name(s) with sequences
--label-column label Column with class labels
--label-list infer Specific labels or infer
--regression False Treat label as continuous, split high/low
--out-folder . Output directory; reports go into <out-folder>/class/
--report-types html simple json, html, simple
--plot-type boxen boxen or violin
--end-position auto Last position the per-position checks reach. Defaults to the last position at least 50 of each class's sequences reach
--min-coverage 0.25 Fraction of each class that must reach a position before it can be flagged, on top of the 250 sequences every compared position needs
--log-level INFO DEBUG, INFO, WARNING, ERROR
--log-file none Path to also write logs to; logs go to the console only when unset

Tuning the per-position window. The two options control different things: --min-coverage how far a position can be flagged, --end-position how far the checks run at all. A position is flagged only where at least max(250, --min-coverage x class size) sequences in each class reach it; positions past that are reported as Unknown and are not drawn. --min-coverage 0 leaves only the 250, which cannot be switched off. Pass --end-position to trim a long fixed-length window (where the default trims nothing, because every sequence reaches every position), to keep the report smaller, or to pin the same window across runs so two reports line up position by position. It can only narrow what gets flagged, never widen it.

evaluate-splits options
Option Default Description
--train-input required Training file(s)
--test-input required Test file(s)
--sequence-column sequence Column with sequences
--out-folder . Output directory; reports go into <out-folder>/split/
--report-types html simple html, simple
--similarity-threshold 90.0 % similarity for leakage flag
--threads auto MMseqs2 thread count
--split-memory-limit unlimited Upper RAM limit for MMseqs2 prefilter structures (e.g. 10G, 1T)
--keep-tmp-files False Keep temp files for debugging
--log-level INFO DEBUG, INFO, WARNING, ERROR
--log-file none Path to also write logs to; logs go to the console only when unset

Contributions & Support

Contributions and suggestions for new features are welcome, as are bug reports! Please create a new issue for any of these, including example reports where possible. Pull-requests for fixes and additions are very welcome. See the contributing notes for more information about how the process works.

Citation

If you use genomic-benchmarks-qc in your research, please cite this repository: https://github.com/genomic-benchmarks/genomic-benchmarks-qc

License

MIT-style. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

genomic_benchmarks_qc-1.0.0.tar.gz (228.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

genomic_benchmarks_qc-1.0.0-py3-none-any.whl (155.8 kB view details)

Uploaded Python 3

File details

Details for the file genomic_benchmarks_qc-1.0.0.tar.gz.

File metadata

  • Download URL: genomic_benchmarks_qc-1.0.0.tar.gz
  • Upload date:
  • Size: 228.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for genomic_benchmarks_qc-1.0.0.tar.gz
Algorithm Hash digest
SHA256 278213371dc0f2889d8945409101b568a46c55abffcde6e5b713eded2247ed0f
MD5 d4dc4469bfd86b423b3d9cd5c45d2572
BLAKE2b-256 656cbc88713f97b4b3ff4b5fcb0aae3b77c452564a1423b9b35654fe5079ef08

See more details on using hashes here.

Provenance

The following attestation bundles were made for genomic_benchmarks_qc-1.0.0.tar.gz:

Publisher: ci.yml on genomic-benchmarks/genomic-benchmarks-qc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genomic_benchmarks_qc-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for genomic_benchmarks_qc-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4e3e410adc2e7adcf287dcfd17ab4b35dd874d5f97b1eb863187b94955b94f6f
MD5 2e28c820afdaf03bf8960376a3762672
BLAKE2b-256 6b6c238795d90405ab381eb28634f1d95412f7f15da67ee0784e3bb289b9a4bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for genomic_benchmarks_qc-1.0.0-py3-none-any.whl:

Publisher: ci.yml on genomic-benchmarks/genomic-benchmarks-qc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 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