Sequential Monte Carlo (SMC) algorithms for fitting a generalised additive
mixed model (GAMM) to surface-enhanced resonance Raman spectroscopy (SERRS),
using the method of Moores et al. (2026)
serrsBayes provides model-based quantification of surface-enhanced
resonance Raman spectroscopy (SERRS) using sequential Monte Carlo (SMC)
algorithms. The details of the Bayesian model and informative priors are
provided in Moores et al. (2026) “Bayesian modelling and quantification
of Raman
spectroscopy.,”
published in the 2025 MATRIX Annals, Part II (Springer). Development
of this software was supported by the UK Engineering & Physical Sciences
Research Council (EPSRC) programme grant “In Situ Nanoparticle
Assemblies for Healthcare Diagnostics and
Therapy” (ref:
EP/L014165/1).
Stable releases, including binary packages for Windows & Mac OS, are available from CRAN:
install.packages("serrsBayes")
The current development version can be installed from GitHub:
devtools::install_github("mooresm/serrsBayes")
To simulate a synthetic Raman spectrum with known parameters:
set.seed(1234)
library(serrsBayes)
wavenumbers <- seq(700,1400,by=2)
spectra <- matrix(nrow=1, ncol=length(wavenumbers))
peakLocations <- c(840, 960, 1140, 1220, 1290)
peakAmplitude <- c(11500, 2500, 4000, 3000, 2500)
peakScale <- c(10, 15, 20, 10, 12)
signature <- weightedLorentzian(peakLocations, peakScale, peakAmplitude, wavenumbers)
baseline <- 1000*cos(wavenumbers/200) + 2*wavenumbers
spectra[1,] <- signature + baseline + rnorm(length(wavenumbers),0,200)
plot(wavenumbers, spectra[1,], type='l', xlab=expression(paste("Raman shift (cm"^{-1}, ")")), ylab="Intensity (a.u.)")
lines(wavenumbers, baseline, col=2, lty=4)
lines(wavenumbers, baseline + signature, col=4, lty=2, lwd=2)
Fit the model using SMC:
lPriors <- list(scale.mu=log(11.6) - (0.4^2)/2, scale.sd=0.4, bl.smooth=10^11, bl.knots=50,
beta.mu=5000, beta.sd=5000, noise.sd=200, noise.nu=4)
tm <- system.time(result <- fitSpectraSMC(wavenumbers, spectra, peakLocations, lPriors))
Sample 200 particles from the posterior distribution:
print(tm)
#> user system elapsed
#> 164.263 1.925 43.341
samp.idx <- sample.int(length(result$weights), 200, prob=result$weights)
plot(wavenumbers, spectra[1,], type='l', xlab=expression(paste("Raman shift (cm"^{-1}, ")")), ylab="Intensity (a.u.)")
for (pt in samp.idx) {
bl.est <- result$basis %*% result$alpha[,1,pt]
lines(wavenumbers, bl.est, col="#C3000009")
lines(wavenumbers, bl.est + result$expFn[pt,], col="#0000C309")
}