Skip to main content
Work in Progress

This page is currently under development. Content may be incomplete or contain inaccuracies. If you notice any errors or have suggestions, please contact us.

Use Case 2: Spectroscopic Data Analysis

Spectroscopic techniques measure how matter interacts with electromagnetic radiation, producing spectra that reveal molecular structure, composition, and concentration. This use case covers the management and analysis of spectroscopic data from techniques like NMR, IR/Raman, UV-Vis, and mass spectrometry.

Workflow Overview

Measurement → Data conversion → Processing → Interpretation
Raw vendor data → Open format (JCAMP-DX/mzML) → Processed spectra → Identified compounds / quantification

Key Concepts

Common Spectroscopic Techniques

TechniqueWhat it MeasuresKey InformationTypical File Size
NMRNuclear spin environmentsMolecular structure, connectivity1-100 MB per experiment
IR / RamanMolecular vibrationsFunctional groups, bond types10 KB - 1 MB
UV-VisElectronic transitionsConcentration, conjugation1-100 KB
Mass Spectrometry (MS)Mass-to-charge ratiosMolecular weight, fragmentation100 MB - 10 GB
X-ray Diffraction (XRD)Crystal lattice spacingsCrystal structure, phase identification1-10 MB

Data Processing Steps

  • Baseline correction — Remove background signal drift
  • Normalization — Scale spectra for comparison
  • Peak picking — Identify signal positions and intensities
  • Phase correction (NMR) — Correct phase distortions in frequency-domain spectra
  • Calibration — Map instrument response to known standards
  • Deconvolution — Separate overlapping peaks

Data Format Challenges

Each instrument manufacturer uses proprietary binary formats. Converting to open formats is critical for:

  • Long-term data preservation
  • Cross-instrument comparison
  • Sharing and reproducibility

General

  • Python (SciPy, NumPy, matplotlib) — Custom processing and visualization
  • R (hyperSpec, ChemoSpec) — Chemometric analysis

NMR

  • TopSpin (Bruker) — Acquisition and processing
  • MestReNova — Processing and analysis
  • nmrglue (Python) — Read/process NMR data programmatically

Mass Spectrometry

  • ProteoWizard / msconvert — Convert vendor formats to mzML
  • OpenMS — LC-MS data processing
  • MZmine — Untargeted metabolomics

IR/Raman

  • OPUS (Bruker) — IR data processing
  • SpectraGryph — Free viewer for multiple spectral formats

Code Example: Processing IR Spectra with Python

Click to expand Python workflow
#!/usr/bin/env python3
"""Process and compare IR spectra from JCAMP-DX files."""

import numpy as np
from scipy.signal import savgol_filter, find_peaks
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from pathlib import Path

def read_jcamp(filepath: Path) -> tuple[np.ndarray, np.ndarray]:
"""Read a JCAMP-DX file and return wavenumber and absorbance arrays."""
wavenumbers = []
absorbance = []
in_data = False

with open(filepath, "r") as f:
for line in f:
line = line.strip()
if line.startswith("##XYDATA") or line.startswith("##XYPOINTS"):
in_data = True
continue
if line.startswith("##END"):
break
if in_data and not line.startswith("##"):
parts = line.split()
if len(parts) >= 2:
wavenumbers.append(float(parts[0]))
absorbance.append(float(parts[1]))

return np.array(wavenumbers), np.array(absorbance)


# Step 1: Load spectra
wn_sample, abs_sample = read_jcamp(Path("sample.jdx"))
wn_ref, abs_ref = read_jcamp(Path("reference.jdx"))

# Step 2: Baseline correction (simple rubber band method)
def rubber_band_baseline(wavenumbers, spectrum, n_points=50):
"""Simple convex hull baseline correction."""
from scipy.spatial import ConvexHull
points = np.column_stack([wavenumbers, spectrum])
hull = ConvexHull(points)
baseline_idx = sorted(set(hull.vertices))
baseline_fn = interp1d(wavenumbers[baseline_idx], spectrum[baseline_idx],
kind="linear", fill_value="extrapolate")
return spectrum - baseline_fn(wavenumbers)

abs_sample_corr = rubber_band_baseline(wn_sample, abs_sample)

# Step 3: Smooth with Savitzky-Golay filter
abs_smooth = savgol_filter(abs_sample_corr, window_length=15, polyorder=3)

# Step 4: Normalize (min-max)
abs_norm = (abs_smooth - abs_smooth.min()) / (abs_smooth.max() - abs_smooth.min())

# Step 5: Find peaks
peaks, properties = find_peaks(abs_norm, height=0.1, prominence=0.05)
peak_wavenumbers = wn_sample[peaks]

print(f"Found {len(peaks)} peaks at: {peak_wavenumbers}")

# Step 6: Plot
fig, axes = plt.subplots(2, 1, figsize=(12, 8))

axes[0].plot(wn_sample, abs_sample, label="Raw", alpha=0.5)
axes[0].plot(wn_sample, abs_sample_corr, label="Baseline-corrected")
axes[0].set_xlabel("Wavenumber (cm⁻¹)")
axes[0].set_ylabel("Absorbance")
axes[0].invert_xaxis()
axes[0].legend()
axes[0].set_title("Raw vs. Baseline-Corrected Spectrum")

axes[1].plot(wn_sample, abs_norm, label="Processed sample")
axes[1].plot(wn_sample[peaks], abs_norm[peaks], "rx", markersize=8, label="Detected peaks")
axes[1].set_xlabel("Wavenumber (cm⁻¹)")
axes[1].set_ylabel("Normalized Absorbance")
axes[1].invert_xaxis()
axes[1].legend()
axes[1].set_title("Processed Spectrum with Peak Detection")

plt.tight_layout()
plt.savefig("ir_analysis.pdf", dpi=300)
plt.savefig("ir_analysis.png", dpi=300)
print("Analysis complete!")

Expected Outputs

  • Processed spectra — Baseline-corrected, normalized spectra in open formats (JCAMP-DX, CSV)
  • Peak lists — Tables of peak positions, intensities, and assignments
  • Comparison plots — Overlay of sample vs. reference spectra (PDF + PNG)
  • Quantification results — Concentrations from calibration curves
  • Structure assignments — Molecular structure determined from spectral evidence

Computational Requirements

TaskCPURAMStorageTime
Single spectrum processing1<1 GBMinimalSeconds
Batch processing (100s of spectra)2-42-4 GB1-10 GBMinutes
LC-MS untargeted metabolomics4-88-32 GB10-100 GBHours
NMR structure elucidation (2D)2-44-8 GB1-5 GBMinutes-hours

Common Issues & Troubleshooting

Common Problems

Vendor format lock-in

  • Convert to open formats (JCAMP-DX for spectroscopy, mzML for MS) as early as possible
  • Use ProteoWizard's msconvert for mass spectrometry data
  • Document the conversion process and any parameters used

Poor baseline

  • Automated baseline correction may fail for noisy spectra — try different algorithms
  • Manual baseline correction may be needed for complex backgrounds
  • Always visually inspect results

Peak overlap

  • Use deconvolution (curve fitting) to separate overlapping signals
  • Consider 2D techniques (e.g., 2D NMR) for complex mixtures

Key Considerations

  • Convert vendor formats early — Raw proprietary files may become unreadable as software evolves
  • Store raw data alongside processed results — Never discard the original measurement
  • Document instrument parameters — Acquisition settings are essential for reproducibility
  • Use reference databases — Compare spectra against SDBS, NIST, or MassBank for identification
  • Consider electronic lab notebooks — Link spectra to sample preparation records using eLabFTW or Chemotion