using CairoMakie using Statistics using Colors function create_msi_parameter_plot() # Create the figure with subplots for each preprocessing step fig = Figure(size=(1600, 1200), fontsize=12) # Define the preprocessing steps and their parameters steps = [ ("BaselineCorrection", ["iterations: 10", "method: SNIP", "window: 3.0"]), ("Calibration", ["fit_order: 2", "method: internal_standards", "ppm_tolerance: 20.0"]), ("Normalization", ["method: rms"]), ("PeakAlignment", ["max_shift_ppm: 50.0", "method: linear", "tolerance: 20.0 ppm"]), ("PeakBinningParams", ["max_bin_width_ppm: 60.0", "method: adaptive", "min_peak_per_bin: 3"]), ("PeakPicking", ["half_window: 5", "method: centroid", "snr_threshold: 15.0"]), ("PeakSelection", ["max_fwhm_ppm: 95.39", "min_fwhm_ppm: 19.08", "min_snr: 3.0"]), ("Smoothing", ["method: savitzky_golay", "order: 3", "window: 5"]) ] # Create a grid of subplots g = fig[1, 1] = GridLayout() # Plot each preprocessing step for (idx, (step_name, params)) in enumerate(steps) row, col = fldmod1(idx, 2) ax = Axis(g[row, col], title=step_name, titlesize=14) # Generate simulated m/z values and intensities mz_range = range(100, 1000, length=500) if step_name == "BaselineCorrection" # Simulate spectrum with baseline true_signal = 0.5 .* exp.(-0.001 .* (mz_range .- 400).^2) .+ 0.3 .* exp.(-0.002 .* (mz_range .- 600).^2) baseline = 0.1 .+ 0.05 .* sin.(mz_range ./ 50) noisy_signal = true_signal .+ baseline .+ 0.02 .* randn(length(mz_range)) corrected = noisy_signal .- baseline lines!(ax, mz_range, noisy_signal, color=:blue, linewidth=2, label="Raw") lines!(ax, mz_range, baseline, color=:red, linewidth=2, linestyle=:dash, label="Baseline") lines!(ax, mz_range, corrected, color=:green, linewidth=2, label="Corrected") elseif step_name == "Calibration" # Simulate calibration shift reference_peaks = [200, 400, 600, 800] measured_peaks = reference_peaks .+ 2.0 .* randn(length(reference_peaks)) scatter!(ax, reference_peaks, fill(0.5, length(reference_peaks)), color=:red, markersize=15, label="Reference") scatter!(ax, measured_peaks, fill(0.3, length(measured_peaks)), color=:blue, markersize=10, label="Measured") # Add calibration lines for i in 1:length(reference_peaks) lines!(ax, [measured_peaks[i], reference_peaks[i]], [0.3, 0.5], color=:black, linewidth=1, linestyle=:dash) end elseif step_name == "Normalization" # Simulate normalization effect spectra = [ 0.8 .* exp.(-0.001 .* (mz_range .- 300).^2) .+ 0.2 .* randn(length(mz_range)), 1.2 .* exp.(-0.001 .* (mz_range .- 300).^2) .+ 0.2 .* randn(length(mz_range)), 0.9 .* exp.(-0.001 .* (mz_range .- 300).^2) .+ 0.2 .* randn(length(mz_range)) ] normalized_spectra = [spec ./ std(spec) for spec in spectra] for (i, spec) in enumerate(spectra) lines!(ax, mz_range, spec .+ i*0.3, color=RGBA(1, 0, 0, 0.6), linewidth=2, label=i==1 ? "Before Norm" : "") end for (i, spec) in enumerate(normalized_spectra) lines!(ax, mz_range, spec .+ i*0.3, color=RGBA(0, 0, 1, 0.6), linewidth=2, label=i==1 ? "After Norm" : "") end elseif step_name == "PeakAlignment" # Simulate peak alignment base_peaks = [300, 500, 700] shifts = [-15, 5, 10] for (i, shift) in enumerate(shifts) shifted_peaks = base_peaks .+ shift aligned_peaks = base_peaks scatter!(ax, shifted_peaks, fill(i, length(shifted_peaks)), color=:red, markersize=12, label=i==1 ? "Before Align" : "") scatter!(ax, aligned_peaks, fill(i+0.3, length(aligned_peaks)), color=:green, markersize=12, label=i==1 ? "After Align" : "") # Show alignment lines for j in 1:length(base_peaks) lines!(ax, [shifted_peaks[j], aligned_peaks[j]], [i, i+0.3], color=:black, linewidth=1, linestyle=:dash) end end elseif step_name == "PeakBinningParams" # Simulate peak binning with centroids raw_peaks_mz = 100:25:900 raw_peaks_intensity = rand(length(raw_peaks_mz)) # Create binned peaks (wider bins) bin_centers = 150:60:850 bin_intensities = [sum(raw_peaks_intensity[abs.(raw_peaks_mz .- center) .< 30]) for center in bin_centers] .* 0.8 # Profile mode (continuous) profile_signal = zeros(length(mz_range)) for (mz, int) in zip(raw_peaks_mz, raw_peaks_intensity) profile_signal .+= int .* exp.(-0.001 .* (mz_range .- mz).^2) end lines!(ax, mz_range, profile_signal, color=:blue, linewidth=2, label="Profile") barplot!(ax, bin_centers, bin_intensities, color=RGBA(1, 0, 0, 0.7), width=50, label="Binned Centroids") elseif step_name == "PeakPicking" # Simulate peak picking from profile to centroids profile_signal = 0.6 .* exp.(-0.0005 .* (mz_range .- 400).^2) .+ 0.4 .* exp.(-0.0008 .* (mz_range .- 650).^2) .+ 0.1 .* randn(length(mz_range)) # Simulate picked peaks (centroids) peak_positions = [380, 405, 640, 660] peak_intensities = [0.5, 0.6, 0.35, 0.4] lines!(ax, mz_range, profile_signal, color=:blue, linewidth=2, label="Profile Spectrum") scatter!(ax, peak_positions, peak_intensities, color=:red, markersize=20, label="Picked Centroids", strokewidth=2) elseif step_name == "PeakSelection" # Simulate peak selection based on criteria all_peaks_mz = 200:50:800 all_peaks_fwhm = rand(length(all_peaks_mz)) .* 100 .+ 10 all_peaks_snr = rand(length(all_peaks_mz)) .* 10 # Selection criteria selected = (all_peaks_fwhm .>= 19.08) .& (all_peaks_fwhm .<= 95.39) .& (all_peaks_snr .>= 3.0) scatter!(ax, all_peaks_mz[.!selected], all_peaks_fwhm[.!selected], color=:red, markersize=15, label="Rejected") scatter!(ax, all_peaks_mz[selected], all_peaks_fwhm[selected], color=:green, markersize=15, label="Selected") # Add selection criteria lines hlines!(ax, [19.08, 95.39], color=:black, linestyle=:dash, linewidth=2) text!(ax, 850, 50; text="FWHM bounds", color=:black, fontsize=10) elseif step_name == "Smoothing" # Simulate smoothing effect true_signal = 0.7 .* exp.(-0.001 .* (mz_range .- 450).^2) .+ 0.5 .* exp.(-0.0008 .* (mz_range .- 650).^2) noisy_signal = true_signal .+ 0.1 .* randn(length(mz_range)) # Simple smoothing simulation smoothed = similar(noisy_signal) window = 5 for i in 1:length(noisy_signal) start_idx = max(1, i - window ÷ 2) end_idx = min(length(noisy_signal), i + window ÷ 2) smoothed[i] = mean(noisy_signal[start_idx:end_idx]) end lines!(ax, mz_range, noisy_signal, color=:red, linewidth=1, label="Noisy") lines!(ax, mz_range, smoothed, color=:blue, linewidth=2, label="Smoothed") lines!(ax, mz_range, true_signal, color=:green, linewidth=1, linestyle=:dash, label="True") end # Add parameter text using a more reliable approach param_text = join(params, "\n") text!(ax, param_text, position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), color=:black, fontsize=10, font=:regular) # Add legend for selected plots if idx <= 4 axislegend(ax, position=:rt, framevisible=true, backgroundcolor=RGBA(1,1,1,0.8)) end # Customize axes ax.xlabel = "m/z" ax.ylabel = idx in [1,3,5,7] ? "Intensity" : "" ax.xgridvisible = false ax.ygridvisible = false end # Add overall title Label(fig[0, :], "MSI Preprocessing Pipeline Parameters and Simulations", fontsize=18, font=:bold, padding=(0, 0, 10, 0)) # Add explanation explanation = """ Simulation of MSI preprocessing parameters showing: • Blue lines: Profile/continuous spectra • Red bars/points: Centroid data • Dashed lines: Reference/true signals • Green: Processed/corrected data • Each subplot demonstrates key parameters for the preprocessing step """ Label(fig[2, :], explanation, fontsize=12, tellwidth=false, padding=(10, 10, 10, 10)) # Adjust layout colgap!(g, 20) rowgap!(g, 20) fig end # Create and display the plot fig = create_msi_parameter_plot() # Save the plot save("msi_preprocessing_parameters.png", fig) println("Plot saved as 'msi_preprocessing_parameters.png'") # Display the plot (if in an interactive environment) fig