A common task in FTIR spectroscopy is identifying and quantifying functional groups within a complex mixture. Biodiesel-diesel blends provide an excellent example: the presence and concentration of biodiesel introduces characteristic carbonyl (C=O), ester (C-O-C), and alkene (=C-H) absorption bands that are absent in pure diesel.
This vignette walks through a complete peak fitting workflow — from
detecting overlapping peaks to evaluating fit quality — using the
built-in biodiesel dataset. The goal is not to catalogue
individual functions, but to demonstrate a practical analysis pipeline
that you can adapt to your own samples.
The biodiesel dataset contains ATR-FTIR spectra of
diesel samples with increasing biodiesel content (0% to 10% plus
commercial blends). Each spectrum was collected across the standard
mid-IR range, and the fingerprint region (approximately 1800–1000 cm⁻¹)
contains the most diagnostic features for distinguishing biodiesel from
conventional diesel.
data(biodiesel)
head(biodiesel)
#> PlotFTIR data:
#> Spectral range: 700.7395 - 710.0579 cm⁻¹
#> Resolution: variable
#> Intensity type: absorbance
#> Number of samples: 1
#> Sample IDs: biodiesel_0The data is in the long-format structure: wavenumber,
absorbance, and sample_id columns with 11
distinct samples. The biodiesel_0 sample (pure diesel)
serves as our baseline for comparison.
Before attempting peak fitting, it is important to understand the raw spectral landscape. The fingerprint region of the pure diesel sample already shows several discernible features:
sample_spectrum <- biodiesel[biodiesel$sample_id == "biodiesel_0", ]
plot_ftir(
sample_spectrum,
plot_title = c("Pure Diesel Spectrum", "Fingerprint region (1800–1000 cm⁻¹)"),
legend_title = "Sample"
) |>
zoom_in_on_range(c(1800, 1000))FTIR Spectra of pure diesel, from 1800-1000 cm-1.
Even this pure diesel sample reveals multiple overlapping bands. As biodiesel content increases, additional peaks emerge — particularly a carbonyl stretch near 1740 cm⁻¹ that is absent in pure diesel. This is the feature we will isolate and quantify.
The first step in any peak fitting workflow is identifying candidate
peak locations. Rather than manually inspecting the spectrum,
find_ftir_peaks() combines several mathematical techniques
to detect peaks automatically.
The algorithm applies Savitzky-Golay smoothing to reduce noise, then identifies peaks through three complementary approaches:
Peaks detected by different methods within a configurable window are merged into single representative locations.
subset_spectrum <- sample_spectrum[
sample_spectrum$wavenumber < 1800 & sample_spectrum$wavenumber > 1000,
]
detected_peaks <- find_ftir_peaks(subset_spectrum)
detected_peaks
#> [1] 1034.336 1064.155 1153.611 1159.202 1164.793 1215.112 1304.568 1339.978
#> [9] 1358.615 1375.388 1408.934 1457.389 1503.981 1606.483 1697.802 1742.530The algorithm detected 16 peaks across the fingerprint region. These locations will serve as initial estimates for the fitting procedure. Note that the peak detection is performed on smoothed data, so the exact wavenumbers may differ slightly from visual inspection of the raw spectrum — this is expected and desirable, as it reduces the influence of noise on peak positioning.
This automatic detection step is heuristic rather than definitive. It
is useful for proposing starting peak centers, but it should not be read
as a validated deconvolution on its own. In crowded regions, shoulder
peaks, or broad overlapping bands, it is often worth tuning the
detection parameters or supplying peaklist directly.
With peak locations identified, the next step is fitting each peak
with a mathematical function. The fit_peaks() function
supports four peak shapes, each appropriate for different spectral
characteristics. The choice of model affects both the accuracy of peak
parameters and the interpretability of the results.
The Voigt profile is a convolution of Gaussian and Lorentzian functions. It is generally the most appropriate model for FTIR spectra because it accounts for both instrumental broadening (Gaussian component) and natural line width effects (Lorentzian component). The pseudo-Voigt approximation used here is computationally efficient while maintaining accuracy.
fitted_voigt <- fit_peaks(subset_spectrum, method = "voigt")
fit_peak_df(fitted_voigt)
#> sample_id peak wavenumber sigma eta amplitude mix_ratio
#> 1 biodiesel_0 1 1020.378 22.620410 0.41667423 0.70607177 0.041334784
#> 2 biodiesel_0 2 1073.967 21.214391 0.46904772 0.61732677 0.036139483
#> 3 biodiesel_0 3 1142.874 26.438359 0.57022452 0.41963039 0.024565961
#> 4 biodiesel_0 4 1154.669 28.496689 0.78763583 0.38042812 0.022270986
#> 5 biodiesel_0 5 1165.108 30.535235 0.90849034 0.37788038 0.022121836
#> 6 biodiesel_0 6 1235.631 26.689703 0.34232764 0.58102456 0.034014282
#> 7 biodiesel_0 7 1295.061 22.373832 0.32421289 0.93511503 0.054743412
#> 8 biodiesel_0 8 1339.359 14.374979 0.86994255 0.79205797 0.046368580
#> 9 biodiesel_0 9 1366.080 11.455864 0.64757172 1.03248712 0.060443759
#> 10 biodiesel_0 10 1377.426 5.654138 0.60234771 1.86168373 0.108986506
#> 11 biodiesel_0 11 1430.670 18.132001 0.38013072 1.25041274 0.073201540
#> 12 biodiesel_0 12 1456.775 11.032503 0.08075851 6.45034640 0.377615544
#> 13 biodiesel_0 13 1489.794 20.522162 0.38137758 1.19693275 0.070070719
#> 14 biodiesel_0 14 1600.188 15.319595 0.65711181 0.38245647 0.022389729
#> 15 biodiesel_0 15 1600.890 137.807667 0.52843896 0.05348895 0.003131345
#> 16 biodiesel_0 16 1644.661 161.435246 0.70224043 0.04443884 0.002601534
#> peak_shape
#> 1 voigt
#> 2 voigt
#> 3 voigt
#> 4 voigt
#> 5 voigt
#> 6 voigt
#> 7 voigt
#> 8 voigt
#> 9 voigt
#> 10 voigt
#> 11 voigt
#> 12 voigt
#> 13 voigt
#> 14 voigt
#> 15 voigt
#> 16 voigtThe output shows the fitted parameters for each peak: wavenumber
location (wavenumber), width (sigma), shape
parameter (eta), and relative contribution
(mix_ratio). These fitted components are model-based
summaries of the observed spectrum rather than direct proofs that each
component corresponds to a unique physical band. In practice, especially
where bands overlap strongly, component interpretation should be checked
against residuals, chemical context, and any prior expectations about
plausible peak centers.
Gaussian functions are appropriate when instrumental broadening dominates the line shape, or when computational speed is a priority. They are simpler than Voigt profiles and work well for narrow, symmetric peaks with minimal overlap.
fitted_gauss <- fit_peaks(subset_spectrum, method = "gauss")
fit_peak_df(fitted_gauss)
#> sample_id peak wavenumber sigma amplitude mix_ratio peak_shape
#> 1 biodiesel_0 1 1025.538 15.636774 0.68023213 0.039822083 gauss
#> 2 biodiesel_0 2 1076.358 20.887670 0.72153205 0.042239858 gauss
#> 3 biodiesel_0 3 1133.338 19.869280 0.43510387 0.025471808 gauss
#> 4 biodiesel_0 4 1157.307 19.495302 0.23573096 0.013800138 gauss
#> 5 biodiesel_0 5 1172.412 15.262803 0.27931221 0.016351468 gauss
#> 6 biodiesel_0 6 1230.188 25.298231 0.71915802 0.042100878 gauss
#> 7 biodiesel_0 7 1294.259 21.939430 1.09007351 0.063814976 gauss
#> 8 biodiesel_0 8 1337.670 12.037717 0.64418390 0.037711750 gauss
#> 9 biodiesel_0 9 1363.620 11.191571 0.85552757 0.050084211 gauss
#> 10 biodiesel_0 10 1377.416 7.213309 1.83162791 0.107226981 gauss
#> 11 biodiesel_0 11 1418.432 16.741745 0.96820860 0.056680773 gauss
#> 12 biodiesel_0 12 1456.482 12.108292 7.06855671 0.413806751 gauss
#> 13 biodiesel_0 13 1497.974 20.799804 0.99740888 0.058390213 gauss
#> 14 biodiesel_0 14 1594.631 24.585315 0.47094531 0.027570034 gauss
#> 15 biodiesel_0 15 1682.710 23.211156 0.03869402 0.002265221 gauss
#> 16 biodiesel_0 16 1745.083 20.140979 0.04548636 0.002662858 gaussThe Gaussian fit produced similar peak locations to the Voigt model but with narrower widths. This is expected: without the Lorentzian long-tail component, Gaussian peaks are more confined and may underestimate the extent of peak shoulders.
Lorentzian functions describe natural line width effects and are appropriate when the broadening is dominated by the natural lifetime of molecular transitions. They produce the characteristic long tails that make them useful for analyzing heavily overlapped peaks.
fitted_lorentz <- fit_peaks(subset_spectrum, method = "lorentz")
fit_peak_df(fitted_lorentz)
#> sample_id peak wavenumber gam amplitude mix_ratio peak_shape
#> 1 biodiesel_0 1 1019.813 27.305106 0.76201148 0.044609601 lorentz
#> 2 biodiesel_0 2 1072.054 25.860579 0.62509666 0.036594347 lorentz
#> 3 biodiesel_0 3 1143.668 32.642238 0.38336745 0.022443059 lorentz
#> 4 biodiesel_0 4 1155.553 33.247654 0.36218858 0.021203208 lorentz
#> 5 biodiesel_0 5 1165.318 29.786254 0.35992087 0.021070452 lorentz
#> 6 biodiesel_0 6 1243.707 28.454749 0.57080798 0.033416185 lorentz
#> 7 biodiesel_0 7 1295.611 22.808534 0.93208297 0.054565910 lorentz
#> 8 biodiesel_0 8 1339.394 17.307770 0.77883797 0.045594656 lorentz
#> 9 biodiesel_0 9 1367.295 9.505631 1.00739772 0.058974978 lorentz
#> 10 biodiesel_0 10 1377.667 4.842034 1.76221480 0.103163406 lorentz
#> 11 biodiesel_0 11 1443.020 8.024790 1.45993824 0.085467561 lorentz
#> 12 biodiesel_0 12 1458.260 9.278236 6.71774281 0.393269438 lorentz
#> 15 biodiesel_0 15 1464.907 144.494597 0.03117201 0.001824868 lorentz
#> 16 biodiesel_0 16 1467.340 182.514359 0.02227289 0.001303897 lorentz
#> 13 biodiesel_0 13 1468.957 14.692719 1.04711719 0.061300232 lorentz
#> 14 biodiesel_0 14 1604.856 8.946642 0.25961239 0.015198203 lorentzThe Lorentzian fit yielded broader sigma values than the Gaussian model, reflecting the longer tails. For FTIR analysis, pure Lorentzian profiles are less commonly used than Voigt because real spectra rarely exhibit purely Lorentzian line shapes — they typically show a mix of both broadening mechanisms.
The Doniach-Sunjić (DSG) function extends the Voigt profile with an
additional asymmetry parameter (alpha), making it
particularly useful for asymmetric peaks that arise from overlapping
transitions or instrumental effects. This is especially relevant for
FTIR spectra where bands often exhibit shoulders or asymmetry due to
Fermi resonance or overlapping functional groups.
fitted_dsg <- fit_peaks(subset_spectrum, method = "dsg")
fit_peak_df(fitted_dsg)
#> sample_id peak wavenumber sigma eta alpha amplitude
#> 1 biodiesel_0 1 1021.729 21.40673 0.2300115 1.040588e-01 0.68752459
#> 2 biodiesel_0 2 1073.426 20.95560 0.2654450 4.178487e-02 0.60471732
#> 3 biodiesel_0 3 1143.217 26.81239 0.4639632 1.536343e-06 0.41035249
#> 4 biodiesel_0 4 1154.571 28.32741 0.7240691 1.294036e-02 0.38246833
#> 5 biodiesel_0 5 1164.695 27.24772 0.9999984 3.076093e-02 0.38019900
#> 6 biodiesel_0 6 1236.692 26.40841 0.2236552 4.246228e-03 0.55029214
#> 7 biodiesel_0 7 1295.369 21.87505 0.2505226 1.536343e-06 0.90168888
#> 8 biodiesel_0 8 1340.207 15.76347 0.9166489 7.143756e-03 0.83265056
#> 9 biodiesel_0 9 1367.304 10.97263 0.5547417 6.767811e-03 1.08659688
#> 10 biodiesel_0 10 1377.527 5.08541 0.5266138 3.546381e-03 1.85128098
#> 11 biodiesel_0 11 1435.739 14.44531 0.2761619 2.297387e-02 1.43496041
#> 12 biodiesel_0 12 1457.101 10.32301 0.0713265 1.536343e-06 6.23193387
#> 13 biodiesel_0 13 1484.830 20.48238 0.1760790 1.536343e-06 1.24997682
#> 15 biodiesel_0 15 1577.526 135.43847 0.2820403 1.860076e-02 0.05411126
#> 16 biodiesel_0 16 1599.758 168.95308 0.7073266 2.394076e-03 0.04423006
#> 14 biodiesel_0 14 1602.472 12.82152 0.2434255 7.932406e-02 0.37879840
#> mix_ratio peak_shape
#> 1 0.040248997 doniach-sunjic-gauss
#> 2 0.035401302 doniach-sunjic-gauss
#> 3 0.024022815 doniach-sunjic-gauss
#> 4 0.022390423 doniach-sunjic-gauss
#> 5 0.022257573 doniach-sunjic-gauss
#> 6 0.032215148 doniach-sunjic-gauss
#> 7 0.052786582 doniach-sunjic-gauss
#> 8 0.048744947 doniach-sunjic-gauss
#> 9 0.063611448 doniach-sunjic-gauss
#> 10 0.108377509 doniach-sunjic-gauss
#> 11 0.084005311 doniach-sunjic-gauss
#> 12 0.364829259 doniach-sunjic-gauss
#> 13 0.073176020 doniach-sunjic-gauss
#> 15 0.003167776 doniach-sunjic-gauss
#> 16 0.002589312 doniach-sunjic-gauss
#> 14 0.022175578 doniach-sunjic-gaussThe DSG fit introduces the alpha parameter, which
controls the degree of asymmetry. When alpha is near zero,
the DSG profile reduces to a symmetric Voigt. Non-zero values indicate
asymmetric broadening — a common feature in complex mixtures like
diesel-biodiesel blends.
To understand how each model represents the data, we can overlay the fitted spectra on the original:
# Generate fitted spectra for each model
voigt_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_voigt)
gauss_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_gauss)
lorentz_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_lorentz)
# Build comparison data frame
comparison_data <- data.frame(
wavenumber = rep(subset_spectrum$wavenumber, 4),
absorbance = c(subset_spectrum$absorbance, voigt_fit, gauss_fit, lorentz_fit),
type = rep(c("Original", "Voigt", "Gaussian", "Lorentz"),
each = nrow(subset_spectrum))
)
# Plot comparison
ggplot(comparison_data, aes(x = wavenumber, y = absorbance, color = type)) +
geom_line(linewidth = 0.8) +
facet_wrap(~type, ncol = 1, scales = "free_y") +
labs(
title = "Comparison of Peak Fitting Models",
x = "Wavenumber (cm⁻¹)",
y = "Absorbance"
) +
theme_minimal()Plots of each type of peak fit, comparing the results against the original spectrum.
In this example, the Voigt and Lorentzian fits track the observed spectrum more closely than the Gaussian fit, with the Voigt model offering a reasonable balance of flexibility and computational efficiency. The Gaussian fit is simpler, but its symmetric, rapidly decaying shape can miss shoulders or broader tails. These comparisons are data-dependent rather than universal; for a new spectrum, residual patterns and subject-matter expectations matter more than any single default recommendation.
Once a fit is complete, it is useful to examine the individual peak
components that comprise the overall model. The
plot_components() function decomposes the fitted spectrum
into its constituent peaks, revealing which functional groups contribute
to each region of the spectrum.
plot_components(
subset_spectrum,
fitted_voigt,
plot_fit = TRUE,
plot_title = c("Voigt Peak Components", "Decomposition of the fingerprint region")
)Plotted components of a pseudo-voigt peak-shape fitted diesel.
Each colored line represents a single fitted peak component. The thick black line shows the sum of all components overlaid on the original spectrum. Peaks that are nearly identical in color to the original spectrum indicate a good fit. Gaps between the component sum and the original spectrum highlight regions where the current peak model is insufficient — these may indicate additional peaks that were not detected, or the need for a different peak shape.
A good peak fit should leave residuals (the difference between observed and fitted values) that resemble random noise. Systematic patterns in the residuals indicate that the model is missing important features.
plot_fit_residuals(
subset_spectrum,
fitted_voigt,
plot_title = c("Residuals of Voigt Fit", "Differences between original and fitted spectrum")
)Residuals of pseudo-voigt peak fitting
The residual plot shows the magnitude and pattern of the remaining error. Ideally, residuals should be centered around zero with no systematic structure. Large residuals at specific wavenumbers suggest either undetected peaks or a mismatch between the chosen peak shape and the actual spectral feature. In this case, the residuals are relatively small across most of the spectrum, confirming that the Voigt model provides a reasonable representation of the data.
Peak fitting is not limited to a single spectrum. When analyzing a series of samples — such as the biodiesel calibration standards — fitting each sample independently allows you to track how peak parameters change with concentration.
sample_ids <- unique(biodiesel$sample_id)
peak_table <- data.frame()
for (sid in sample_ids) {
sample_data <- biodiesel[biodiesel$sample_id == sid, ]
subset_data <- sample_data[
sample_data$wavenumber < 1800 & sample_data$wavenumber > 1000,
]
fitted <- fit_peaks(subset_data, method = "voigt")
peak_df <- fit_peak_df(fitted)
peak_df$sample_id <- sid
peak_table <- rbind(peak_table, peak_df)
}
head(peak_table)
#> sample_id peak wavenumber sigma eta amplitude mix_ratio
#> 1 biodiesel_0 1 1020.378 22.62041 0.4166742 0.7060718 0.04133478
#> 2 biodiesel_0 2 1073.967 21.21439 0.4690477 0.6173268 0.03613948
#> 3 biodiesel_0 3 1142.874 26.43836 0.5702245 0.4196304 0.02456596
#> 4 biodiesel_0 4 1154.669 28.49669 0.7876358 0.3804281 0.02227099
#> 5 biodiesel_0 5 1165.108 30.53523 0.9084903 0.3778804 0.02212184
#> 6 biodiesel_0 6 1235.631 26.68970 0.3423276 0.5810246 0.03401428
#> peak_shape
#> 1 voigt
#> 2 voigt
#> 3 voigt
#> 4 voigt
#> 5 voigt
#> 6 voigtThe resulting table contains fitted peak parameters for every sample. By filtering for specific peaks (e.g., the carbonyl peak near 1740 cm⁻¹), you can quantify how peak intensity, width, and position correlate with biodiesel concentration. This is the foundation of quantitative FTIR analysis: peak fitting converts qualitative spectral features into measurable, comparable numerical data.
| Peak Shape | Best For | Trade-offs |
|---|---|---|
| Voigt (default) | General FTIR spectra; combines instrumental and natural broadening | Slightly slower than Gaussian; recommended starting point |
| Gaussian | Narrow, symmetric peaks; computational efficiency | May underestimate peak shoulders and asymmetric features |
| Lorentzian | Peaks dominated by natural line width effects | Less common in FTIR; produces long tails that may overfit |
| Doniach-Sunjić-Gauss | Asymmetric peaks with shoulders; overlapping transitions | More parameters to tune; may overfit simple spectra |
find_ftir_peaks() arguments or supplying
peaklist directly.sigma, gam, mix_ratio,
eta, alpha, conv_cri, and
maxit can be passed directly to
fit_peaks().fixed_peaks = TRUE when you want
to compare peak parameters across samples without the optimizer shifting
peak locations.The peak fitting methods in PlotFTIR are implemented via the
EMpeaksR package, which uses spectrum-adapted
expectation-maximization algorithms:
Matsumura, T., Nagamura, N., Akaho, S., Nagata, K., & Ando, Y. (2019). “Spectrum adapted expectation-maximization algorithm for high-throughput peak shift analysis”. Science and Technology of Advanced Materials, 20(1), pp 733-745. doi:10.1080/14686996.2019.1620123
Matsumura, T., Nagamura, N., Akaho, S., Nagata, K., & Ando, Y. (2021). “Spectrum adapted expectation-conditional maximization algorithm for extending high-throughput peak separation method in XPS analysis”. Science and Technology of Advanced Materials: Methods, 1(1), pp 45-55. doi:10.1080/27660400.2021.1899449
Savitzky, A.; Golay, M.J.E. (1964). “Smoothing and Differentiation of Data by Simplified Least Squares Procedures”. Analytical Chemistry 36, pp 1627-1639. doi:10.1021/ac60214a047
Une tâche courante en spectroscopie IRTF est l’identification et la quantification des groupes fonctionnels au sein d’un mélange complexe. Les mélanges diesel-biodiesel fournissent un excellent exemple : la présence et la concentration de biodiesel introduisent des bandes d’absorption caractéristiques du carbonyle (C=O), de l’ester (C-O-C) et de l’alcène (=C-H) qui sont absentes dans le diesel pur.
Ce didacticiel présente un flux de travail complet d’ajustement de
pics — de la détection des pics chevauchés à l’évaluation de la qualité
de l’ajustement — en utilisant l’ensemble de données
biodiesel intégré. L’objectif n’est pas de cataloguer les
fonctions individuelles, mais de démontrer un pipeline d’analyse
pratique que vous pouvez adapter à vos propres échantillons.
L’ensemble de données biodiesel contient des spectres
IRTF-ATR de mélanges diesel avec du biodiesel croissant (0 % à 10 % plus
des mélanges commerciaux). Chaque spectre a été collecté sur la gamme
moyenne-IR standard, et la région de l’empreinte digitale (environ
1800–1000 cm⁻¹) contient les caractéristiques les plus diagnostiques
pour distinguer le biodiesel du diesel conventionnel.
data(biodiesel)
head(biodiesel)
#> PlotFTIR data:
#> Spectral range: 700.7395 - 710.0579 cm⁻¹
#> Resolution: variable
#> Intensity type: absorbance
#> Number of samples: 1
#> Sample IDs: biodiesel_0La sortie montre la structure au format long : les colonnes
wavenumber, absorbance et
sample_id avec 11 échantillons distincts. L’échantillon
biodiesel_0 (diesel pur) sert de référence pour la
comparaison.
Avant de tenter un ajustement de pics, il est important de comprendre le paysage spectral brut. La région de l’empreinte digitale de l’échantillon de diesel pur révèle déjà plusieurs caractéristiques discernables :
sample_spectrum <- biodiesel[biodiesel$sample_id == "biodiesel_0", ]
plot_ftir(
sample_spectrum,
plot_title = c("Spectre de Diesel Pur", "Région de l'empreinte digitale (1800–1000 cm⁻¹)"),
legend_title = "Échantillon",
lang = "fr"
) |>
zoom_in_on_range(c(1800, 1000))Spectres FTIR du diesel pur, de 1800 à 1000 cm-1.
Même cet échantillon de diesel pur révèle plusieurs bandes superposées. À mesure que la teneur en biodiesel augmente, des pics supplémentaires apparaissent — en particulier une bande de carbonyle vers 1740 cm⁻¹ qui est absente dans le diesel pur. C’est la caractéristique que nous allons isoler et quantifier.
La première étape de tout flux de travail d’ajustement de pics est
l’identification des emplacements candidats des pics. Plutôt que
d’inspecter manuellement le spectre, find_ftir_peaks()
combine plusieurs techniques mathématiques pour détecter les pics
automatiquement.
Une fois les emplacements des pics identifiés, l’étape suivante
consiste à ajuster chaque pic avec une fonction mathématique. La
fonction fit_peaks() prend en charge quatre formes de pics,
chacune appropriée pour différentes caractéristiques spectrales. Le
choix du modèle affecte à la fois la précision des paramètres des pics
et l’interprétabilité des résultats.
Le profil de Voigt est une convolution des fonctions de Gauss et de Lorentz. Il est généralement le modèle le plus approprié pour les spectres IRTF car il tient compte à la fois de l’élargissement instrumental (composante Gaussienne) et des effets de largeur de raie naturelle (composante de Lorentz). L’approximation pseudo-Voigt utilisée ici est efficace sur le plan computationnel tout en maintenant la précision.
fitted_voigt <- fit_peaks(subset_spectrum, method = "voigt")
fit_peak_df(fitted_voigt)
#> sample_id peak wavenumber sigma eta amplitude mix_ratio
#> 1 biodiesel_0 1 1020.378 22.620410 0.41667423 0.70607177 0.041334784
#> 2 biodiesel_0 2 1073.967 21.214391 0.46904772 0.61732677 0.036139483
#> 3 biodiesel_0 3 1142.874 26.438359 0.57022452 0.41963039 0.024565961
#> 4 biodiesel_0 4 1154.669 28.496689 0.78763583 0.38042812 0.022270986
#> 5 biodiesel_0 5 1165.108 30.535235 0.90849034 0.37788038 0.022121836
#> 6 biodiesel_0 6 1235.631 26.689703 0.34232764 0.58102456 0.034014282
#> 7 biodiesel_0 7 1295.061 22.373832 0.32421289 0.93511503 0.054743412
#> 8 biodiesel_0 8 1339.359 14.374979 0.86994255 0.79205797 0.046368580
#> 9 biodiesel_0 9 1366.080 11.455864 0.64757172 1.03248712 0.060443759
#> 10 biodiesel_0 10 1377.426 5.654138 0.60234771 1.86168373 0.108986506
#> 11 biodiesel_0 11 1430.670 18.132001 0.38013072 1.25041274 0.073201540
#> 12 biodiesel_0 12 1456.775 11.032503 0.08075851 6.45034640 0.377615544
#> 13 biodiesel_0 13 1489.794 20.522162 0.38137758 1.19693275 0.070070719
#> 14 biodiesel_0 14 1600.188 15.319595 0.65711181 0.38245647 0.022389729
#> 15 biodiesel_0 15 1600.890 137.807667 0.52843896 0.05348895 0.003131345
#> 16 biodiesel_0 16 1644.661 161.435246 0.70224043 0.04443884 0.002601534
#> peak_shape
#> 1 voigt
#> 2 voigt
#> 3 voigt
#> 4 voigt
#> 5 voigt
#> 6 voigt
#> 7 voigt
#> 8 voigt
#> 9 voigt
#> 10 voigt
#> 11 voigt
#> 12 voigt
#> 13 voigt
#> 14 voigt
#> 15 voigt
#> 16 voigtLa sortie montre les paramètres ajustés pour chaque pic :
l’emplacement du nombre d’ondes (wavenumber), la largeur
(sigma), le paramètre de forme (eta) et la
contribution relative (mix_ratio). Ces composantes ajustées
sont des résumés du spectre observé fondés sur un modèle, et non des
preuves directes qu’une composante correspond à une bande physique
unique. En pratique, surtout lorsque les bandes se chevauchent
fortement, l’interprétation des composantes doit être vérifiée à l’aide
des résidus, du contexte chimique et de toute attente préalable
concernant des centres de pics plausibles.
Les fonctions gaussiennes sont appropriées lorsque l’élargissement instrumental domine la forme de raie, ou lorsque la vitesse computationnelle est une priorité. Elles sont plus simples que les profils de Voigt et fonctionnent bien pour les pics étroits et symétriques avec un chevauchement minimal.
fitted_gauss <- fit_peaks(subset_spectrum, method = "gauss")
fit_peak_df(fitted_gauss)
#> sample_id peak wavenumber sigma amplitude mix_ratio peak_shape
#> 1 biodiesel_0 1 1025.538 15.636774 0.68023213 0.039822083 gauss
#> 2 biodiesel_0 2 1076.358 20.887670 0.72153205 0.042239858 gauss
#> 3 biodiesel_0 3 1133.338 19.869280 0.43510387 0.025471808 gauss
#> 4 biodiesel_0 4 1157.307 19.495302 0.23573096 0.013800138 gauss
#> 5 biodiesel_0 5 1172.412 15.262803 0.27931221 0.016351468 gauss
#> 6 biodiesel_0 6 1230.188 25.298231 0.71915802 0.042100878 gauss
#> 7 biodiesel_0 7 1294.259 21.939430 1.09007351 0.063814976 gauss
#> 8 biodiesel_0 8 1337.670 12.037717 0.64418390 0.037711750 gauss
#> 9 biodiesel_0 9 1363.620 11.191571 0.85552757 0.050084211 gauss
#> 10 biodiesel_0 10 1377.416 7.213309 1.83162791 0.107226981 gauss
#> 11 biodiesel_0 11 1418.432 16.741745 0.96820860 0.056680773 gauss
#> 12 biodiesel_0 12 1456.482 12.108292 7.06855671 0.413806751 gauss
#> 13 biodiesel_0 13 1497.974 20.799804 0.99740888 0.058390213 gauss
#> 14 biodiesel_0 14 1594.631 24.585315 0.47094531 0.027570034 gauss
#> 15 biodiesel_0 15 1682.710 23.211156 0.03869402 0.002265221 gauss
#> 16 biodiesel_0 16 1745.083 20.140979 0.04548636 0.002662858 gaussL’ajustement Gaussien a produit des emplacements de pics similaires au modèle de Voigt mais avec des largeurs plus étroites. Cela est attendu : sans la composante de queue longue de Lorentz, les pics gaussiens sont plus confinés et peuvent sous-estimer l’étendue des épaules de pics.
Les fonctions de Lorentz décrivent les effets de largeur de raie naturelle et sont appropriées lorsque l’élargissement est dominé par la durée de vie naturelle des transitions moléculaires. Elles produisent les queues longues caractéristiques qui les rendent utiles pour l’analyse des pics fortement chevauchés.
fitted_lorentz <- fit_peaks(subset_spectrum, method = "lorentz")
fit_peak_df(fitted_lorentz)
#> sample_id peak wavenumber gam amplitude mix_ratio peak_shape
#> 1 biodiesel_0 1 1019.813 27.305106 0.76201148 0.044609601 lorentz
#> 2 biodiesel_0 2 1072.054 25.860579 0.62509666 0.036594347 lorentz
#> 3 biodiesel_0 3 1143.668 32.642238 0.38336745 0.022443059 lorentz
#> 4 biodiesel_0 4 1155.553 33.247654 0.36218858 0.021203208 lorentz
#> 5 biodiesel_0 5 1165.318 29.786254 0.35992087 0.021070452 lorentz
#> 6 biodiesel_0 6 1243.707 28.454749 0.57080798 0.033416185 lorentz
#> 7 biodiesel_0 7 1295.611 22.808534 0.93208297 0.054565910 lorentz
#> 8 biodiesel_0 8 1339.394 17.307770 0.77883797 0.045594656 lorentz
#> 9 biodiesel_0 9 1367.295 9.505631 1.00739772 0.058974978 lorentz
#> 10 biodiesel_0 10 1377.667 4.842034 1.76221480 0.103163406 lorentz
#> 11 biodiesel_0 11 1443.020 8.024790 1.45993824 0.085467561 lorentz
#> 12 biodiesel_0 12 1458.260 9.278236 6.71774281 0.393269438 lorentz
#> 15 biodiesel_0 15 1464.907 144.494597 0.03117201 0.001824868 lorentz
#> 16 biodiesel_0 16 1467.340 182.514359 0.02227289 0.001303897 lorentz
#> 13 biodiesel_0 13 1468.957 14.692719 1.04711719 0.061300232 lorentz
#> 14 biodiesel_0 14 1604.856 8.946642 0.25961239 0.015198203 lorentzL’ajustement de Lorentz a produit des valeurs sigma plus larges que le modèle gaussien, reflétant les queues plus longues. Pour l’analyse IRTF, les profils de Lorentz purs sont moins couramment utilisés que Voigt car les spectres réels présentent rarement des formes de raie purement lorentziennes — elles montrent généralement un mélange des deux mécanismes d’élargissement.
La fonction Doniach-Sunjić (DSG) étend le profil de Voigt avec un
paramètre d’asymétrie supplémentaire (alpha), ce qui la
rend particulièrement utile pour les pics asymétriques qui résultent de
transitions chevauchées ou d’effets instrumentaux. Ceci est
particulièrement pertinent pour les spectres IRTF où les bandes
présentent souvent des épaules ou une asymétrie dues à la résonance de
Fermi ou à des groupes fonctionnels chevauchés.
fitted_dsg <- fit_peaks(subset_spectrum, method = "dsg")
fit_peak_df(fitted_dsg)
#> sample_id peak wavenumber sigma eta alpha amplitude
#> 1 biodiesel_0 1 1021.729 21.40673 0.2300115 1.040588e-01 0.68752459
#> 2 biodiesel_0 2 1073.426 20.95560 0.2654450 4.178487e-02 0.60471732
#> 3 biodiesel_0 3 1143.217 26.81239 0.4639632 1.536343e-06 0.41035249
#> 4 biodiesel_0 4 1154.571 28.32741 0.7240691 1.294036e-02 0.38246833
#> 5 biodiesel_0 5 1164.695 27.24772 0.9999984 3.076093e-02 0.38019900
#> 6 biodiesel_0 6 1236.692 26.40841 0.2236552 4.246228e-03 0.55029214
#> 7 biodiesel_0 7 1295.369 21.87505 0.2505226 1.536343e-06 0.90168888
#> 8 biodiesel_0 8 1340.207 15.76347 0.9166489 7.143756e-03 0.83265056
#> 9 biodiesel_0 9 1367.304 10.97263 0.5547417 6.767811e-03 1.08659688
#> 10 biodiesel_0 10 1377.527 5.08541 0.5266138 3.546381e-03 1.85128098
#> 11 biodiesel_0 11 1435.739 14.44531 0.2761619 2.297387e-02 1.43496041
#> 12 biodiesel_0 12 1457.101 10.32301 0.0713265 1.536343e-06 6.23193387
#> 13 biodiesel_0 13 1484.830 20.48238 0.1760790 1.536343e-06 1.24997682
#> 15 biodiesel_0 15 1577.526 135.43847 0.2820403 1.860076e-02 0.05411126
#> 16 biodiesel_0 16 1599.758 168.95308 0.7073266 2.394076e-03 0.04423006
#> 14 biodiesel_0 14 1602.472 12.82152 0.2434255 7.932406e-02 0.37879840
#> mix_ratio peak_shape
#> 1 0.040248997 doniach-sunjic-gauss
#> 2 0.035401302 doniach-sunjic-gauss
#> 3 0.024022815 doniach-sunjic-gauss
#> 4 0.022390423 doniach-sunjic-gauss
#> 5 0.022257573 doniach-sunjic-gauss
#> 6 0.032215148 doniach-sunjic-gauss
#> 7 0.052786582 doniach-sunjic-gauss
#> 8 0.048744947 doniach-sunjic-gauss
#> 9 0.063611448 doniach-sunjic-gauss
#> 10 0.108377509 doniach-sunjic-gauss
#> 11 0.084005311 doniach-sunjic-gauss
#> 12 0.364829259 doniach-sunjic-gauss
#> 13 0.073176020 doniach-sunjic-gauss
#> 15 0.003167776 doniach-sunjic-gauss
#> 16 0.002589312 doniach-sunjic-gauss
#> 14 0.022175578 doniach-sunjic-gaussL’ajustement DSG introduit le paramètre alpha, qui
contrôle le degré d’asymétrie. Lorsque alpha est proche de
zéro, le profil DSG se réduit à un Voigt symétrique. Des valeurs non
nulles indiquent un élargissement asymétrique — une caractéristique
commune dans les mélanges complexes comme les mélanges
diesel-biodiesel.
Pour comprendre comment chaque modèle représente les données, nous pouvons superposer les spectres ajustés sur l’original :
# Générer les spectres ajustés pour chaque modèle
voigt_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_voigt)
gauss_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_gauss)
lorentz_fit <- PlotFTIR:::.get_fit_spectra(subset_spectrum, fitted_lorentz)
# Construire le tableau de comparaison
comparison_data <- data.frame(
wavenumber = rep(subset_spectrum$wavenumber, 4),
absorbance = c(subset_spectrum$absorbance, voigt_fit, gauss_fit, lorentz_fit),
type = rep(c("Original", "Voigt", "Gaussien", "Lorentz"),
each = nrow(subset_spectrum))
)
# Tracer la comparaison
ggplot(comparison_data, aes(x = wavenumber, y = absorbance, color = type)) +
geom_line(linewidth = 0.8) +
facet_wrap(~type, ncol = 1, scales = "free_y") +
labs(
title = "Comparaison des Modèles d'Ajustement de Pics",
x = "Nombre d'ondes (cm⁻¹)",
y = "Absorbance"
) +
theme_minimal()Graphiques de chaque type d’ajustement de pic, comparant les résultats au spectre original.
Dans cet exemple, les ajustements de Voigt et de Lorentz suivent le spectre observé plus étroitement que l’ajustement gaussien, le modèle de Voigt offrant un compromis raisonnable entre souplesse et efficacité computationnelle. L’ajustement gaussien est plus simple, mais sa forme symétrique à décroissance rapide peut manquer des épaules ou des queues plus larges. Ces comparaisons dépendent des données plutôt que d’être universelles ; pour un nouveau spectre, les motifs de résidus et les attentes du domaine importent davantage qu’une recommandation unique par défaut.
Une fois un ajustement terminé, il est utile d’examiner les
composants individuels des pics qui composent le modèle global. La
fonction plot_components() décompose le spectre ajusté en
ses pics constitutifs, révélant quels groupes fonctionnels contribuent à
chaque région du spectre.
plot_components(
subset_spectrum,
fitted_voigt,
plot_fit = TRUE,
plot_title = c("Composants des Pics de Voigt", "Décomposition de la région de l'empreinte digitale"),
lang = "fr"
)Composantes tracées d’un moteur diesel équipé d’un profil de pic pseudo-Voigt.
Chaque ligne colorée représente un pic ajusté individuel. La ligne épaisse noire montre la somme de tous les composants superposée sur le spectre original. Les pics qui sont presque identiques en couleur au spectre original indiquent un bon ajustement. Les écarts entre la somme des composants et le spectre original mettent en évidence les régions où le modèle de pic actuel est insuffisant — ceux-ci peuvent indiquer des pics supplémentaires qui n’ont pas été détectés, ou le besoin d’une forme de pic différente.
Un bon ajustement de pics devrait laisser des résidus (la différence entre les valeurs observées et ajustées) qui ressemblent à du bruit aléatoire. Des motifs systématiques dans les résidus indiquent que le modèle manque de caractéristiques importantes.
plot_fit_residuals(
subset_spectrum,
fitted_voigt,
plot_title = c("Résidus de l'Ajustement de Voigt", "Différences entre le spectre original et le spectre ajusté"),
lang = "fr"
)Résidus de l’ajustement de pic pseudo-Voigt
Le graphique de résidus montre l’amplitude et le motif de l’erreur restante. Idéalement, les résidus devraient être centrés autour de zéro sans structure systématique. De grands résidus à des nombres d’ondes spécifiques indiquent soit des pics non détectés, soit un manque de correspondance entre la forme de pic choisie et la caractéristique spectrale réelle. Dans ce cas, les résidus sont relativement petits sur la majeure partie du spectre, confirmant que le modèle de Voigt fournit une représentation raisonnable des données.
L’ajustement de pics n’est pas limité à un seul spectre. Lorsqu’on analyse une série d’échantillons — comme les étalons de calibration biodiesel — l’ajustement de chaque échantillon indépendamment permet de suivre comment les paramètres des pics changent avec la concentration.
sample_ids <- unique(biodiesel$sample_id)
peak_table <- data.frame()
for (sid in sample_ids) {
sample_data <- biodiesel[biodiesel$sample_id == sid, ]
subset_data <- sample_data[
sample_data$wavenumber < 1800 & sample_data$wavenumber > 1000,
]
fitted <- fit_peaks(subset_data, method = "voigt")
peak_df <- fit_peak_df(fitted)
peak_df$sample_id <- sid
peak_table <- rbind(peak_table, peak_df)
}
head(peak_table)
#> sample_id peak wavenumber sigma eta amplitude mix_ratio
#> 1 biodiesel_0 1 1020.378 22.62041 0.4166742 0.7060718 0.04133478
#> 2 biodiesel_0 2 1073.967 21.21439 0.4690477 0.6173268 0.03613948
#> 3 biodiesel_0 3 1142.874 26.43836 0.5702245 0.4196304 0.02456596
#> 4 biodiesel_0 4 1154.669 28.49669 0.7876358 0.3804281 0.02227099
#> 5 biodiesel_0 5 1165.108 30.53523 0.9084903 0.3778804 0.02212184
#> 6 biodiesel_0 6 1235.631 26.68970 0.3423276 0.5810246 0.03401428
#> peak_shape
#> 1 voigt
#> 2 voigt
#> 3 voigt
#> 4 voigt
#> 5 voigt
#> 6 voigtLe tableau résultant contient les paramètres de pics ajustés pour chaque échantillon. En filtrant pour des pics spécifiques (par exemple, le pic de carbonyle vers 1740 cm⁻¹), vous pouvez quantifier comment l’intensité, la largeur et la position du pic corrélatent avec la concentration en biodiesel. C’est le fondement de l’analyse quantitative IRTF : l’ajustement de pics convertit les caractéristiques spectrales qualitatives en données numériques mesurables et comparables.
| Forme de Pic | Idéale Pour | Compromis |
|---|---|---|
| Voigt (défaut) | Spectres IRTF généraux ; combine l’élargissement instrumental et naturel | Légèrement plus lent que Gaussien ; point de départ recommandé |
| Gaussien | Pics étroits et symétriques ; efficacité computationnelle | Peut sous-estimer les épaules de pics et les caractéristiques asymétriques |
| Lorentzien | Pics dominés par les effets de largeur de raie naturelle | Moins courant en IRTF ; produit des queues longues qui peuvent surajuster |
| Doniach-Sunjić-Gauss | Pics asymétriques avec épaules ; transitions chevauchées | Plus de paramètres à régler ; peut surajuster les spectres simples |
find_ftir_peaks() ou de fournir
peaklist directement.sigma, gam,
mix_ratio, eta, alpha,
conv_cri et maxit peuvent être passés
directement à fit_peaks().fixed_peaks = TRUE lorsque
vous voulez comparer les paramètres des pics entre échantillons sans que
l’optimiseur ne déplace les emplacements des pics.Les méthodes d’ajustement de pics dans PlotFTIR sont implémentées via
le package EMpeaksR, qui utilise des algorithmes
d’espérance-maximisation adaptés aux spectres :
Matsumura, T., Nagamura, N., Akaho, S., Nagata, K., & Ando, Y. (2019). “Spectrum adapted expectation-maximization algorithm for high-throughput peak shift analysis”. Science and Technology of Advanced Materials, 20(1), pp 733-745. doi:10.1080/14686996.2019.1620123
Matsumura, T., Nagamura, N., Akaho, S., Nagata, K., & Ando, Y. (2021). “Spectrum adapted expectation-conditional maximization algorithm for extending high-throughput peak separation method in XPS analysis”. Science and Technology of Advanced Materials: Methods, 1(1), pp 45-55. doi:10.1080/27660400.2021.1899449
Savitzky, A.; Golay, M.J.E. (1964). “Smoothing and Differentiation of Data by Simplified Least Squares Procedures”. Analytical Chemistry 36, pp 1627-1639. doi:10.1021/ac60214a047
Comment Fonctionne la Détection des Pics
L’algorithme applique un lissage de Savitzky-Golay pour réduire le bruit, puis identifie les pics par trois approches complémentaires :
Les pics détectés par différentes méthodes dans une fenêtre configurable sont fusionnés en emplacements représentatifs uniques.
L’algorithme a détecté 16 pics sur la région de l’empreinte digitale. Ces emplacements serviront d’estimations initiales pour la procédure d’ajustement. Notez que la détection des pics est effectuée sur des données lissées, donc les nombres d’ondes exacts peuvent différer légèrement de l’inspection visuelle du spectre brut — ce qui est attendu et souhaitable, car cela réduit l’influence du bruit sur le positionnement des pics.
Cette étape de détection automatique est heuristique plutôt que définitive. Elle est utile pour proposer des centres de pics initiaux, mais elle ne doit pas être interprétée à elle seule comme une déconvolution validée. Dans les régions encombrées, pour les pics en épaule ou pour les bandes larges qui se chevauchent, il est souvent utile d’ajuster les paramètres de détection ou de fournir
peaklistdirectement.