324 lines
13 KiB
Julia
324 lines
13 KiB
Julia
using CairoMakie
|
|
using Statistics
|
|
using Colors
|
|
|
|
"""
|
|
Plots the effect of baseline correction, showing how different numbers of SNIP
|
|
iterations affect the estimated baseline.
|
|
"""
|
|
function plot_baseline_correction_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Baseline Correction (SNIP)", xlabel="m/z", ylabel="Intensity")
|
|
|
|
mz_range = range(200, 1000, length=1500)
|
|
true_signal = 1.2 .* exp.(-0.001 .* (mz_range .- 450).^2) .+ 0.8 .* exp.(-0.002 .* (mz_range .- 750).^2)
|
|
baseline_comp = 0.15 .+ 0.1 .* sin.(mz_range ./ 60) .+ 0.0001 .* mz_range
|
|
noise = 0.04 .* randn(length(mz_range))
|
|
raw_signal = true_signal .+ baseline_comp .+ noise
|
|
|
|
# Simplified SNIP simulation for visualization
|
|
function simple_snip(y, iterations)
|
|
b = copy(y)
|
|
for _ in 1:iterations
|
|
for i in 2:length(b)-1
|
|
b[i] = min(b[i], 0.5 * (b[i-1] + b[i+1]))
|
|
end
|
|
end
|
|
return b
|
|
end
|
|
|
|
baseline_iter_20 = simple_snip(raw_signal, 20)
|
|
baseline_iter_200 = simple_snip(raw_signal, 200)
|
|
corrected_signal = raw_signal .- baseline_iter_200
|
|
|
|
lines!(ax, mz_range, raw_signal, color=(:grey, 0.6), label="Raw Signal")
|
|
lines!(ax, mz_range, corrected_signal, color=:green, linewidth=2.5, label="Corrected Signal")
|
|
l1 = lines!(ax, mz_range, baseline_iter_20, color=(:red, 0.7), linestyle=:dash, linewidth=2, label="Baseline (iterations: 20)")
|
|
l2 = lines!(ax, mz_range, baseline_iter_200, color=:red, linewidth=2.5, label="Baseline (iterations: 200)")
|
|
|
|
text!(ax, "Parameter: `iterations`\nMore iterations result in a more aggressive baseline that follows the signal floor more closely.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12)
|
|
axislegend(ax, position=:rt)
|
|
save("descriptive_plot_baseline_correction.png", fig)
|
|
println("Saved: descriptive_plot_baseline_correction.png")
|
|
end
|
|
|
|
"""
|
|
Plots the effect of smoothing, comparing different window sizes.
|
|
"""
|
|
function plot_smoothing_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Smoothing (Savitzky-Golay)", xlabel="m/z", ylabel="Intensity")
|
|
|
|
mz_range = range(400, 700, length=1000)
|
|
true_signal = 0.8 .* exp.(-0.002 .* (mz_range .- 500).^2) .+ 0.6 .* exp.(-0.001 .* (mz_range .- 600).^2)
|
|
noisy_signal = true_signal .+ 0.1 .* randn(length(mz_range))
|
|
|
|
function simple_moving_average(y, window)
|
|
smoothed = similar(y)
|
|
for i in 1:length(y)
|
|
start_idx = max(1, i - window ÷ 2)
|
|
end_idx = min(length(y), i + window ÷ 2)
|
|
smoothed[i] = mean(y[start_idx:end_idx])
|
|
end
|
|
return smoothed
|
|
end
|
|
|
|
smoothed_small_window = simple_moving_average(noisy_signal, 5)
|
|
smoothed_large_window = simple_moving_average(noisy_signal, 21)
|
|
|
|
lines!(ax, mz_range, noisy_signal, color=(:red, 0.4), label="Noisy Signal")
|
|
lines!(ax, mz_range, true_signal, color=:black, linestyle=:dash, linewidth=2, label="True Signal")
|
|
lines!(ax, mz_range, smoothed_small_window, color=:blue, linewidth=2, label="Smoothed (window: 5)")
|
|
lines!(ax, mz_range, smoothed_large_window, color=:purple, linewidth=2, label="Smoothed (window: 21)")
|
|
|
|
text!(ax, "Parameter: `window`\nA larger window increases smoothing but may broaden peaks.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12)
|
|
axislegend(ax, position=:rt)
|
|
save("descriptive_plot_smoothing.png", fig)
|
|
println("Saved: descriptive_plot_smoothing.png")
|
|
end
|
|
|
|
"""
|
|
Visualizes the peak picking process for profile-mode data, illustrating the
|
|
effects of SNR threshold and peak prominence.
|
|
"""
|
|
function plot_peak_picking_profile_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Peak Picking (Profile Mode)", xlabel="m/z", ylabel="Intensity")
|
|
|
|
mz = 1:200
|
|
base_signal = 10 .* exp.(-((mz .- 50).^2) ./ (2*3^2)) .+ 7 .* exp.(-((mz .- 120).^2) ./ (2*5^2)) .+ 2
|
|
small_peak_signal = zeros(200)
|
|
for i in 80:90
|
|
small_peak_signal[i] = 3 * exp(-((i - 85)^2) / 2.0)
|
|
end
|
|
noise = 0.5 .* randn(200)
|
|
intensity = base_signal .+ small_peak_signal .+ noise
|
|
|
|
noise_level = median(abs.(intensity .- median(intensity))) * 1.4826 # MAD
|
|
snr_threshold_val = 3.0
|
|
intensity_threshold = noise_level * snr_threshold_val
|
|
|
|
picked_peaks_mz = [50, 85, 120]
|
|
picked_peaks_intensity = intensity[picked_peaks_mz]
|
|
|
|
hlines!(ax, [noise_level], color=:gray, linestyle=:dot, label="Est. Noise Level")
|
|
hlines!(ax, [intensity_threshold], color=:orange, linestyle=:dash, label="SNR Threshold (snr_threshold = 3.0)")
|
|
lines!(ax, mz, intensity, color=:blue, label="Profile Spectrum")
|
|
scatter!(ax, picked_peaks_mz, picked_peaks_intensity, color=:green, markersize=15, strokewidth=2, label="Peaks passing SNR")
|
|
scatter!(ax, [25], [intensity[25]], color=:red, marker=:x, markersize=15, label="Local max below SNR")
|
|
|
|
# Illustrate prominence
|
|
arrows!(ax, [120, 120], [intensity[135], intensity[120]], [0, 0], [intensity[120]-intensity[135], 0], color=:purple)
|
|
text!(ax, 125, (intensity[120]+intensity[135])/2, text="Prominence", color=:purple)
|
|
|
|
axislegend(ax, position=:rt)
|
|
save("descriptive_plot_peakpicking_profile.png", fig)
|
|
println("Saved: descriptive_plot_peakpicking_profile.png")
|
|
end
|
|
|
|
"""
|
|
Visualizes peak picking (filtering) for centroid-mode data based on an SNR
|
|
(intensity) threshold.
|
|
"""
|
|
function plot_peak_picking_centroid_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Peak Picking (Centroid Mode)", xlabel="m/z", ylabel="Intensity")
|
|
|
|
mz = [100, 150, 200, 250, 300, 350, 400]
|
|
intensity = [10, 5, 25, 8, 3, 18, 12]
|
|
snr_threshold_val = 10.0
|
|
|
|
stem!(ax, mz, intensity, color=:gray, label="Input Centroids")
|
|
|
|
selected_mask = intensity .>= snr_threshold_val
|
|
stem!(ax, mz[selected_mask], intensity[selected_mask], color=:green, trunkwidth=3, label="Selected Peaks (intensity >= 10)")
|
|
stem!(ax, mz[.!selected_mask], intensity[.!selected_mask], color=:red, trunkwidth=3, label="Rejected Peaks (intensity < 10)")
|
|
|
|
hlines!(ax, [snr_threshold_val], color=:orange, linestyle=:dash, label="Intensity Threshold (snr_threshold)")
|
|
|
|
text!(ax, "Parameter: `snr_threshold`\nIn centroid mode, this acts as a direct intensity filter.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12)
|
|
axislegend(ax, position=:rt)
|
|
save("descriptive_plot_peakpicking_centroid.png", fig)
|
|
println("Saved: descriptive_plot_peakpicking_centroid.png")
|
|
end
|
|
|
|
"""
|
|
Plots the effect of different normalization methods on a set of spectra.
|
|
"""
|
|
function plot_normalization_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
|
|
mz = range(300, 400, length=500)
|
|
spec1 = 1.5 .* exp.(-((mz .- 350).^2) ./ 50)
|
|
spec2 = 0.8 .* exp.(-((mz .- 350).^2) ./ 50)
|
|
|
|
ax1 = Axis(fig[1, 1], title="Before Normalization", ylabel="Absolute Intensity")
|
|
lines!(ax1, mz, spec1, label="Spectrum A (High TIC)")
|
|
lines!(ax1, mz, spec2, label="Spectrum B (Low TIC)")
|
|
axislegend(ax1)
|
|
|
|
ax2 = Axis(fig[1, 2], title="After Normalization (TIC)", ylabel="Relative Intensity")
|
|
lines!(ax2, mz, spec1 ./ sum(spec1), label="Spectrum A (Normalized)")
|
|
lines!(ax2, mz, spec2 ./ sum(spec2), label="Spectrum B (Normalized)")
|
|
|
|
text!(ax2, "Effect: Spectra are scaled to have the same total area, making their intensities comparable.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12, justification=:left)
|
|
|
|
save("descriptive_plot_normalization.png", fig)
|
|
println("Saved: descriptive_plot_normalization.png")
|
|
end
|
|
|
|
"""
|
|
Illustrates mass calibration, showing how a calibration curve corrects measured
|
|
m/z values based on reference peaks.
|
|
"""
|
|
function plot_calibration_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Calibration", xlabel="Measured m/z", ylabel="m/z Error (Measured - Reference)")
|
|
|
|
ref_mz = [200, 400, 600, 800, 1000]
|
|
measured_mz = ref_mz .+ [0.1, 0.15, 0.2, 0.25, 0.3] .+ 0.02 .* randn(5)
|
|
errors = measured_mz .- ref_mz
|
|
|
|
# Fit a linear model to the error
|
|
A = [ones(5) measured_mz]
|
|
coeffs = A \ errors
|
|
correction_func(m) = m - (coeffs[1] .+ coeffs[2] .* m)
|
|
|
|
fit_line = coeffs[1] .+ coeffs[2] .* measured_mz
|
|
|
|
scatter!(ax, measured_mz, errors, color=:red, markersize=15, label="Measured Error")
|
|
lines!(ax, measured_mz, fit_line, color=:blue, label="Calibration Curve (fit_order=1)")
|
|
|
|
text!(ax, "Parameter: `fit_order`\nA curve is fit to the error of known reference peaks.\nThis curve is then used to correct all m/z values.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12)
|
|
axislegend(ax, position=:rb)
|
|
save("descriptive_plot_calibration.png", fig)
|
|
println("Saved: descriptive_plot_calibration.png")
|
|
end
|
|
|
|
"""
|
|
Visualizes peak alignment by showing multiple spectra with misaligned peaks
|
|
before and after the alignment process.
|
|
"""
|
|
function plot_alignment_details()
|
|
fig = Figure(size=(1600, 600), fontsize=14)
|
|
ax1 = Axis(fig[1, 1], title="Before Alignment", xlabel="m/z", yticklabelsvisible=false, ygridvisible=false)
|
|
ax2 = Axis(fig[1, 2], title="After Alignment", xlabel="m/z", yticklabelsvisible=false, ygridvisible=false)
|
|
|
|
mz = range(490, 510, length=1000)
|
|
shifts = [-0.5, 0.0, 0.8]
|
|
colors = [:blue, :green, :purple]
|
|
|
|
for (i, shift) in enumerate(shifts)
|
|
peak_center = 500 + shift
|
|
spectrum = exp.(-((mz .- peak_center).^2) ./ 0.1)
|
|
lines!(ax1, mz, spectrum .+ i, color=colors[i])
|
|
vlines!(ax1, [peak_center], color=(colors[i], 0.5), linestyle=:dash)
|
|
|
|
# After alignment, all peaks are at 500
|
|
aligned_spectrum = exp.(-((mz .- 500).^2) ./ 0.1)
|
|
lines!(ax2, mz, aligned_spectrum .+ i, color=colors[i])
|
|
end
|
|
vlines!(ax2, [500], color=:red, linestyle=:dash, label="Reference m/z")
|
|
axislegend(ax2)
|
|
|
|
save("descriptive_plot_alignment.png", fig)
|
|
println("Saved: descriptive_plot_alignment.png")
|
|
end
|
|
|
|
"""
|
|
Illustrates peak selection by filtering a population of peaks based on
|
|
FWHM and SNR criteria.
|
|
"""
|
|
function plot_peak_selection_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Peak Selection", xlabel="FWHM (ppm)", ylabel="Signal-to-Noise Ratio (SNR)")
|
|
|
|
n_peaks = 100
|
|
fwhm = rand(n_peaks) .* 150
|
|
snr = rand(n_peaks) .* 20
|
|
|
|
min_fwhm_ppm = 20.0
|
|
max_fwhm_ppm = 100.0
|
|
min_snr = 5.0
|
|
|
|
selected_mask = (fwhm .>= min_fwhm_ppm) .& (fwhm .<= max_fwhm_ppm) .& (snr .>= min_snr)
|
|
|
|
scatter!(ax, fwhm[.!selected_mask], snr[.!selected_mask], color=(:red, 0.5), label="Rejected Peaks")
|
|
scatter!(ax, fwhm[selected_mask], snr[selected_mask], color=:green, label="Selected Peaks")
|
|
|
|
vlines!(ax, [min_fwhm_ppm, max_fwhm_ppm], color=:blue, linestyle=:dash, label="FWHM bounds")
|
|
hlines!(ax, [min_snr], color=:orange, linestyle=:dash, label="SNR bound")
|
|
|
|
poly!(ax, BBox(min_fwhm_ppm, max_fwhm_ppm, min_snr, 22), color=(:green, 0.1))
|
|
text!(ax, "Selection Region", position=(60, 12), color=:green, fontsize=14)
|
|
|
|
axislegend(ax)
|
|
save("descriptive_plot_peak_selection.png", fig)
|
|
println("Saved: descriptive_plot_peak_selection.png")
|
|
end
|
|
|
|
"""
|
|
Visualizes the adaptive peak binning process, showing how peaks from different
|
|
spectra are grouped into a common bin based on a PPM tolerance.
|
|
"""
|
|
function plot_peak_binning_details()
|
|
fig = Figure(size=(1200, 700), fontsize=14)
|
|
ax = Axis(fig[1, 1], title="Detailed View: Adaptive Peak Binning", xlabel="m/z", yticklabelsvisible=false)
|
|
|
|
ref_mz = 500.0
|
|
tolerance_ppm = 50.0
|
|
tol_mz = ref_mz * tolerance_ppm / 1e6
|
|
|
|
bin_start = ref_mz - tol_mz/2
|
|
bin_end = ref_mz + tol_mz/2
|
|
|
|
peaks_mz = [ref_mz - 0.01, ref_mz + 0.005, ref_mz + 0.02, ref_mz - 0.015]
|
|
peak_intensities = [0.8, 1.0, 0.9, 0.7]
|
|
peak_colors = [:blue, :green, :purple, :orange]
|
|
|
|
vspan!(ax, bin_start, bin_end, color=(:gray, 0.2), label="Bin (tolerance: 50 ppm)")
|
|
stem!(ax, peaks_mz, peak_intensities, color=peak_colors, markersize=15)
|
|
|
|
# Show bin center
|
|
bin_center = mean(peaks_mz)
|
|
vlines!(ax, [bin_center], color=:red, linestyle=:dash, label="Calculated Bin Center")
|
|
|
|
text!(ax, "Parameter: `tolerance`\nPeaks from different spectra within the tolerance window are grouped into a single feature.",
|
|
position=Point2f(0.05, 0.95), space=:relative, align=(:left, :top), fontsize=12)
|
|
axislegend(ax, position=:rt)
|
|
xlims!(ax, ref_mz - tol_mz*2, ref_mz + tol_mz*2)
|
|
save("descriptive_plot_peak_binning.png", fig)
|
|
println("Saved: descriptive_plot_peak_binning.png")
|
|
end
|
|
|
|
|
|
"""
|
|
Main function to generate and save all descriptive plots.
|
|
"""
|
|
function create_and_save_all_plots()
|
|
println("Generating detailed descriptive plots for preprocessing steps...")
|
|
|
|
plot_baseline_correction_details()
|
|
plot_smoothing_details()
|
|
plot_peak_picking_profile_details()
|
|
plot_peak_picking_centroid_details()
|
|
plot_normalization_details()
|
|
plot_calibration_details()
|
|
plot_alignment_details()
|
|
plot_peak_selection_details()
|
|
plot_peak_binning_details()
|
|
|
|
println("\nAll descriptive plots have been saved in the current directory.")
|
|
end
|
|
|
|
# Execute the plot generation
|
|
if abspath(PROGRAM_FILE) == @__FILE__
|
|
create_and_save_all_plots()
|
|
end
|
|
|