suppressPackageStartupMessages({ library(MALDIquant) library(MALDIquantForeign) library(ggplot2) library(ggrepel) library(dplyr) library(stringr) library(readr) }) ## ===================== Configuration ===================== # File paths base_path <- "/home/sierra/Documentos/Analisis de datos/Analisis de datos R" file_std <- file.path(base_path, "Fragmentos/Escopolamina_tuneo_fraq_20ev.mzML") file_smp <- file.path(base_path, "Fragmentos/LD_LTP_MS_cot_fraq_304.mzML") output_dir <- file.path(base_path, "Fragmentos/Resultados_MS2/Comparativos") output_file <- file.path(output_dir, "Mirror_Plot_Scopolamine_304_Academic.png") # Plotting parameters mz_range <- c(80, 320) label_threshold <- 0.05 topN_labels <- 8 dpi_png <- 300 bar_width_Da <- 0.2 # Preprocessing parameters halfWindowSize <- 5 snr_peaks <- 2.0 tolerance_Da <- 0.02 ## ===================== Utilities ===================== massSpectrum_to_df <- function(sp) data.frame(mz = mz(sp), intensity = intensity(sp)) # Refined centroid (intensity-weighted) refine_centroid <- function(df, m0, w) { sub <- df[df$mz >= (m0 - w) & df$mz <= (m0 + w), ] if (nrow(sub) == 0) return(m0) sum(sub$mz * sub$intensity) / sum(sub$intensity) } # Preprocessing pipeline for a single file (averaging scans if multiple exist) process_file <- function(f) { scans <- importMzMl(f) if (length(scans) > 1) { scans <- smoothIntensity(scans, method = "SavitzkyGolay", halfWindowSize = 7) scans <- removeBaseline(scans, method = "SNIP", iterations = 60) scans <- calibrateIntensity(scans, method = "TIC") # Basic alignment if multiple scans pks <- detectPeaks(scans, method = "MAD", halfWindowSize = halfWindowSize, SNR = 1.5) ref <- referencePeaks(pks, minFrequency = 0.5, tolerance = tolerance_Da) scans <- alignSpectra(scans, halfWindowSize = halfWindowSize, SNR = 1.5, tolerance = tolerance_Da, reference = ref) spec_avg <- averageMassSpectra(scans, method = "mean") } else { spec_avg <- scans[[1]] spec_avg <- smoothIntensity(spec_avg, method = "SavitzkyGolay", halfWindowSize = 7) spec_avg <- removeBaseline(spec_avg, method = "SNIP", iterations = 60) spec_avg <- calibrateIntensity(spec_avg, method = "TIC") } return(spec_avg) } # Label generation make_labels <- function(spec, df, rel_threshold = 0.05, topN = 5, w = tolerance_Da) { pk <- detectPeaks(spec, method = "MAD", halfWindowSize = halfWindowSize, SNR = snr_peaks) if (length(pk) == 0) return(NULL) lab_df <- data.frame(mz = mz(pk), intensity = intensity(pk)) lab_df$mz <- vapply(lab_df$mz, function(m) refine_centroid(df, m, w), numeric(1)) lab_df$rel_int <- lab_df$intensity / max(lab_df$intensity, na.rm = TRUE) # Select top N and those above threshold keep_idx <- unique(c(order(lab_df$rel_int, decreasing = TRUE)[seq_len(min(topN, nrow(lab_df)))], which(lab_df$rel_int >= rel_threshold))) lab_df <- lab_df[sort(keep_idx), ] lab_df$label <- sprintf("%.2f", lab_df$mz) return(lab_df) } ## ===================== Execution ===================== message("Processing Scopolamine spectra...") spec_std <- process_file(file_std) spec_smp <- process_file(file_smp) df_std <- massSpectrum_to_df(spec_std) %>% mutate(intensity = intensity / max(intensity)) df_smp <- massSpectrum_to_df(spec_smp) %>% mutate(intensity = intensity / max(intensity)) labels_std <- make_labels(spec_std, df_std, label_threshold, topN_labels) labels_smp <- make_labels(spec_smp, df_smp, label_threshold, topN_labels) # Prepare mirrored data df_smp$y <- df_smp$intensity df_std$y <- -df_std$intensity df_smp$Group <- "Sample (Leaf)" df_std$Group <- "Reference Standard" # Combine for plotting df_all <- bind_rows(df_smp, df_std) # Filter by range df_all <- df_all %>% filter(mz >= mz_range[1], mz <= mz_range[2]) message("Generating mirror plot...") p <- ggplot() + # Mirror spectra (lines) geom_segment(data = df_smp %>% filter(mz >= mz_range[1], mz <= mz_range[2]), aes(x = mz, xend = mz, y = 0, yend = y), color = "#1f77b4", linewidth = 0.3) + geom_segment(data = df_std %>% filter(mz >= mz_range[1], mz <= mz_range[2]), aes(x = mz, xend = mz, y = 0, yend = y), color = "#d62728", linewidth = 0.3) + geom_hline(yintercept = 0, color = "black", linewidth = 0.5) + # Labels geom_text_repel(data = labels_smp, aes(x = mz, y = rel_int, label = label), nudge_y = 0.05, size = 3.5, color = "#1f77b4", fontface = "bold", max.overlaps = 20, min.segment.length = 0) + geom_text_repel(data = labels_std, aes(x = mz, y = -rel_int, label = label), nudge_y = -0.05, size = 3.5, color = "#d62728", fontface = "bold", max.overlaps = 20, min.segment.length = 0) + # English Academic Annotations scale_y_continuous(labels = function(x) abs(x) * 100, limits = c(-1.15, 1.15), breaks = seq(-1, 1, 0.5)) + scale_x_continuous(limits = mz_range, breaks = seq(mz_range[1], mz_range[2], 20)) + labs(title = expression(bold("MS/MS Spectrum Comparison: Scopolamine (") * bolditalic("m/z") * bold(" 304.17)")), subtitle = "Upper: Scopolamine in Leaf (Sample) | Lower: Scopolamine Standard (Reference)\nCollision Energy: 20 eV", x = expression(italic(m/z)), y = "Relative Abundance (%)") + theme_classic(base_size = 14) + theme( plot.title = element_text(face = "bold", hjust = 0.5), plot.subtitle = element_text(hjust = 0.5, size = 11, lineheight = 1.2), axis.title = element_text(face = "bold"), axis.line = element_line(color = "black"), panel.grid.major.y = element_line(color = "grey90", linetype = "dotted") ) # Save plot dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) ggsave(output_file, p, width = 11, height = 7, dpi = dpi_png) message("✓ Mirror plot saved to: ", output_file)