Skip to content

fix(stats): paired-end unmapped reads split between too short and other - #247

Open
BenjaminDEMAILLE wants to merge 1 commit into
mainfrom
fix/unmapped-other-bucket
Open

fix(stats): paired-end unmapped reads split between too short and other#247
BenjaminDEMAILLE wants to merge 1 commit into
mainfrom
fix/unmapped-other-bucket

Conversation

@BenjaminDEMAILLE

Copy link
Copy Markdown
Contributor

Closes #48, reproduced on the reporter's own fixture.

The rule

STAR decides the bucket in ReadAlign_mappedFilter.cpp:

if ( nW==0 ) {//no good windows
    statsRA.unmappedOther++;
    unmapType=0;
} else if ( (trBest->maxScore < P.outFilterScoreMin) || ... ) {//too short
    statsRA.unmappedShort++;
    unmapType=1;

So other means no window was found at all, and too short means a window existed and its best transcript failed the score or length thresholds.

The single-end path here already made that distinction. The paired-end path returned TooShort for every unmapped pair, which is why other was permanently zero.

Measured on the reported fixture

nf-core/rnaseq test data: 50 000 pairs, S. cerevisiae chrI + GFP transgene, --outFilterMultimapNmax 20, otherwise defaults.

too short other
before 7374 0
after 3778 3596
STAR 2.7.11b 3766 3609

The totals already agreed (7374 against STAR's 7375), which is what made this a labelling bug rather than a counting one. The remaining 12-13 reads at the boundary are a separate question: they are reads where the two implementations disagree about whether a window exists at all, not about how to label one.

Tests

tests/unmapped_buckets.rs plants all three populations: pairs that map, pairs that seed but cannot clear the score threshold (a window exists, so "too short"), and pairs with no genomic seed at all ("other"). It also asserts the buckets sum to the input count.

Checked against a control: with the fix reverted, the paired-end test fails and the single-end one still passes, which is exactly the shape the bug had.

cargo test, cargo clippy --all-targets -- -D warnings on a cold cache, and cargo fmt --check are green.

Note on the reproduction

--readFilesCommand zcat produces zero reads on macOS for both STAR and rustar-aligner, silently: the run succeeds and Log.final.out reports 0 input reads. I hit it before getting to the real comparison. Unrelated to this fix, but worth knowing when reproducing anything from a gzipped fixture on a Mac; #239 makes --readFilesCommand gunzip -c work by running the command through a shell, which sidesteps it.

🤖 Generated with Claude Code

STAR decides the bucket in ReadAlign_mappedFilter.cpp: nW == 0, meaning no
good window at all, is unmappedOther; only a read that had a window whose best
transcript failed the score or length thresholds is unmappedShort. The
single-end path here already made that distinction. The paired-end path
returned TooShort for every unmapped pair, so the "other" bucket in
Log.final.out was permanently zero and "too short" carried both populations.

Measured on the nf-core/rnaseq test data the issue reports (50 000 pairs,
S. cerevisiae chrI + GFP transgene, --outFilterMultimapNmax 20):

  before   too short 7374   other 0
  after    too short 3778   other 3596
  STAR     too short 3766   other 3609

The totals were already right (7374 against STAR's 7375), which is why this
showed up as mis-categorisation rather than as a counting bug.

tests/unmapped_buckets.rs plants all three populations: pairs that map, pairs
that seed but cannot clear the score threshold, and pairs with no genomic seed
at all. Checked against the control: without this change the paired-end test
fails and the single-end one still passes, which is the shape the bug had.

Closes #48.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log.final.out folds all unmapped reads into too short; other bucket always 0

1 participant