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.
Comparing label 0 with label 1 in data/miRNA_mRNA_pairs_dataset.tsv
9 checks:9 PassHow a flag is decided →
gb-qc v1.0.0 · run 2026-08-26 23:58:43

Basic Descriptive Statistics

Filename miRNA_mRNA_pairs_dataset.tsv miRNA_mRNA_pairs_dataset.tsv
Label 0 1
Sequence column gene_noncodingRNA gene_noncodingRNA
Number of sequences 600 600
Unique sequences 600 600
Minimum length 67 67
Mean length 72.07 72.08
Maximum length 75 76
Number of bases 43243 43246
%GC content 50.74 50.05

The set of characters each label uses. It fails if the two sets differ at all, and there is no Warning - the question is yes or no.

What matters is the asymmetry, not the character: N in both labels passes, but a character only one label has makes every sequence carrying it perfectly classifiable. What to do about it →

Label 0 1
Unique bases A, C, G, T A, C, G, T

How much of the data survives deduplication, pooled over both labels. Pass only at 100%, Warning above 98%, Fail below it - so any duplication at all is at least a Warning.

Duplicates make the dataset smaller than its row count, and copies landing on both sides of a random split inflate the score you report. What to do about it →

No duplicate sequences were found in either class.

Sequences that appear under both labels: identical input, opposite label, so no model can get both right. One shared sequence fails; there is no Warning.

It puts a hard ceiling on achievable accuracy, and the offending sequences are listed in gb-qc-duplicates.txt beside this report. What to do about it →

No duplicate sequences were found between classes.

The length distribution of each label. The flag is the AU-ROC of length on its own: how well a model that does nothing but count characters separates the two labels.

It fires when the labels were sampled or trimmed differently, which is the easiest bias to introduce by accident. What to do about it →

Sequence Lengths Plot

The GC% of every sequence, one distribution per label. The flag is the AU-ROC of GC content on its own.

The classic compositional confound: it fires whenever the labels come from different genomic contexts - promoters against background, coding against intergenic. GC-matching the negatives removes most of it. What to do about it →

Per Sequence GC Content

One panel per base: how often it occurs in a sequence, compared between the labels. Each base is scored separately and the flag is the worst of them, so one red panel flags the check.

It usually fires alongside GC content, and then the two are one finding. Firing without it means the imbalance is A against T or C against G, which points at strand asymmetry. What to do about it →

Per Sequence Nucleotide Content

The same comparison for two-base combinations, each row holding the pairs that start with one base. All sixteen are scored separately and the flag is the worst of them.

Which pair it is says something specific: CG alone points at methylation or promoter context, while all sixteen at once is the GC finding again. What to do about it →

Per Sequence Dinucleotide Content

One panel per base, one line per label, running 5' to 3' along the sequence. The shaded bands are the flagged positions - orange Warning, red Fail, grey not scored. Every position is scored separately and the flag is the worst single one. The lower panel is how much of each label still reaches each position.

A flag means something at a fixed location gives the label away: an adapter or barcode left on one label, a padding convention applied to one only, or a real motif - which is the signal, not a leak. Where the flags sit tells you which: a cluster is a motif, position 1 alone is an artefact. What to do about it →Reading the figure →

Positions 1–72 were compared, and those are the positions the figure draws: everything in it was scored, so a stretch with no flag on it is a stretch that passed. Each position is compared on the sequences that reach it, and only where enough of them do: the larger of 250 sequences — below which a difference this size turns up on sampling noise alone — and 25% of the class, below which a cohort can be large and still describe only the longest sequences. Position 72 is the last that clears both: 0: 84.3% (506 sequences) and 1: 86.8% (521 sequences).

The sequences run further, to position 73, where 0: 28.8% (173 sequences) and 1: 27.0% (162 sequences) remain. Those later positions are not drawn and are reported as Unknown rather than compared: they are reached by too few of each class for a difference there to be a difference between the classes rather than between their longest sequences. So the figure ends before the sequences do, and says nothing either way about what happens past its right-hand edge. The panel at the bottom shows how the number of sequences behind each class falls along the window: the window ends where the lower of the two curves falls below the cohort a position has to have behind it.

This figure is drawn in the browser and needs JavaScript. The flagged positions are listed below, and the same plot is in this report's plots/ directory as a PNG.

Drag to zoom · shift-drag to pan · scroll to zoom · double-click to reset · hover for the frequencies and flags at a position

Flagged positions

The same check counted from the other end: position 1 is the last base of a sequence, position 2 the one before it. Read it exactly like the forward figure above.

On fixed-length sequences it is redundant and will report the same numbers. It earns its place on variable-length data, where something anchored to the sequence end - a poly-A tail, a 3' adapter - sits at a different forward position in every sequence. More on this check →

Positions 1–72 were compared, and those are the positions the figure draws: everything in it was scored, so a stretch with no flag on it is a stretch that passed. Each position is compared on the sequences that reach it, and only where enough of them do: the larger of 250 sequences — below which a difference this size turns up on sampling noise alone — and 25% of the class, below which a cohort can be large and still describe only the longest sequences. Position 72 is the last that clears both: 0: 84.3% (506 sequences) and 1: 86.8% (521 sequences).

The sequences run further, to position 73, where 0: 28.8% (173 sequences) and 1: 27.0% (162 sequences) remain. Those later positions are not drawn and are reported as Unknown rather than compared: they are reached by too few of each class for a difference there to be a difference between the classes rather than between their longest sequences. So the figure ends before the sequences do, and says nothing either way about what happens past its right-hand edge. The panel at the bottom shows how the number of sequences behind each class falls along the window: the window ends where the lower of the two curves falls below the cohort a position has to have behind it.

This figure is drawn in the browser and needs JavaScript. The flagged positions are listed below, and the same plot is in this report's plots/ directory as a PNG.

Drag to zoom · shift-drag to pan · scroll to zoom · double-click to reset · hover for the frequencies and flags at a position

Flagged positions