Repository navigation
fix(flagstat): split counts by QC pass/fail and follow samtools' flagstat_loop - #175
Draft
NicoDeVeaux wants to merge 2 commits into
Draft
NicoDeVeaux wants to merge 2 commits into
NicoDeVeaux wants to merge 2 commits into
Conversation
…plicate counts Failing tests for three divergences from samtools flagstat 1.22.1: - QC-failed reads (0x200) are counted in the QC-passed column; the second column is always 0. - "properly paired" counts a PAIRED|PROPER_PAIR record that is itself unmapped (samtools requires !UNMAP, bam_stat.c:61). - A QC-failed duplicate (0x600) is in neither "duplicates" column, while "primary duplicates" counts it. The unit tests check every 12-bit flag against a transcription of samtools' flagstat_loop; the integration tests compare `rustqc rna` output with golden output from samtools 1.22.1. The SECONDARY|SUPPLEMENTARY line (#125) is kept at its current behaviour and pinned as such. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stat_loop flagstat now keeps its own `[QC-passed, QC-failed]` counters (`FlagstatCounts`), counted like samtools' `flagstat_loop` (bam_stat.c): - QC-failed reads (0x200) go in the second column, with their own percentages, instead of being folded into the first. - "properly paired" requires the read itself to be mapped. - "duplicates" counts every 0x400 record, not the RSeQC bam_stat cascade counter that drops QC-failed duplicates. The SECONDARY|SUPPLEMENTARY case (#125) is unchanged: such a record still counts on both lines. The counters that only fed flagstat are removed from BamStatAccum/BamStatResult; those samtools stats reads are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(flagstat): split counts by QC pass/fail and follow samtools' flagstat_loop
What
samtools/<sample>.flagstatnow matchessamtools flagstat1.22.1 (and 1.24) on every 12-bit flag, except the SECONDARY|SUPPLEMENTARY line, which is deliberately unchanged (#125, below). Three divergences are fixed:flagstat.rsprinted<n> + 0andN/Afor every line, folding QC-failed reads into the QC-passed column and changing its percentages.bam_stat.c:61:(flag & BAM_FPROPER_PAIR) && !(flag & BAM_FUNMAP)). A PAIRED|PROPER_PAIR record that is unmapped (e.g. 0x4F) was counted.The flagstat counters move to a
FlagstatCountsstruct of[QC-passed, QC-failed]pairs, counted like samtools'flagstat_loopand merged across parallel workers. The six counters that only fed flagstat (mapped,read1_flagstat,read2_flagstat,both_mapped,singletons,mate_diff_chr) are removed fromBamStatAccum/BamStatResult; the onessamtools statsoutput reads are untouched, sostatsand RSeQCbam_statoutput do not change.Why
Each case is two plain mapped reads (chr1:1001, chr1:1201) plus one record at chr1:1001:
main2 + 1, mapped(100.00% : 100.00%)3 + 0,(100.00% : N/A)=)0 + 0 (0.00% : N/A)1 + 0 (100.00% : N/A)0 + 1/0 + 10 + 0/1 + 0The same record without the trigger bit (0x0, 0x4B, 0x400) gives identical output in both tools at
main.How tested
src/rna/rseqc/flagstat.rs): a transcription of samtools 1.22.1flagstat_loopandout_fmt_default(bam_stat.c:48-79,124-143) is the oracle.test_flagstat_matches_samtools_flagstat_loop_per_flagchecks each of the 4,096 flags alone in three placements (mate on the same contig; mate on another contig with MAPQ 60; with MAPQ 3);..._mergedchecks all 12,288 records split across two merged accumulators. Atmain: 6,336 of 12,288 cases differ.rustqc rnaand compare the whole flagstat file with samtools 1.22.1 output: the three cases above (test_flagstat_qc_failed_column,test_flagstat_properly_paired_requires_mapped_read,test_flagstat_qc_failed_duplicate_counts_as_duplicate), andtest_flagstat_matches_samtools_on_every_flag, every flag in three placements across chr1/chr2 at--threads 1and2, againsttests/expected/samtools/all_flags.flagstat(generated once with samtools 1.22.1; command in the README beside it; 1.24 prints the same). All four fail atmainand pass here.cargo test --release --locked --offline: 202 unit, 12 bin, 23 integration, 2 doc tests pass.cargo fmt --checkpasses.cargo clippy -- -D warningsreports the same threeneedless_range_looperrors as unmodifiedmainwith the local toolchain (clippy 0.1.90); with that lint allowed,--all-targetsis clean.flagstat_exhaustive.py(each flag plus two background reads, vs samtools 1.22.1) goes from 2,624 of 4,096 flags differing atmainto 1,024, all of them the samtools flagstat: reads with both SECONDARY and SUPPLEMENTARY flags are double-counted in the supplementary total #125 line;counterexamples.pyshows Q1, Q3 and Q4 no longer differ and Q2 (samtools flagstat: reads with both SECONDARY and SUPPLEMENTARY flags are double-counted in the supplementary total #125) still does. Both scripts now exit 1 because they assert the old divergences.Not changed, and why
else if), while its man page defines supplementary as "0x800 bit set"; which one RustQC should follow is an open decision.test_flagstat_secondary_supplementary_overlap_unchangedpins the current output, and the every-flag tests add those records to samtools' supplementary count; both say so and should change with samtools flagstat: reads with both SECONDARY and SUPPLEMENTARY flags are double-counted in the supplementary total #125.f64; samtools uses(float)n / total * 100.0. No test input here rounds differently, but very large counts could differ in the last digit.samtools stats"reads properly paired" still uses the oldproperly_pairedcounter (no!UNMAPguard). That is a separate output with its own samtools definition and is not checked here.🤖 Generated with Claude Code