Skip to content

Feature filter multiply labeled peptides - #145

Open
Rudhik1904 wants to merge 5 commits into
develfrom
feature-filter_multiply_labeled_peptides
Open

Feature filter multiply labeled peptides#145
Rudhik1904 wants to merge 5 commits into
develfrom
feature-filter_multiply_labeled_peptides

Conversation

@Rudhik1904

@Rudhik1904 Rudhik1904 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Motivation and Context

Protein turnover analysis models each peptide as existing in exactly two mass
states: fully light and fully heavy. A peptide carrying more than one labelable
residue (e.g. two lysines under Lys6) breaks that assumption, because it can be
partially labeled and so occupies intermediate mass states the model has no
representation for. Including such peptides biases the fraction-new denominator.

Both turnover-capable converters were letting these peptides through. Label
assignment tested only for the presence of a labelable residue via grepl,
never the count, so a peptide with two or more labelable residues was classified
as heavy or light like any other and passed into downstream analysis.

This PR filters peptides with two or more labelable residues out of the turnover
paths in the Spectronaut and DIA-NN converters, and reports how many were
removed. Heavy and light rows for the same peptide are dropped together, so the
light/heavy ratio stays unbiased.

Changes

  • Added .filterMultiplyLabeledPeptides() to R/utils_clean_features.R: strips
    label and modification annotations from PeptideSequence, counts labelable
    residues on the stripped sequence, and drops rows with a count of two or more.
  • Added .countRegexMatches() helper in the same file, scoring no-match and NA
    as zero.
  • Called the new filter from .assignSpectronautIsotopeLabelType() in
    R/clean_Spectronaut.R, ahead of .classifyIsotopeLabelType(). Residues are
    counted across all labels in heavyLabels combined, so with
    c("K[Lys6]", "R[Arg10]") a peptide with one lysine and one arginine counts as
    two and is removed.
  • Called the new filter from the ModifiedSequence path in R/clean_DIANN.R,
    counting across all of labeledAminoAcids combined. The channel-based path is
    deliberately exempt, since it does not infer labeling from sequence content.
  • Annotations are stripped before counting, so residue letters appearing inside
    an unrelated modification are not miscounted as labelable sites.
  • Removal is reported through MSstatsLog/MSstatsMsg at INFO, since the
    exclusion is otherwise invisible to the user. Distinct peptides are counted on
    the stripped sequence so the heavy and light forms of one peptide are reported
    as one peptide, with the affected row count given alongside.
  • Documented the behavior and its rationale on the user-facing heavyLabels and
    labeledAminoAcids parameters, including the note that supporting partially
    labeled peptides is future work, and regenerated the affected .Rd files.

Testing

Added unit tests; full suite is 766 assertions, 0 failures, no new warnings.

  • inst/tinytest/test_utils_clean_features.R — direct tests of
    .filterMultiplyLabeledPeptides(): that heavy and light forms of one peptide
    are reported as a single peptide with the row count alongside, and that nothing
    is reported when there is nothing to remove. Console output is captured
    directly, since the appender writes with cat().
  • inst/tinytest/test_clean_Spectronaut.R — multiply labeled peptides are
    filtered from .assignSpectronautIsotopeLabelType(); the count is taken across
    all labels combined; residue letters inside an unrelated modification are not
    counted; heavyLabels = NULL leaves the data untouched.
  • inst/tinytest/test_clean_DIANN.R — the same cases for the ModifiedSequence
    path, plus a test asserting the Channel path is exempt and its rows are
    preserved.

Checklist Before Requesting a Review

  • I have read the MSstats contributing guidelines
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules

Motivation and Context

Turnover workflows do not support peptides with multiple labelable residues. These peptides can produce more than two labeling states. The PR removes these peptides before isotope-label classification.

The filtering applies to Spectronaut and DIA-NN ModifiedSequence workflows. DIA-NN channel-based workflows remain unchanged.

Changes

  • Added shared utilities to:
    • Count labelable residues after removing sequence annotations.
    • Remove peptides with two or more labelable residues.
    • Remove all heavy and light rows for each affected peptide.
    • Report removed peptide and row counts through MSstatsLog and MSstatsMsg at INFO.
  • Applied filtering across all configured Spectronaut heavy labels.
  • Applied filtering to the DIA-NN ModifiedSequence path.
  • Updated heavyLabels documentation to require residue-qualified labels, such as K[Lys6].
  • Documented the current limitation to peptides with exactly one labelable residue.
  • Updated Roxygen-generated .Rd documentation.
  • Documented that partial labeling is not supported.

Unit Tests

  • Added tests for multiply labeled peptide removal.
  • Verified removal across combined labels, such as lysine and arginine.
  • Verified that sequence annotations do not affect residue counts.
  • Verified removal of all rows for affected peptides.
  • Verified peptide and row count reporting.
  • Verified preservation of singly labeled and unlabeled peptides.
  • Verified null-label handling.
  • Verified that DIA-NN channel-based workflows retain all rows.
  • The full test suite reports 766 assertions with no failures or new warnings.

Coding Guideline Violations

The provided change summary does not identify any coding guideline violations.

Label assignment previously tested only for the presence of a labelable
residue via grepl, never the count, so peptides with 2+ labelable
residues (e.g. 2 lysines under Lys6) passed through into turnover
analysis. Those peptides can be partially labeled, which the two-state
turnover model cannot represent.

Add .countRegexMatches and .filterMultiplyLabeledPeptides to the shared
feature-cleaning utils, and gate them behind a new filter_multiply_
labeled flag on .classifyIsotopeLabelType so DIANN is unaffected until
it opts in. Spectronaut enables the filter.

The count is taken on the bracket-stripped sequence so that residue
letters inside an unrelated modification tag are not counted, and across
all residues in heavyLabels combined, since one lysine plus one arginine
is doubly labelable when both labels are specified. Heavy and light rows
are dropped together to avoid biasing the fraction-new denominator.
Apply the same 2+ labelable residue filter to the DIANN
ModifiedSequence path. The Channel path is exempt: label state is read
from a dedicated column rather than inferred from sequence content, so
the converter cannot see the partial-labeling state at this layer.

Move the filter out of .classifyIsotopeLabelType and into an explicit
pre-processing call in each converter. DIANN could not reuse the flag
added in the previous commit, since its light_regex matches label tags
rather than bare residues, leaving the classifier with no residue
pattern to count in DIANN mode. Threading two more regexes through a
five-parameter function would also have kept a function named
"classify" silently dropping rows. .filterMultiplyLabeledPeptides now
strips annotations itself, so both converters share the counting logic
while supplying their own patterns.

Counting strips all parentheticals rather than only the label tags, so
that a residue letter inside an unrelated modification such as
(Kmod) is not counted, mirroring how the Spectronaut path strips all
bracket annotations before counting.
The filter was silent, so peptides disappeared from a user's data with
nothing to trace the loss back to. Log the exclusion through the
standard MSstatsLog/MSstatsMsg pair, at INFO rather than WARN since
this is intentional documented behaviour rather than a data problem.

Report distinct peptides rather than rows, counted on the stripped
sequence so the heavy and light forms of one peptide are not reported
as two, with the row count alongside it for scale. Rows alone would
overstate the loss, since one peptide spans many fragment ions, charge
states and runs.

Both converters route through .filterMultiplyLabeledPeptides, so a
single call site covers Spectronaut and DIANN.
The user-facing @PARAM blocks described peptides as being classified
heavy, light or unlabeled, with no mention of the fourth outcome now
possible: removed entirely. Anyone reading ?SpectronauttoMSstatsFormat
or ?DIANNtoMSstatsFormat would have had no way to know some of their
peptides do not survive the converter.

Document the filter on both converters, stating that residues are
counted across all supplied labels together, and flag support for
multiply labeled peptides as future work rather than a settled design
choice.

On DIANN the note sits inside the ModifiedSequence-parsing section and
says the channel path is unaffected, since the filter applies to only
one of that converter's two paths.

Regenerate man/. MSstatsClean.Rd and the two .cleanRaw* pages inherit
these params, so they pick the text up as well.
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a0d22ff-7f1f-4e5f-af17-1d0c7d803c27

📥 Commits

Reviewing files that changed from the base of the PR and between e82b79e and 556ab9d.

📒 Files selected for processing (13)
  • R/clean_DIANN.R
  • R/clean_Spectronaut.R
  • R/converters_DIANNtoMSstatsFormat.R
  • R/converters_SpectronauttoMSstatsFormat.R
  • R/utils_clean_features.R
  • inst/tinytest/test_clean_DIANN.R
  • inst/tinytest/test_clean_Spectronaut.R
  • inst/tinytest/test_utils_clean_features.R
  • man/DIANNtoMSstatsFormat.Rd
  • man/MSstatsClean.Rd
  • man/SpectronauttoMSstatsFormat.Rd
  • man/dot-cleanRawDIANN.Rd
  • man/dot-cleanRawSpectronaut.Rd

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

DIA-NN and Spectronaut sequence-based isotope-label workflows now remove peptides with multiple labelable residues before classification. The change adds shared filtering helpers, preserves DIA-NN channel processing, updates Spectronaut label syntax documentation, and adds tests.

Changes

Isotope-label filtering

Layer / File(s) Summary
Filtering helper and classification wiring
R/utils_clean_features.R, R/clean_DIANN.R, R/clean_Spectronaut.R
Added residue-counting and filtering helpers. DIA-NN and Spectronaut invoke filtering before isotope-label classification.
Workflow and helper tests
inst/tinytest/test_clean_DIANN.R, inst/tinytest/test_clean_Spectronaut.R, inst/tinytest/test_utils_clean_features.R
Tests cover multiple labelable residues, combined residue counts, modification annotations, removal reporting, retained labels, and the DIA-NN channel path.
Label-format and filtering documentation
R/clean_DIANN.R, R/clean_Spectronaut.R, R/converters_DIANNtoMSstatsFormat.R, R/converters_SpectronauttoMSstatsFormat.R, man/*.Rd
Documented residue-qualified Spectronaut labels, multi-label peptide removal, DIA-NN path differences, and the turnover limitation to peptides with one labelable residue.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 556ab

The PR filters multiply labeled peptides from the affected turnover conversion paths and reports removals; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant SequenceData
  participant filterMultiplyLabeledPeptides
  participant IsotopeClassification
  SequenceData->>filterMultiplyLabeledPeptides: count labelable residues
  filterMultiplyLabeledPeptides-->>SequenceData: remove multiply labeled rows
  SequenceData->>IsotopeClassification: classify remaining sequences
Loading

Suggested reviewers: tonywu1999

Poem

I’m a rabbit with labels to sort,
Two marks in one peptide fall short.
K and R join the count,
Then filtered rows mount—
Clean sequences hop to report.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: filtering multiply labeled peptides.
Description check ✅ Passed The description includes the required context, changes, testing, and checklist sections with detailed and relevant information.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (13 skipped: 13 unsupported.)
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature-filter_multiply_labeled_peptides

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant