Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions R/utils_fractions.R
Original file line number Diff line number Diff line change
Expand Up @@ -265,16 +265,30 @@
#' @keywords internal
.removeOverlappingFeatures = function(input) {
Fraction = NULL

if (data.table::uniqueN(input$Fraction) > 1) {
measurement_count = input[
(IsotopeLabelType == "L" | is.na(IsotopeLabelType)) &
!is.na(Intensity) & Intensity > 0,
.(n_obs = uniqueN(Run)),
by = .(feature, Fraction)
]
# Features absent from measurement_count have no L or NA observations.
# Fall back to H observations so they are not silently dropped.
h_only_features = setdiff(unique(input$feature), unique(measurement_count$feature))
if (length(h_only_features) > 0) {
h_count = input[
feature %in% h_only_features &
IsotopeLabelType == "H" &
!is.na(Intensity) & Intensity > 0,
.(n_obs = uniqueN(Run)),
by = .(feature, Fraction)
]
measurement_count = rbind(measurement_count, h_count)
}
measurement_count[, is_max := n_obs == max(n_obs), by = "feature"]
max_fractions = measurement_count[(is_max)]

fraction_map = .resolveFractionTies(input, max_fractions)
input = input[fraction_map, on = .(feature, Fraction), nomatch = 0]
}
Expand All @@ -293,14 +307,28 @@
#' @keywords internal
.resolveFractionTies = function(input, max_fractions) {
tie_features = max_fractions[, .(n_ties = .N), by = "feature"][n_ties > 1, feature]

if (length(tie_features) > 0) {
tied_fractions = max_fractions[feature %in% tie_features, .(feature, Fraction)]
avg_abundance = input[
tied_fractions, on = .(feature, Fraction), nomatch = 0
][!is.na(Intensity) & Intensity > 0,
.(mean_abundance = mean(Intensity)),
by = .(feature, Fraction)]
tied_input = input[tied_fractions, on = .(feature, Fraction), nomatch = 0]
avg_abundance = tied_input[
(IsotopeLabelType == "L" | is.na(IsotopeLabelType)) &
!is.na(Intensity) & Intensity > 0,
.(mean_abundance = mean(Intensity)),
by = .(feature, Fraction)
]
# H-only tied features have no L or NA rows; fall back to H for tie-breaking.
h_only_tied = setdiff(tie_features, unique(avg_abundance$feature))
if (length(h_only_tied) > 0) {
h_avg = tied_input[
feature %in% h_only_tied &
IsotopeLabelType == "H" &
!is.na(Intensity) & Intensity > 0,
.(mean_abundance = mean(Intensity)),
by = .(feature, Fraction)
]
avg_abundance = rbind(avg_abundance, h_avg)
}
best_tied = avg_abundance[, .SD[which.max(mean_abundance)], by = "feature"]
best_simple = max_fractions[
!feature %in% tie_features,
Expand Down
69 changes: 69 additions & 0 deletions inst/tinytest/test_fractions.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ fractionated = data.table::data.table(
feature = rep(c("A", "B"), each = 6),
Fraction = rep(rep(c(1, 2), each = 3), times = 2),
Run = 1:12,
IsotopeLabelType = "L",
Intensity = c(NA, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2)
)
### More observations win
Expand All @@ -87,6 +88,7 @@ fractionated_third = data.table::data.table(
feature = rep("A", 9),
Fraction = c(rep(1, 3), rep(2, 3), rep(3, 3)),
Run = 1:9,
IsotopeLabelType = "L",
Intensity = c(1, 1, 1, # Fraction 1: 3 obs, mean = 1 (ties for max n_obs)
2, 2, 2, # Fraction 2: 3 obs, mean = 2 (ties for max n_obs)
10, NA, NA) # Fraction 3: 1 obs, mean = 10 (loses on n_obs but mean would win w/o fix)
Expand All @@ -95,6 +97,73 @@ expect_equal(
unique(MSstatsConvert:::.removeOverlappingFeatures(fractionated_third)$Fraction),
2
)
### L/H isotope label types: fraction selection uses only L obs, output keeps both L and H;
### NA-only features: fraction selection uses NA obs
# Feature "A" (L+H): F1 has 1 L obs (run 1) + 4 H obs (runs 1-4) = 4 unique runs total
# F2 has 3 L obs (runs 5-7) + 1 H obs (run 5) = 3 unique runs total
# → If counting all unique runs: F1 wins (4 > 3)
# → If counting L unique runs only: F2 wins (3 > 1)
# → Expected: F2 selected, confirming H obs do not influence fraction selection
# → Both L and H rows from F2 are returned
# Feature "B" (NA only): F1 has 2 NA obs (runs 8-9), F2 has 4 NA obs (runs 10-13)
# → NA obs: F1=2, F2=4 → F2 wins; all 4 F2 rows are kept
fractionated_lh = data.table::data.table(
feature = c(rep("A", 9), rep("B", 6)),
Fraction = c(rep(1, 5), rep(2, 4),
rep(1, 2), rep(2, 4)),
Run = c(1, 1, 2, 3, 4, # A F1: run 1 paired L+H, runs 2-4 H only
5, 5, 6, 7, # A F2: run 5 paired L+H, runs 6-7 L only
8, 9, 10, 11, 12, 13),
IsotopeLabelType = c("L", "H", "H", "H", "H", # A F1: 1 L, 4 H
"L", "H", "L", "L", # A F2: 3 L, 1 H
rep(NA_character_, 6)),
Intensity = rep(1, 15)
)
### Feature A: F1 has more total obs but fewer L obs — F2 must win to confirm L-only logic
expect_equal(
unique(MSstatsConvert:::.removeOverlappingFeatures(fractionated_lh[feature == "A"])$Fraction),
2
)
### Both L and H rows are returned for the winning fraction
expect_true(
all(c("L", "H") %in% MSstatsConvert:::.removeOverlappingFeatures(fractionated_lh[feature == "A"])$IsotopeLabelType)
)
### Feature B (NA only) selected via NA obs count
expect_equal(
unique(MSstatsConvert:::.removeOverlappingFeatures(fractionated_lh[feature == "B"])$Fraction),
2
)
### Full dataset: A keeps F2 (L+H rows), B keeps F2 (NA rows)
expect_identical(
MSstatsConvert:::.removeOverlappingFeatures(data.table::copy(fractionated_lh)),
fractionated_lh[(feature == "A" & Fraction == 2) | (feature == "B" & Fraction == 2)]
)
### H-only feature: falls back to H obs for fraction selection (not silently dropped)
# F1 has 1 H obs, F2 has 3 H obs → F2 wins
fractionated_h_only = data.table::data.table(
feature = rep("A", 4),
Fraction = c(1, 2, 2, 2),
Run = 1:4,
IsotopeLabelType = "H",
Intensity = 1
)
expect_equal(
unique(MSstatsConvert:::.removeOverlappingFeatures(fractionated_h_only)$Fraction),
2
)
### H-only feature with tied obs count: higher mean H intensity breaks the tie
# F1: 2 obs, mean intensity 1; F2: 2 obs, mean intensity 3 → F2 wins
fractionated_h_tied = data.table::data.table(
feature = rep("A", 4),
Fraction = rep(c(1, 2), each = 2),
Run = 1:4,
IsotopeLabelType = "H",
Intensity = c(1, 1, 3, 3)
)
expect_equal(
unique(MSstatsConvert:::.removeOverlappingFeatures(fractionated_h_tied)$Fraction),
2
)
fractionated_tmt = fractionated = data.table::data.table(
feature = rep(c("A", "B"), each = 6),
Fraction = rep(rep(c(1, 2), each = 3), times = 2),
Expand Down
21 changes: 0 additions & 21 deletions man/dot-getCorrectFraction.Rd

This file was deleted.

26 changes: 26 additions & 0 deletions man/dot-resolveFractionTies.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading