///|
fn baseline_result(
values : Array[Double],
baseline : Array[Double],
weights : Array[Double],
iterations : Int32,
converged : Bool,
relative_change : Double,
) -> BaselineResult raise SpectralError {
let corrected = Array::makei(values.length(), fn(i) {
values[i] - baseline[i]
})
for v in corrected {
ignore(checked_result(v, "baseline correction"))
}
{ baseline, corrected, weights, iterations, converged, relative_change, }
}
///|
/// Asymmetric least-squares. Weights in the result are those used for the
/// returned baseline, rather than the next unsolved weight proposal.
pub fn asls(
input : Spectrum,
asymmetry~ : Double = 0.01,
options~ : IterationOptions = iteration_options(),
) -> BaselineResult raise SpectralError {
validate_iteration_options(options)
finite(asymmetry, "asymmetry")
if asymmetry < 1.0e-5 || asymmetry > 1.0 - 1.0e-5 {
raise Invalid("asymmetry must lie in [1e-5,1-1e-5]")
}
let y = input.values
let mut weights = Array::make(y.length(), 1.0)
for iteration in 1..<=options.max_iterations {
let baseline = whittaker(y, weights, options.lambda)
let next = Array::makei(y.length(), fn(i) {
if y[i] > baseline[i] {
asymmetry
} else {
1.0 - asymmetry
}
})
let change = relative_difference(next, weights)
let converged = change <= options.tolerance
if converged || iteration == options.max_iterations {
return baseline_result(y, baseline, weights, iteration, converged, change)
}
weights = next
}
raise Numerical("unreachable AsLS iteration state")
}
///|
/// Remove a fitted curve while retaining the original owned axis.
pub fn Spectrum::subtract_baseline(
self : Spectrum,
baseline : Array[Double],
) -> Spectrum raise SpectralError {
validate_values(baseline, 3)
if baseline.length() != self.length() {
raise Invalid("baseline length differs")
}
let values = Array::makei(self.length(), fn(i) {
self.values[i] - baseline[i]
})
spectrum(self.axis, values)
}