R/prefilter_tracers.R

Defines functions prefilter_tracers

Documented in prefilter_tracers

#' Prefilter tracers and clean raw or summary source datasets
#'
#' Evaluates source samples and removes rows that exceed user-defined thresholds for missing isotopic ratios or missing/zero elemental contents. Absolute zeros
#' in content columns are treated as analytically missing data and are actively converted to NAs to prevent downstream statistical bias. The mixture row
#' (assumed to be the last row) is always preserved.
#'
#' @param data Data frame containing source and mixture samples. Supports raw and averaged formats.
#' @param max_na_ratio Numeric. Maximum allowable proportion of missing ratio values per row (0 to 1). Default 0.30.
#' @param max_zero_content Numeric. Maximum allowable proportion of missing or zero content values per row (0 to 1). Default 0.30.
#'
#' @return A cleaned data frame with failing source rows removed and zeros in content columns converted to NAs.
#' @export
prefilter_tracers <- function(data, max_na_ratio = 0.30, max_zero_content = 0.30) {
  
  if (!is.data.frame(data)) {
    stop("'data' must be a data frame.")
  }
  
  col_names <- colnames(data)
  mix_row_idx <- nrow(data)
  
  if (mix_row_idx <= 1L) {
    stop("Data frame must contain at least one source row and one mixture row.")
  }
  
  # --- 1. Dynamically identify Ratio and Content columns ---
  c_cols <- grep("[Cc]onc|[Cc]ont", col_names, value = TRUE)
  r_cols <- setdiff(col_names[-c(1, 2)], c(c_cols, "n"))
  
  if (length(c_cols) == 0 || length(r_cols) == 0) {
    stop("Could not identify Ratio or Content columns to filter. Ensure content columns contain 'Conc' or 'Cont'.")
  }
  
  # --- 2. Convert absolute zeros in Content to NAs ---
  for (col in c_cols) {
    zero_mask <- !is.na(data[[col]]) & data[[col]] == 0
    if (any(zero_mask)) {
      data[zero_mask, col] <- NA
    }
  }
  
  # --- 3. Evaluate Sources ONLY (rows 1 to mix_row_idx - 1) ---
  source_idx <- seq_len(mix_row_idx - 1L)
  
  # Calculate % NAs in Ratios per row
  pct_na_ratios <- rowSums(is.na(data[source_idx, r_cols, drop = FALSE])) / length(r_cols)
  
  # Calculate % NAs in Content per row
  pct_na_content <- rowSums(is.na(data[source_idx, c_cols, drop = FALSE])) / length(c_cols)
  
  # --- 4. Identify which rows fail the thresholds ---
  bad_ratio_idx   <- source_idx[pct_na_ratios > max_na_ratio]
  bad_content_idx <- source_idx[pct_na_content > max_zero_content]
  
  rows_to_remove <- unique(c(bad_ratio_idx, bad_content_idx))
  sample_ids <- as.character(data[[1]])
  
  # --- 5. Print detailed messages and filter ---
  if (length(rows_to_remove) > 0) {
    
    if (length(rows_to_remove) == length(source_idx)) {
      stop("Filter threshold removed ALL source samples. Lower the threshold or inspect your dataset for excessive NAs.")
    }
    
    message(sprintf("\n--- FILTERING COMPLETE: Discarded %d source sample(s) out of %d ---",
                    length(rows_to_remove), length(source_idx)))
    
    if (length(bad_ratio_idx) > 0) {
      message(sprintf(" > Removed %d row(s) due to >%.0f%% NAs in Ratios.\n   Sample IDs: %s",
                      length(bad_ratio_idx),
                      max_na_ratio * 100,
                      paste(sample_ids[bad_ratio_idx], collapse = ", ")))
    }
    
    if (length(bad_content_idx) > 0) {
      message(sprintf(" > Removed %d row(s) due to >%.0f%% Zeros/NAs in Content.\n   Sample IDs: %s",
                      length(bad_content_idx),
                      max_zero_content * 100,
                      paste(sample_ids[bad_content_idx], collapse = ", ")))
    }
    
    data_clean <- data[-rows_to_remove, , drop = FALSE]
    
  } else {
    message("\n--- FILTERING COMPLETE: All source samples passed the thresholds. Zeros converted to NAs. ---")
    data_clean <- data
  }
  
  return(data_clean)
}

Try the OceanTracer package in your browser

Any scripts or data that you put into this service are public.

OceanTracer documentation built on Oct. 15, 2026, 5:09 p.m.