Note
Aller à la fin pour télécharger le code complet de l’exemple.
Analyse de spectre#
Cet exemple présente un traitement avancé du signal pour l’analyse spectroscopique d’un spectre de paracétamol. On y montre comment appliquer une réduction de bruit, sélectionner une région d’intérêt, ajuster des pics et supprimer une tendance grâce aux fonctionnalités de Sigima. Chaque étape s’appuie sur la précédente pour construire un flux d’analyse complet.
- Utilisation :
python paracetamol_example.py
Ce script illustre des flux de traitement de données spectroscopiques couramment utilisés en chimie analytique et en science des matériaux.
import numpy as np
import sigima.objects
import sigima.proc.signal
from sigima import viz
from sigima.tests.data import create_paracetamol_signal
from sigima.tools.signal import fitting, peakdetection
# Constants
XLABEL_ANGLE = "Angle"
YLABEL_INTENSITY = "Intensity"
Charger le signal test et visualiser l’aperçu initial#
On charge un spectre de paracétamol fourni dans les données de test de Sigima. Ce spectre contient des pics d’absorption caractéristiques que l’on analyse à l’aide de différentes techniques de traitement du signal.
# Load the paracetamol signal from test data
sig = create_paracetamol_signal()
x_orig, y_orig = sig.xydata
print("✓ Paracetamol spectrum loaded successfully!")
print(f"Signal contains {len(x_orig)} data points")
print(f"Energy range: {x_orig.min():.1f} to {x_orig.max():.1f} eV")
print(f"Intensity range: {y_orig.min():.1f} to {y_orig.max():.1f}")
# Visualize the original spectrum
viz.view_curves(sig, title="Paracetamol Spectrum - Original")
✓ Paracetamol spectrum loaded successfully!
Signal contains 999 data points
Energy range: 5.0 to 54.9 eV
Intensity range: 56.0 to 997.4
Appliquer un filtre de Wiener pour réduire le bruit#
Le signal est déjà très propre. Toutefois, pour illustrer les capacités de filtrage de Sigima, on applique un filtre de Wiener afin de réduire tout bruit résiduel tout en préservant les caractéristiques spectrales.
sig_filt = sigima.proc.signal.wiener(sig)
print("\n✓ Wiener filter applied!")
print("The Wiener filter provides optimal noise reduction for signals")
print("with known statistical properties.")
# Compare original and filtered signals
viz.view_curves(
[sig, sig_filt], title="Paracetamol Spectrum - Original vs Wiener Filtered"
)
✓ Wiener filter applied!
The Wiener filter provides optimal noise reduction for signals
with known statistical properties.
Sélectionner une région d’intérêt (ROI)#
On concentre d’abord l’analyse sur un pic d’intérêt. Pour cela, on définit une région d’intérêt (ROI) autour de la caractéristique que l’on souhaite analyser.
# Define ROI around the peak
roi_bounds = [35.5, 41.3] # Energy range in eV
sig_filt.roi = sigima.objects.create_signal_roi(roi_bounds)
print(f"\n✓ ROI defined from {roi_bounds[0]} to {roi_bounds[1]} eV")
print("This focuses analysis on the primary absorption feature")
# Visualize the signal with ROI
viz.view_curves(sig_filt, title="Paracetamol Spectrum - Filtered with ROI")
✓ ROI defined from 35.5 to 41.3 eV
This focuses analysis on the primary absorption feature
Ajuster le pic avec une gaussienne#
On peut maintenant ajuster le pic dans la ROI sélectionnée à l’aide d’un modèle gaussien. Cela fournit des paramètres quantitatifs tels que la position, l’amplitude et la largeur du pic.
# Perform Gaussian fit on the ROI-selected data
fit = sigima.proc.signal.gaussian_fit(sig_filt)
print("\n✓ Gaussian fit completed!")
print("This characterizes the main absorption peak with parameters:")
print("- Peak position (energy)")
print("- Peak amplitude (intensity)")
print("- Peak width (FWHM)")
# Visualize the signal with Gaussian fit
viz.view_curves([sig_filt, fit], title="Paracetamol Spectrum - ROI with Gaussian Fit")
✓ Gaussian fit completed!
This characterizes the main absorption peak with parameters:
- Peak position (energy)
- Peak amplitude (intensity)
- Peak width (FWHM)
Supprimer la tendance linéaire#
Après avoir ajusté le pic principal, on peut vouloir supprimer toute dérive de ligne de base présente dans l’ensemble du spectre.
La fonction de suppression de tendance de Sigima réalise un ajustement linéaire sur tout le signal, y compris les pics. Dans notre spectre, les pics occupent une grande partie du signal, ce qui convient aux signaux où ils sont répartis symétriquement autour du centre avec des amplitudes similaires. Ce n’est pas le cas ici et on ne peut pas s’attendre à ce que cette fonction soit performante. C’est toutefois un exemple intéressant pour montrer comment combiner les fonctions de Sigima afin de mener une analyse plus avancée.
Pour illustrer la limitation mentionnée ci-dessus, on applique la fonction de suppression de tendance directement sur le signal filtré. Il est important de se souvenir que l’on avait défini une ROI afin de se concentrer sur le pic principal. On doit supprimer cette contrainte de ROI pour appliquer la suppression de tendance sur l’intégralité du signal.
# Remove ROI constraint for full signal detrending
sig_filt.roi = None
# Apply linear detrending to remove baseline drift
detrended_signal = sigima.proc.signal.detrending(sig_filt, method="linear")
print("\n✓ Linear detrending applied!")
# Compare filtered and detrended signals
viz.view_curves(
[sig_filt, detrended_signal], title="Paracetamol Spectrum - Filtered vs Detrended"
)
✓ Linear detrending applied!
La comparaison montre, comme prévu, que la fonction de suppression de tendance n’est pas adaptée à ce signal. Comme expliqué plus haut, l’algorithme réalise un ajustement linéaire sur tout le signal, y compris les pics. Cet effet apparaît clairement sur la figure : les pics de gauche, plus élevés que ceux de droite, se retrouvent après la suppression de tendance avec une intensité initiale plus faible que ceux de droite, et tous les pics possèdent une ligne de base négative.
Améliorer la suppression de tendance en excluant les pics#
Une idée pour contourner la limitation de la fonction de suppression de tendance vient du comportement du signal traité : on a identifié que le problème vient du fait que l’ajustement linéaire n’est pas effectué uniquement sur la ligne de base mais aussi sur les pics.
Pour obtenir une meilleure suppression de tendance, on peut donc détecter d’abord les pics puis ajuster une droite uniquement sur les zones sans pic. On s’attend raisonnablement à ce que cette approche fournisse une estimation de ligne de base plus précise et un signal mieux corrigé.
Détecter automatiquement les pics#
On peut utiliser la fonction de détection de pics de Sigima pour identifier automatiquement les pics du spectre. Cette fonction analyse le signal et renvoie les indices des pics détectés.
# Identify peaks in the detrended signal
peak_indices = peakdetection.peak_indices(sig_filt.y)
print("\n✓ Peak detection completed!")
print(f"Found {len(peak_indices)} potential peaks in the spectrum")
# Print detected peak positions
x_data = sig_filt.x
for i, peak_idx in enumerate(peak_indices):
energy = x_data[peak_idx]
intensity = sig_filt.y[peak_idx]
print(f" Peak {i + 1}: Energy = {energy:.2f} eV, Intensity = {intensity:.1f}")
✓ Peak detection completed!
Found 12 potential peaks in the spectrum
Peak 1: Energy = 13.95 eV, Intensity = 459.0
Peak 2: Energy = 16.10 eV, Intensity = 605.1
Peak 3: Energy = 18.10 eV, Intensity = 983.5
Peak 4: Energy = 19.50 eV, Intensity = 464.7
Peak 5: Energy = 21.20 eV, Intensity = 738.3
Peak 6: Energy = 23.80 eV, Intensity = 335.6
Peak 7: Energy = 27.45 eV, Intensity = 658.2
Peak 8: Energy = 28.45 eV, Intensity = 770.4
Peak 9: Energy = 31.05 eV, Intensity = 878.3
Peak 10: Energy = 31.80 eV, Intensity = 352.0
Peak 11: Energy = 34.20 eV, Intensity = 400.5
Peak 12: Energy = 38.45 eV, Intensity = 403.3
Ajuster plusieurs gaussiennes#
On peut maintenant ajuster plusieurs fonctions gaussiennes sur les pics détectés. Cela permet de caractériser chaque pic individuellement et d’obtenir pour chacun la position, l’amplitude et la largeur.
# Perform multi-Gaussian fit using detected peaks
fitted_y, params = fitting.multigaussian_fit(
sig_filt.x, sig_filt.y, peak_indices=peak_indices.tolist()
)
# Create fitted signal object for visualization
fitted_signal = sig_filt.copy()
fitted_signal.y = fitted_y
fitted_signal.title = "Multi-Gaussian Fit"
print("\n✓ Multi-Gaussian fitting completed!")
print("Each detected peak is fitted with individual Gaussian functions")
# Visualize the final fitting result
viz.view_curves(
[sig_filt, fitted_signal],
title="Paracetamol Spectrum - Detrended with Multi-Gaussian Fit",
)
✓ Multi-Gaussian fitting completed!
Each detected peak is fitted with individual Gaussian functions
Définir des ROI hors pics pour améliorer la suppression de tendance#
Pour améliorer la suppression de tendance, on définit des ROI qui excluent les pics détectés. Cela permet d’ajuster la ligne de base uniquement sur les zones sans pic.
# Extract peak parameters from the multi-Gaussian fit
# Each peak has 3 parameters: amplitude, center, sigma
num_peaks = len(peak_indices)
peak_params = []
peaks_roi_bounds = np.zeros((num_peaks, 2))
for i in range(num_peaks):
# Extract parameters for each Gaussian (amplitude, center, sigma)
amplitude = params[f"amp_{i + 1}"]
center = params[f"x0_{i + 1}"]
sigma = params[f"sigma_{i + 1}"]
peak_params.append([amplitude, center, sigma])
# Define exclusion zone as center ± 2*sigma
exclusion_start = center - 2 * abs(sigma)
exclusion_end = center + 2 * abs(sigma)
peaks_roi_bounds[i] = [exclusion_start, exclusion_end]
print(f"Peak {i + 1}: Center = {center:.2f} eV, Sigma = {sigma:.3f}")
print(f" Exclusion zone: [{exclusion_start:.2f}, {exclusion_end:.2f}] eV")
# Create ROIs including detected peaks
roi = sigima.objects.create_signal_roi(peaks_roi_bounds)
# invert ROIs to exclude peaks
sig_filt.roi = roi.inverted(sig_filt.x.min(), sig_filt.x.max())
# Visualize the signal with new ROI
viz.view_curves(
sig_filt, title="Paracetamol Spectrum - Filtered with Peak Exclusion ROIs"
)
Peak 1: Center = 13.94 eV, Sigma = 0.247
Exclusion zone: [13.44, 14.43] eV
Peak 2: Center = 16.08 eV, Sigma = 0.177
Exclusion zone: [15.72, 16.43] eV
Peak 3: Center = 18.08 eV, Sigma = 0.214
Exclusion zone: [17.65, 18.50] eV
Peak 4: Center = 19.45 eV, Sigma = 0.423
Exclusion zone: [18.60, 20.29] eV
Peak 5: Center = 21.19 eV, Sigma = 0.187
Exclusion zone: [20.82, 21.56] eV
Peak 6: Center = 23.88 eV, Sigma = 0.394
Exclusion zone: [23.09, 24.66] eV
Peak 7: Center = 27.42 eV, Sigma = 0.170
Exclusion zone: [27.08, 27.76] eV
Peak 8: Center = 28.48 eV, Sigma = 0.173
Exclusion zone: [28.14, 28.83] eV
Peak 9: Center = 31.05 eV, Sigma = 0.142
Exclusion zone: [30.77, 31.33] eV
Peak 10: Center = 31.79 eV, Sigma = 0.146
Exclusion zone: [31.50, 32.08] eV
Peak 11: Center = 34.17 eV, Sigma = 0.237
Exclusion zone: [33.70, 34.65] eV
Peak 12: Center = 38.41 eV, Sigma = 0.201
Exclusion zone: [38.00, 38.81] eV
On peut ensuite ajuster une droite sur le signal en utilisant les ROI définies pour exclure les pics. On obtient ainsi une estimation plus précise de la ligne de base.
fitted_signal = fit = sigima.proc.signal.linear_fit(sig_filt)
On peut enfin soustraire l’ajustement linéaire étendu du signal filtré original pour obtenir un signal mieux débarrassé de sa tendance. On constate que la ligne de base est désormais correctement estimée, ce qui conduit à une correction plus fiable.
better_detrended_signal = sigima.proc.signal.difference(sig_filt, fitted_signal)
better_detrended_signal.title = "Improved Detrended Signal"
print("\n✓ Improved detrending applied!")
# Compare filtered and better detrended signals
viz.view_curves(
[sig_filt, better_detrended_signal],
title="Paracetamol Spectrum - Filtered vs Improved Detrended",
show_roi=False,
)
✓ Improved detrending applied!
Pour aller plus loin…#
À la dernière étape, on a nettement amélioré la suppression de tendance du signal. Toutefois, plusieurs pics situés à gauche ne sont toujours pas détectés et la correction peut encore progresser. Nous vous invitons à expérimenter en ajustant les paramètres de la fonction de détection de pics et à explorer son code source pour mieux comprendre son fonctionnement.