Skip to content

Commit 5ab0b47

Browse files
authored
fix(globalStandards): Use median instead of mean for global standards normalization (#198)
1 parent 1c96555 commit 5ab0b47

2 files changed

Lines changed: 33 additions & 24 deletions

File tree

R/utils_normalize.R

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,11 @@ MSstatsNormalize = function(input, normalization_method, peptides_dict = NULL, s
196196
#' @keywords internal
197197
.normalizeGlobalStandards = function(input, peptides_dict, standards) {
198198
PeptideSequence = PEPTIDE = PROTEIN = median_by_fraction = NULL
199-
Standard = FRACTION = LABEL = ABUNDANCE = RUN = mean_by_run = NULL
199+
Standard = FRACTION = LABEL = ABUNDANCE = RUN = median_by_run = NULL
200200

201201
input_with_peptides <- merge(input, peptides_dict, by = "PEPTIDE", all.x = TRUE)
202-
if (length(standards) == 1 && standards == "unlabeled") {
202+
is_unlabeled <- length(standards) == 1 && standards == "unlabeled"
203+
if (is_unlabeled) {
203204
standards = unique(input_with_peptides[is.na(input_with_peptides$LABEL), ]$PeptideSequence)
204205
if (length(standards) == 0) {
205206
msg = "nameStandards = 'unlabeled' but no unlabeled peptides found in data."
@@ -224,29 +225,37 @@ MSstatsNormalize = function(input, normalization_method, peptides_dict = NULL, s
224225
standards_data[, standard := ifelse(!is.na(PeptideSequence) & PeptideSequence %in% standards,
225226
PeptideSequence,
226227
PROTEIN)]
227-
means_by_standard <- standards_data[,
228-
list(mean_abundance = mean(ABUNDANCE, na.rm = TRUE)),
229-
by = .(RUN, standard)
230-
]
231-
means_by_standard <- dcast(means_by_standard,
232-
RUN ~ standard,
233-
value.var = "mean_abundance")
234-
means_by_standard = data.table::melt(means_by_standard, id.vars = "RUN",
235-
variable.name = "Standard", value.name = "ABUNDANCE")
236-
means_by_standard[, mean_by_run := mean(ABUNDANCE, na.rm = TRUE), by = "RUN"]
237-
means_by_standard = merge(means_by_standard, unique(input[, list(RUN, FRACTION)]),
238-
by = "RUN")
239-
means_by_standard[, median_by_fraction := median(mean_by_run, na.rm = TRUE),
240-
by = "FRACTION"]
241-
means_by_standard[, ABUNDANCE := NULL]
242-
means_by_standard[, Standard := NULL]
243-
means_by_standard = unique(means_by_standard)
228+
if (is_unlabeled) {
229+
run_summaries <- standards_data[,
230+
list(median_by_run = median(ABUNDANCE, na.rm = TRUE)),
231+
by = "RUN"]
232+
run_summaries <- merge(run_summaries, unique(input[, list(RUN, FRACTION)]), by = "RUN")
233+
run_summaries[, median_by_fraction := median(median_by_run, na.rm = TRUE), by = "FRACTION"]
234+
} else {
235+
medians_by_standard <- standards_data[,
236+
list(median_abundance = median(ABUNDANCE, na.rm = TRUE)),
237+
by = .(RUN, standard)
238+
]
239+
medians_by_standard <- dcast(medians_by_standard,
240+
RUN ~ standard,
241+
value.var = "median_abundance")
242+
medians_by_standard <- data.table::melt(medians_by_standard, id.vars = "RUN",
243+
variable.name = "Standard", value.name = "ABUNDANCE")
244+
medians_by_standard[, median_by_run := median(ABUNDANCE, na.rm = TRUE), by = "RUN"]
245+
medians_by_standard <- merge(medians_by_standard, unique(input[, list(RUN, FRACTION)]),
246+
by = "RUN")
247+
medians_by_standard[, median_by_fraction := median(median_by_run, na.rm = TRUE),
248+
by = "FRACTION"]
249+
medians_by_standard[, ABUNDANCE := NULL]
250+
medians_by_standard[, Standard := NULL]
251+
run_summaries <- unique(medians_by_standard)
252+
}
244253

245-
input = merge(input, means_by_standard, all.x = TRUE, by = c("RUN", "FRACTION"))
246-
input[, ABUNDANCE := ABUNDANCE - mean_by_run + median_by_fraction]
254+
input = merge(input, run_summaries, all.x = TRUE, by = c("RUN", "FRACTION"))
255+
input[, ABUNDANCE := ABUNDANCE - median_by_run + median_by_fraction]
247256

248257
getOption("MSstatsLog")("INFO", "Normalization : normalization with global standards protein - okay")
249-
input[ , !(colnames(input) %in% c("mean_by_run", "median_by_fraction")), with = FALSE]
258+
input[ , !(colnames(input) %in% c("median_by_run", "median_by_fraction")), with = FALSE]
250259
}
251260

252261

inst/tinytest/test_utils_normalize.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ test_unlabeled_standard_detected <- function() {
116116

117117
output <- MSstats:::.normalizeGlobalStandards(input, peptide_dict, "unlabeled")
118118

119-
# Uniform standard => median_by_fraction == mean_by_run for every run,
120-
# so ABUNDANCE is unchanged for all peptides.
119+
# Uniform standard => median_by_fraction == median_by_run for every run,
120+
# so ABUNDANCE is unchanged for all peptides (feature-level median, not per-peptide).
121121
expect_equal(
122122
output$ABUNDANCE, input$ABUNDANCE,
123123
info = "unlabeled path: uniform standard intensities should produce no shift in ABUNDANCE"

0 commit comments

Comments
 (0)