From ed66fafacc0f1b62d3339a9d51cffa5c795d1ab5 Mon Sep 17 00:00:00 2001 From: Rudhik1904 Date: Fri, 21 Aug 2026 18:45:06 -0500 Subject: [PATCH 1/5] feat(spectronaut): Filter multiply labeled peptides in turnover mode 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. --- R/clean_Spectronaut.R | 7 ++++- R/utils_clean_features.R | 43 ++++++++++++++++++++++++-- inst/tinytest/test_clean_Spectronaut.R | 34 ++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/R/clean_Spectronaut.R b/R/clean_Spectronaut.R index 48b4287d..c1d9f6f1 100644 --- a/R/clean_Spectronaut.R +++ b/R/clean_Spectronaut.R @@ -185,6 +185,10 @@ #' are classified as \code{NA}. For example, if \code{heavyLabels} is #' \code{"Lys6"}, then \code{PEPTIDEZ} is classified as NA since it #' has no lysine residues that could be labeled. +#' +#' Peptides with two or more labelable residues are dropped, counting across +#' all residues named in \code{heavyLabels} combined, since partial labeling +#' is not supported by the turnover model. #' When \code{heavyLabel} is \code{NULL} the column is left untouched so #' that the downstream \code{columns_to_fill} default of \code{"L"} applies, #' preserving backwards compatibility. @@ -209,7 +213,8 @@ ) spec_input = .classifyIsotopeLabelType(spec_input, heavy_regex, - labeled_aa_regex = labeled_aa_regex) + labeled_aa_regex = labeled_aa_regex, + filter_multiply_labeled = TRUE) for (i in seq_along(heavyLabels)) { escaped = gsub("([\\[\\]])", "\\\\\\1", heavyLabels[i], perl = TRUE) diff --git a/R/utils_clean_features.R b/R/utils_clean_features.R index c1c7094a..5149eb9c 100644 --- a/R/utils_clean_features.R +++ b/R/utils_clean_features.R @@ -309,6 +309,36 @@ } +#' Count regex matches per element, scoring no-match and \code{NA} as 0. +#' @param x Character vector to search. +#' @param pattern Perl-compatible regex. +#' @return Integer vector of match counts, the same length as \code{x}. +#' @keywords internal +#' @noRd +.countRegexMatches = function(x, pattern) { + lengths(regmatches(x, gregexpr(pattern, x, perl = TRUE))) +} + + +#' Drop peptides carrying more than one labelable residue. +#' +#' Such peptides can be partially labeled, which the two-state turnover model +#' cannot represent, so heavy and light rows are dropped together to keep the +#' light/heavy ratio unbiased. +#' +#' @param dt \code{data.table} to filter. +#' @param residue_regex Perl-compatible regex matching one labelable residue. +#' @param sequence_column Column of sequences already stripped of label +#' annotations, so residues inside a modification tag are not counted. +#' @return \code{dt} with multiply labeled rows removed. +#' @keywords internal +#' @noRd +.filterMultiplyLabeledPeptides = function(dt, residue_regex, sequence_column) { + n_labelable = .countRegexMatches(dt[[sequence_column]], residue_regex) + dt[n_labelable < 2L, ] +} + + #' Classify IsotopeLabelType from peptide sequence patterns. #' #' Shared core logic for protein turnover workflows in both Spectronaut and @@ -335,16 +365,25 @@ #' Required for Spectronaut mode; must be non-\code{NULL}. #' Exactly one of \code{light_regex} and \code{labeled_aa_regex} must be #' supplied. -#' @return \code{dt} with \code{IsotopeLabelType} column added or updated. +#' @param filter_multiply_labeled Logical; when \code{TRUE}, peptides with two +#' or more labelable residues are dropped before classification. +#' @return \code{dt} with \code{IsotopeLabelType} column added or updated, and +#' multiply labeled rows removed when \code{filter_multiply_labeled} is +#' \code{TRUE}. #' @keywords internal #' @noRd .classifyIsotopeLabelType = function(dt, heavy_regex, light_regex = NULL, - labeled_aa_regex = NULL) { + labeled_aa_regex = NULL, + filter_multiply_labeled = FALSE) { IsotopeLabelType = PeptideSequence = StrippedSequence = NULL if (!is.null(labeled_aa_regex)) { dt[, StrippedSequence := gsub("\\[.*?\\]", "", PeptideSequence)] + if (filter_multiply_labeled) { + dt = .filterMultiplyLabeledPeptides(dt, labeled_aa_regex, + "StrippedSequence") + } dt[, IsotopeLabelType := data.table::fcase( grepl(heavy_regex, PeptideSequence, perl = TRUE), "H", grepl(labeled_aa_regex, StrippedSequence, perl = TRUE), "L", diff --git a/inst/tinytest/test_clean_Spectronaut.R b/inst/tinytest/test_clean_Spectronaut.R index a4a581a7..e1df964c 100644 --- a/inst/tinytest/test_clean_Spectronaut.R +++ b/inst/tinytest/test_clean_Spectronaut.R @@ -73,3 +73,37 @@ expect_equal(result$PeptideSequence, dt = make_spec_input(c("_PEPTIDEK_", "_PEPTIDER_")) result = MSstatsConvert:::.assignSpectronautIsotopeLabelType(dt, heavyLabels = NULL) expect_equal(result, dt) + +# Multiply labeled peptides (2+ labelable residues) are filtered out +dt = make_spec_input(c( + "_PEPTIDEK[Lys6]_", # 1 K, heavy -> kept + "_PEPTIDEK_", # 1 K, light -> kept + "_PEPK[Lys6]TIDEK[Lys6]_", # 2 K, fully heavy -> dropped + "_PEPK[Lys6]TIDEK_", # 2 K, partially labeled -> dropped + "_PEPKTIDEK_", # 2 K, fully light -> dropped + "_ACDEGFHI_" # 0 K -> kept as NA +)) +result = MSstatsConvert:::.assignSpectronautIsotopeLabelType( + dt, heavyLabels = "K[Lys6]") +expect_equal(result$PeptideSequence, + c("_PEPTIDEK_", "_PEPTIDEK_", "_ACDEGFHI_")) +expect_equal(result$IsotopeLabelType, c("H", "L", NA_character_)) + +# Count is taken across all labelable residues combined: one K plus one R is +# doubly labelable when both labels are specified +dt = make_spec_input(c( + "_PEPTIDEK_", # 1 labelable -> kept + "_PEPTIDEKR_", # 1 K + 1 R -> dropped + "_PEPTIDER_" # 1 labelable -> kept +)) +result = MSstatsConvert:::.assignSpectronautIsotopeLabelType( + dt, heavyLabels = c("K[Lys6]", "R[Arg10]")) +expect_equal(result$PeptideSequence, c("_PEPTIDEK_", "_PEPTIDER_")) +expect_equal(result$IsotopeLabelType, c("L", "L")) + +# Residue letters inside an unrelated modification tag are not counted +dt = make_spec_input(c("_S[Kmodification]PEPTIDEK_")) +result = MSstatsConvert:::.assignSpectronautIsotopeLabelType( + dt, heavyLabels = "K[Lys6]") +expect_equal(nrow(result), 1L) +expect_equal(result$IsotopeLabelType, "L") From 70b405142e5adf2deee8cd7242b0999470e9362d Mon Sep 17 00:00:00 2001 From: Rudhik1904 Date: Fri, 21 Aug 2026 18:47:20 -0500 Subject: [PATCH 2/5] feat(diann): Filter multiply labeled peptides in turnover mode 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. --- R/clean_DIANN.R | 8 +++++ R/clean_Spectronaut.R | 5 ++-- R/utils_clean_features.R | 25 ++++++---------- inst/tinytest/test_clean_DIANN.R | 51 ++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/R/clean_DIANN.R b/R/clean_DIANN.R index 38cea27b..ef0e9460 100644 --- a/R/clean_DIANN.R +++ b/R/clean_DIANN.R @@ -243,6 +243,12 @@ #' parenthetical annotation is stripped out of \code{PeptideSequence}, #' leaving the plain amino acid sequence. #' +#' Peptides with two or more labelable residues are dropped, counting across +#' all of \code{labeledAminoAcids} combined, since partial labeling is not +#' supported by the turnover model. This applies only to the +#' \code{ModifiedSequence} path; channel-based labeling is not inferred from +#' sequence content and is left untouched. +#' #' @param dn_input \code{data.table} after column renaming. #' @param labeledAminoAcids Character vector of single-letter amino acid codes #' (e.g. \code{c("K")} or \code{c("K", "R")}), or \code{NULL} to skip @@ -274,6 +280,8 @@ light_regex <- paste0("\\([^-]+-(?:", aa_pattern, ")-L\\)") strip_regex <- paste0("\\([^-]+-(?:", aa_pattern, ")-[HL]\\)") + dn_input <- .filterMultiplyLabeledPeptides(dn_input, aa_pattern, + "\\([^)]*\\)") dn_input <- .classifyIsotopeLabelType(dn_input, heavy_regex, light_regex) dn_input[, PeptideSequence := gsub(strip_regex, "", PeptideSequence, perl = TRUE)] } diff --git a/R/clean_Spectronaut.R b/R/clean_Spectronaut.R index c1d9f6f1..523ee926 100644 --- a/R/clean_Spectronaut.R +++ b/R/clean_Spectronaut.R @@ -212,9 +212,10 @@ collapse = "|" ) + spec_input = .filterMultiplyLabeledPeptides(spec_input, labeled_aa_regex, + "\\[.*?\\]") spec_input = .classifyIsotopeLabelType(spec_input, heavy_regex, - labeled_aa_regex = labeled_aa_regex, - filter_multiply_labeled = TRUE) + labeled_aa_regex = labeled_aa_regex) for (i in seq_along(heavyLabels)) { escaped = gsub("([\\[\\]])", "\\\\\\1", heavyLabels[i], perl = TRUE) diff --git a/R/utils_clean_features.R b/R/utils_clean_features.R index 5149eb9c..d82d29c8 100644 --- a/R/utils_clean_features.R +++ b/R/utils_clean_features.R @@ -326,15 +326,17 @@ #' cannot represent, so heavy and light rows are dropped together to keep the #' light/heavy ratio unbiased. #' -#' @param dt \code{data.table} to filter. +#' @param dt \code{data.table} with a \code{PeptideSequence} column. #' @param residue_regex Perl-compatible regex matching one labelable residue. -#' @param sequence_column Column of sequences already stripped of label -#' annotations, so residues inside a modification tag are not counted. +#' @param strip_regex Perl-compatible regex matching label and modification +#' annotations, removed before counting so that residue letters inside an +#' annotation are not counted. #' @return \code{dt} with multiply labeled rows removed. #' @keywords internal #' @noRd -.filterMultiplyLabeledPeptides = function(dt, residue_regex, sequence_column) { - n_labelable = .countRegexMatches(dt[[sequence_column]], residue_regex) +.filterMultiplyLabeledPeptides = function(dt, residue_regex, strip_regex) { + stripped = gsub(strip_regex, "", dt[["PeptideSequence"]], perl = TRUE) + n_labelable = .countRegexMatches(stripped, residue_regex) dt[n_labelable < 2L, ] } @@ -365,25 +367,16 @@ #' Required for Spectronaut mode; must be non-\code{NULL}. #' Exactly one of \code{light_regex} and \code{labeled_aa_regex} must be #' supplied. -#' @param filter_multiply_labeled Logical; when \code{TRUE}, peptides with two -#' or more labelable residues are dropped before classification. -#' @return \code{dt} with \code{IsotopeLabelType} column added or updated, and -#' multiply labeled rows removed when \code{filter_multiply_labeled} is -#' \code{TRUE}. +#' @return \code{dt} with \code{IsotopeLabelType} column added or updated. #' @keywords internal #' @noRd .classifyIsotopeLabelType = function(dt, heavy_regex, light_regex = NULL, - labeled_aa_regex = NULL, - filter_multiply_labeled = FALSE) { + labeled_aa_regex = NULL) { IsotopeLabelType = PeptideSequence = StrippedSequence = NULL if (!is.null(labeled_aa_regex)) { dt[, StrippedSequence := gsub("\\[.*?\\]", "", PeptideSequence)] - if (filter_multiply_labeled) { - dt = .filterMultiplyLabeledPeptides(dt, labeled_aa_regex, - "StrippedSequence") - } dt[, IsotopeLabelType := data.table::fcase( grepl(heavy_regex, PeptideSequence, perl = TRUE), "H", grepl(labeled_aa_regex, StrippedSequence, perl = TRUE), "L", diff --git a/inst/tinytest/test_clean_DIANN.R b/inst/tinytest/test_clean_DIANN.R index 64c63f2e..0d9ba80f 100644 --- a/inst/tinytest/test_clean_DIANN.R +++ b/inst/tinytest/test_clean_DIANN.R @@ -99,3 +99,54 @@ expect_equal(result_multi_aa$IsotopeLabelType, c("H", "H", "L", "L", NA_character_)) expect_equal(sort(unique(result_multi_aa$PeptideSequence)), c("PEPTIDEAC", "PEPTIDEK", "PEPTIDER")) + +# Multiply labeled peptides (2+ labelable residues) are filtered out +dt_multi_label = data.table::data.table( + PeptideSequence = c( + "PEPTIDEK(SILAC-K-H)", # 1 K, heavy -> kept + "PEPTIDEK(SILAC-K-L)", # 1 K, light -> kept + "PEPK(SILAC-K-H)TIDEK(SILAC-K-H)", # 2 K, heavy -> dropped + "PEPK(SILAC-K-H)TIDEK(SILAC-K-L)", # 2 K, partial -> dropped + "PEPK(SILAC-K-L)TIDEK(SILAC-K-L)", # 2 K, light -> dropped + "PEPTIDEAC" # 0 K -> kept as NA + ) +) +result_multi_label = MSstatsConvert:::.assignDIANNIsotopeLabelType( + dt_multi_label, labeledAminoAcids = c("K"), has_channel = FALSE +) +expect_equal(result_multi_label$PeptideSequence, + c("PEPTIDEK", "PEPTIDEK", "PEPTIDEAC")) +expect_equal(result_multi_label$IsotopeLabelType, c("H", "L", NA_character_)) + +# Count is taken across all labeled amino acids combined +dt_kr = data.table::data.table( + PeptideSequence = c("PEPTIDEK(SILAC-K-H)", + "PEPK(SILAC-K-H)TIDER(SILAC-R-H)", # 1 K + 1 R -> dropped + "PEPTIDER(SILAC-R-L)") +) +result_kr = MSstatsConvert:::.assignDIANNIsotopeLabelType( + dt_kr, labeledAminoAcids = c("K", "R"), has_channel = FALSE +) +expect_equal(result_kr$PeptideSequence, c("PEPTIDEK", "PEPTIDER")) +expect_equal(result_kr$IsotopeLabelType, c("H", "L")) + +# Residue letters inside an unrelated modification are not counted +dt_mod = data.table::data.table( + PeptideSequence = c("PEPTS(Kmod)IDEK(SILAC-K-H)") +) +result_mod = MSstatsConvert:::.assignDIANNIsotopeLabelType( + dt_mod, labeledAminoAcids = c("K"), has_channel = FALSE +) +expect_equal(nrow(result_mod), 1L) +expect_equal(result_mod$IsotopeLabelType, "H") + +# The Channel path is exempt: labeling is not inferred from sequence content +dt_channel_multi = data.table::data.table( + PeptideSequence = c("PEPKTIDEK", "PEPKTIDEK", "PEPTIDEK"), + Channel = c("H", "L", "H") +) +result_channel_multi = MSstatsConvert:::.assignDIANNIsotopeLabelType( + dt_channel_multi, labeledAminoAcids = c("K"), has_channel = TRUE +) +expect_equal(nrow(result_channel_multi), 3L) +expect_equal(result_channel_multi$IsotopeLabelType, c("H", "L", "H")) From 63b32837a4239834c80131288f4e59e373ae1dbc Mon Sep 17 00:00:00 2001 From: Rudhik1904 Date: Sat, 22 Aug 2026 13:59:10 -0500 Subject: [PATCH 3/5] feat(turnover): Report how many multiply labeled peptides were removed 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. --- R/utils_clean_features.R | 19 +++++++++++++++-- inst/tinytest/test_utils_clean_features.R | 26 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/R/utils_clean_features.R b/R/utils_clean_features.R index d82d29c8..0e28d09c 100644 --- a/R/utils_clean_features.R +++ b/R/utils_clean_features.R @@ -324,7 +324,8 @@ #' #' Such peptides can be partially labeled, which the two-state turnover model #' cannot represent, so heavy and light rows are dropped together to keep the -#' light/heavy ratio unbiased. +#' light/heavy ratio unbiased. The number of peptides removed is logged, since +#' the exclusion is otherwise invisible to the user. #' #' @param dt \code{data.table} with a \code{PeptideSequence} column. #' @param residue_regex Perl-compatible regex matching one labelable residue. @@ -337,7 +338,21 @@ .filterMultiplyLabeledPeptides = function(dt, residue_regex, strip_regex) { stripped = gsub(strip_regex, "", dt[["PeptideSequence"]], perl = TRUE) n_labelable = .countRegexMatches(stripped, residue_regex) - dt[n_labelable < 2L, ] + is_multiply_labeled = n_labelable >= 2L + + if (any(is_multiply_labeled)) { + # Count distinct peptides on the stripped sequence, so that the heavy + # and light forms of one peptide are not reported as two. + msg = paste("**", data.table::uniqueN(stripped[is_multiply_labeled]), + "peptide(s) with more than one labelable residue were", + paste0("removed (", sum(is_multiply_labeled), " row(s))."), + "Turnover analysis is currently limited to peptides with", + "exactly one labelable residue.") + getOption("MSstatsLog")("INFO", msg) + getOption("MSstatsMsg")("INFO", msg) + } + + dt[!is_multiply_labeled, ] } diff --git a/inst/tinytest/test_utils_clean_features.R b/inst/tinytest/test_utils_clean_features.R index aec83c3f..4070faa8 100644 --- a/inst/tinytest/test_utils_clean_features.R +++ b/inst/tinytest/test_utils_clean_features.R @@ -216,6 +216,32 @@ result_multi = MSstatsConvert:::.classifyIsotopeLabelType( ) expect_equal(result_multi$IsotopeLabelType, c("H", "H", "L", "L", NA_character_)) +# Test .filterMultiplyLabeledPeptides ---- +# Heavy and light forms of one peptide are reported as a single peptide, and +# the row count is reported alongside it +dt_report = data.table::data.table(PeptideSequence = c( + "_PEPKTIDEK[Lys6]_", "_PEPKTIDEK[Lys6]_", "_PEPKTIDEK_", # 1 peptide, 3 rows + "_PEPTIDEK_" # kept +)) +# The appender writes with cat(), so the console output is captured directly +log_report = capture.output( + kept <- MSstatsConvert:::.filterMultiplyLabeledPeptides( + dt_report, "K", "\\[.*?\\]") +) +expect_true(any(grepl( + "1 peptide(s) with more than one labelable residue were removed (3 row(s))", + log_report, fixed = TRUE))) +expect_equal(nrow(kept), 1L) + +# Nothing to remove means nothing is reported +dt_quiet = data.table::data.table(PeptideSequence = c("_PEPTIDEK_", "_ACDEG_")) +log_quiet = capture.output( + all_kept <- MSstatsConvert:::.filterMultiplyLabeledPeptides( + dt_quiet, "K", "\\[.*?\\]") +) +expect_false(any(grepl("labelable residue", log_quiet, fixed = TRUE))) +expect_equal(nrow(all_kept), 2L) + # Utility function ---- expect_equal(MSstatsConvert:::.combine(c("A", "B"), c("A", "B")), c("A_A", "B_B")) From 7edac9877280be17c5c37b0a6565a0f325b2c437 Mon Sep 17 00:00:00 2001 From: Rudhik1904 Date: Sat, 22 Aug 2026 14:19:02 -0500 Subject: [PATCH 4/5] docs(turnover): Document multiply labeled peptide filtering 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. --- R/converters_DIANNtoMSstatsFormat.R | 11 +++++++++++ R/converters_SpectronauttoMSstatsFormat.R | 9 +++++++++ man/DIANNtoMSstatsFormat.Rd | 11 +++++++++++ man/MSstatsClean.Rd | 22 +++++++++++++++++++++- man/SpectronauttoMSstatsFormat.Rd | 11 ++++++++++- man/dot-cleanRawDIANN.Rd | 11 +++++++++++ man/dot-cleanRawSpectronaut.Rd | 11 ++++++++++- 7 files changed, 83 insertions(+), 3 deletions(-) diff --git a/R/converters_DIANNtoMSstatsFormat.R b/R/converters_DIANNtoMSstatsFormat.R index baddebbb..01a1c3e7 100644 --- a/R/converters_DIANNtoMSstatsFormat.R +++ b/R/converters_DIANNtoMSstatsFormat.R @@ -48,6 +48,17 @@ #' parenthetical annotation is stripped out of \code{PeptideSequence}, #' leaving the plain amino acid sequence. #' +#' In this path only, peptides carrying more than one labelable residue are +#' removed, and the number removed is reported. Residues are counted across +#' all of \code{labeledAminoAcids} together, so with \code{c("K", "R")} a +#' peptide containing one lysine and one arginine counts as two and is removed. +#' Such peptides can be only partially labeled, producing more than the two +#' mass states (fully light and fully heavy) that the turnover model +#' represents. Supporting them is future work; turnover analysis is currently +#' limited to peptides with exactly one labelable residue. The channel-based +#' path above is unaffected, since it does not infer labeling from sequence +#' content. +#' #' When \code{NULL} (default), protein-turnover mode is disabled and all #' peptides receive \code{IsotopeLabelType = "Light"}. #' @param quantificationColumn Use 'FragmentQuantCorrected'(default) column for quantified intensities for DIANN 1.8.x. diff --git a/R/converters_SpectronauttoMSstatsFormat.R b/R/converters_SpectronauttoMSstatsFormat.R index b929b0e3..5e4c8247 100644 --- a/R/converters_SpectronauttoMSstatsFormat.R +++ b/R/converters_SpectronauttoMSstatsFormat.R @@ -23,6 +23,15 @@ #' (\code{IsotopeLabelType = NA}) based on its labeled sequence. When #' \code{NULL} (default) all peptides receive \code{IsotopeLabelType = "L"}. #' Useful for protein turnover experiments. +#' +#' Peptides carrying more than one labelable residue are removed, and the +#' number removed is reported. Residues are counted across all labels +#' supplied together, so with \code{c("Lys6", "Arg10")} a peptide containing +#' one lysine and one arginine counts as two and is removed. Such peptides +#' can be only partially labeled, producing more than the two mass states +#' (fully light and fully heavy) that the turnover model represents. +#' Supporting them is future work; turnover analysis is currently limited to +#' peptides with exactly one labelable residue. #' @param excludedFromQuantificationFilter Remove rows with F.ExcludedFromQuantification=TRUE Default is TRUE. #' @param filter_with_Qvalue FALSE(default) will not perform any filtering. TRUE will filter out the intensities that have greater than qvalue_cutoff in EG.Qvalue column. Those intensities will be replaced with zero and will be considered as censored missing values for imputation purpose. #' @param qvalue_cutoff Cutoff for EG.Qvalue. default is 0.01. diff --git a/man/DIANNtoMSstatsFormat.Rd b/man/DIANNtoMSstatsFormat.Rd index 5fe18013..a4246a44 100644 --- a/man/DIANNtoMSstatsFormat.Rd +++ b/man/DIANNtoMSstatsFormat.Rd @@ -91,6 +91,17 @@ tags are assigned \code{IsotopeLabelType = NA}. Once classified, the parenthetical annotation is stripped out of \code{PeptideSequence}, leaving the plain amino acid sequence. +In this path only, peptides carrying more than one labelable residue are +removed, and the number removed is reported. Residues are counted across +all of \code{labeledAminoAcids} together, so with \code{c("K", "R")} a +peptide containing one lysine and one arginine counts as two and is removed. +Such peptides can be only partially labeled, producing more than the two +mass states (fully light and fully heavy) that the turnover model +represents. Supporting them is future work; turnover analysis is currently +limited to peptides with exactly one labelable residue. The channel-based +path above is unaffected, since it does not infer labeling from sequence +content. + When \code{NULL} (default), protein-turnover mode is disabled and all peptides receive \code{IsotopeLabelType = "Light"}.} diff --git a/man/MSstatsClean.Rd b/man/MSstatsClean.Rd index d8cccabe..300ea372 100644 --- a/man/MSstatsClean.Rd +++ b/man/MSstatsClean.Rd @@ -147,7 +147,16 @@ classified as heavy (\code{IsotopeLabelType = "H"}), light (\code{IsotopeLabelType = "L"}), or unlabeled (\code{IsotopeLabelType = NA}) based on its labeled sequence. When \code{NULL} (default) all peptides receive \code{IsotopeLabelType = "L"}. -Useful for protein turnover experiments.} +Useful for protein turnover experiments. + +Peptides carrying more than one labelable residue are removed, and the +number removed is reported. Residues are counted across all labels +supplied together, so with \code{c("Lys6", "Arg10")} a peptide containing +one lysine and one arginine counts as two and is removed. Such peptides +can be only partially labeled, producing more than the two mass states +(fully light and fully heavy) that the turnover model represents. +Supporting them is future work; turnover analysis is currently limited to +peptides with exactly one labelable residue.} \item{peptide_id_col}{character name of a column that identifies peptides} @@ -204,6 +213,17 @@ tags are assigned \code{IsotopeLabelType = NA}. Once classified, the parenthetical annotation is stripped out of \code{PeptideSequence}, leaving the plain amino acid sequence. +In this path only, peptides carrying more than one labelable residue are +removed, and the number removed is reported. Residues are counted across +all of \code{labeledAminoAcids} together, so with \code{c("K", "R")} a +peptide containing one lysine and one arginine counts as two and is removed. +Such peptides can be only partially labeled, producing more than the two +mass states (fully light and fully heavy) that the turnover model +represents. Supporting them is future work; turnover analysis is currently +limited to peptides with exactly one labelable residue. The channel-based +path above is unaffected, since it does not infer labeling from sequence +content. + When \code{NULL} (default), protein-turnover mode is disabled and all peptides receive \code{IsotopeLabelType = "Light"}.} diff --git a/man/SpectronauttoMSstatsFormat.Rd b/man/SpectronauttoMSstatsFormat.Rd index 91caca28..73d0cec8 100644 --- a/man/SpectronauttoMSstatsFormat.Rd +++ b/man/SpectronauttoMSstatsFormat.Rd @@ -60,7 +60,16 @@ classified as heavy (\code{IsotopeLabelType = "H"}), light (\code{IsotopeLabelType = "L"}), or unlabeled (\code{IsotopeLabelType = NA}) based on its labeled sequence. When \code{NULL} (default) all peptides receive \code{IsotopeLabelType = "L"}. -Useful for protein turnover experiments.} +Useful for protein turnover experiments. + +Peptides carrying more than one labelable residue are removed, and the +number removed is reported. Residues are counted across all labels +supplied together, so with \code{c("Lys6", "Arg10")} a peptide containing +one lysine and one arginine counts as two and is removed. Such peptides +can be only partially labeled, producing more than the two mass states +(fully light and fully heavy) that the turnover model represents. +Supporting them is future work; turnover analysis is currently limited to +peptides with exactly one labelable residue.} \item{excludedFromQuantificationFilter}{Remove rows with F.ExcludedFromQuantification=TRUE Default is TRUE.} diff --git a/man/dot-cleanRawDIANN.Rd b/man/dot-cleanRawDIANN.Rd index 92bcf043..fbdb4ece 100644 --- a/man/dot-cleanRawDIANN.Rd +++ b/man/dot-cleanRawDIANN.Rd @@ -71,6 +71,17 @@ tags are assigned \code{IsotopeLabelType = NA}. Once classified, the parenthetical annotation is stripped out of \code{PeptideSequence}, leaving the plain amino acid sequence. +In this path only, peptides carrying more than one labelable residue are +removed, and the number removed is reported. Residues are counted across +all of \code{labeledAminoAcids} together, so with \code{c("K", "R")} a +peptide containing one lysine and one arginine counts as two and is removed. +Such peptides can be only partially labeled, producing more than the two +mass states (fully light and fully heavy) that the turnover model +represents. Supporting them is future work; turnover analysis is currently +limited to peptides with exactly one labelable residue. The channel-based +path above is unaffected, since it does not infer labeling from sequence +content. + When \code{NULL} (default), protein-turnover mode is disabled and all peptides receive \code{IsotopeLabelType = "Light"}.} } diff --git a/man/dot-cleanRawSpectronaut.Rd b/man/dot-cleanRawSpectronaut.Rd index edc51a3a..7412c8c7 100644 --- a/man/dot-cleanRawSpectronaut.Rd +++ b/man/dot-cleanRawSpectronaut.Rd @@ -42,7 +42,16 @@ classified as heavy (\code{IsotopeLabelType = "H"}), light (\code{IsotopeLabelType = "L"}), or unlabeled (\code{IsotopeLabelType = NA}) based on its labeled sequence. When \code{NULL} (default) all peptides receive \code{IsotopeLabelType = "L"}. -Useful for protein turnover experiments.} +Useful for protein turnover experiments. + +Peptides carrying more than one labelable residue are removed, and the +number removed is reported. Residues are counted across all labels +supplied together, so with \code{c("Lys6", "Arg10")} a peptide containing +one lysine and one arginine counts as two and is removed. Such peptides +can be only partially labeled, producing more than the two mass states +(fully light and fully heavy) that the turnover model represents. +Supporting them is future work; turnover analysis is currently limited to +peptides with exactly one labelable residue.} } \value{ \code{data.table} From 556ab9d136e558a5ac0632d9327357fd4de96db8 Mon Sep 17 00:00:00 2001 From: Rudhik1904 Date: Sun, 23 Aug 2026 20:23:52 -0500 Subject: [PATCH 5/5] refactor(Spectronaut): Update heavyLabels parameter documentation for clarity --- R/clean_Spectronaut.R | 20 ++++++++++++-------- R/converters_SpectronauttoMSstatsFormat.R | 18 +++++++++++------- man/MSstatsClean.Rd | 18 +++++++++++------- man/SpectronauttoMSstatsFormat.Rd | 18 +++++++++++------- man/dot-cleanRawSpectronaut.Rd | 18 +++++++++++------- 5 files changed, 56 insertions(+), 36 deletions(-) diff --git a/R/clean_Spectronaut.R b/R/clean_Spectronaut.R index 523ee926..ac46a6f1 100644 --- a/R/clean_Spectronaut.R +++ b/R/clean_Spectronaut.R @@ -179,12 +179,15 @@ #' #' In Spectronaut protein turnover reports, heavy peptides appear in #' \code{FG.LabeledSequence} with a bracketed modification, e.g. -#' \code{_PEPTIDEK[Lys6]_}. Any sequence that contains -#' \code{[]} is classified as heavy; all others are light. -#' Sequences that do not have amino acids that can carry the label -#' are classified as \code{NA}. For example, if \code{heavyLabels} is -#' \code{"Lys6"}, then \code{PEPTIDEZ} is classified as NA since it -#' has no lysine residues that could be labeled. +#' \code{_PEPTIDEK[Lys6]_}. Each entry of \code{heavyLabels} names the +#' labelable residue and the label together, as \code{[