Skip to content

Baseline Correction

spectrakit.baseline.baseline_als

baseline_als(
    intensities: ndarray,
    lam: float = DEFAULT_LAMBDA,
    p: float = DEFAULT_P,
    max_iter: int = DEFAULT_MAX_ITER,
    tol: float = DEFAULT_TOL,
    return_info: bool = False,
) -> np.ndarray | ConvergenceInfo

Estimate baseline using Asymmetric Least Squares smoothing.

Iteratively fits a smooth baseline by penalizing deviations asymmetrically: points above the baseline are penalized less (weight p) than points below (weight 1-p).

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (W,) or (N, W).

required
lam float

Smoothness parameter (lambda). Larger = smoother. Typical range: 1e4 to 1e9.

DEFAULT_LAMBDA
p float

Asymmetry parameter. Smaller values push baseline lower. Typical range: 0.001 to 0.05.

DEFAULT_P
max_iter int

Maximum number of iterations.

DEFAULT_MAX_ITER
tol float

Convergence tolerance on weight change.

DEFAULT_TOL
return_info bool

If True, return a :class:ConvergenceInfo object with iteration count, convergence status, and baseline. Only supported for 1-D input.

False

Returns:

Type Description
ndarray | ConvergenceInfo

Estimated baseline (same shape as input), or

ndarray | ConvergenceInfo

class:ConvergenceInfo if return_info=True.

Raises:

Type Description
SpectrumShapeError

If input is not 1-D or 2-D.

EmptySpectrumError

If input has zero elements.

Examples:

>>> corrected = intensities - baseline_als(intensities)
>>> info = baseline_als(intensities, return_info=True)
>>> print(info.iterations, info.converged)
Source code in src/spectrakit/baseline/als.py
def baseline_als(
    intensities: np.ndarray,
    lam: float = DEFAULT_LAMBDA,
    p: float = DEFAULT_P,
    max_iter: int = DEFAULT_MAX_ITER,
    tol: float = DEFAULT_TOL,
    return_info: bool = False,
) -> np.ndarray | ConvergenceInfo:
    """Estimate baseline using Asymmetric Least Squares smoothing.

    Iteratively fits a smooth baseline by penalizing deviations
    asymmetrically: points above the baseline are penalized less
    (weight p) than points below (weight 1-p).

    Args:
        intensities: Spectral intensities, shape (W,) or (N, W).
        lam: Smoothness parameter (lambda). Larger = smoother.
            Typical range: 1e4 to 1e9.
        p: Asymmetry parameter. Smaller values push baseline lower.
            Typical range: 0.001 to 0.05.
        max_iter: Maximum number of iterations.
        tol: Convergence tolerance on weight change.
        return_info: If ``True``, return a :class:`ConvergenceInfo`
            object with iteration count, convergence status, and
            baseline. Only supported for 1-D input.

    Returns:
        Estimated baseline (same shape as input), or
        :class:`ConvergenceInfo` if ``return_info=True``.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.

    Examples:
        >>> corrected = intensities - baseline_als(intensities)
        >>> info = baseline_als(intensities, return_info=True)
        >>> print(info.iterations, info.converged)
    """
    if not 0 < p < 1:
        raise ValueError(f"p (asymmetry) must be in (0, 1), got {p}")
    if lam <= 0:
        raise ValueError(f"lam (smoothness) must be positive, got {lam}")
    if max_iter < 1:
        raise ValueError(f"max_iter must be >= 1, got {max_iter}")

    intensities = ensure_float64(intensities)
    validate_1d_or_2d(intensities)
    warn_if_not_finite(intensities)

    # Pre-compute the penalty matrix H = lam * D'D once.
    # This is the same for every spectrum in a batch since it depends
    # only on spectrum length and lam.
    n = intensities.shape[-1]
    D = sparse.diags([1, -2, 1], [0, 1, 2], shape=(n - 2, n))
    H = lam * D.T @ D

    if return_info:
        if intensities.ndim != 1:
            raise ValueError("return_info=True is only supported for 1-D input")
        return _baseline_als_1d_info(intensities, penalty=H, p=p, max_iter=max_iter, tol=tol)

    return apply_along_spectra(
        _baseline_als_1d, intensities, penalty=H, p=p, max_iter=max_iter, tol=tol
    )

spectrakit.baseline.baseline_snip

baseline_snip(
    intensities: ndarray,
    max_half_window: int = DEFAULT_MAX_HALF_WINDOW,
    decreasing: bool = True,
) -> np.ndarray

Estimate baseline using the SNIP algorithm.

Iteratively clips peaks by comparing each point to the average of its neighbors at increasing window sizes.

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (W,) or (N, W).

required
max_half_window int

Maximum half-window size. Controls how broad the features that get clipped can be.

DEFAULT_MAX_HALF_WINDOW
decreasing bool

If True, iterate from max_half_window down to 1.

True

Returns:

Type Description
ndarray

Estimated baseline, same shape as intensities.

Raises:

Type Description
SpectrumShapeError

If input is not 1-D or 2-D.

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/baseline/snip.py
def baseline_snip(
    intensities: np.ndarray,
    max_half_window: int = DEFAULT_MAX_HALF_WINDOW,
    decreasing: bool = True,
) -> np.ndarray:
    """Estimate baseline using the SNIP algorithm.

    Iteratively clips peaks by comparing each point to the average of
    its neighbors at increasing window sizes.

    Args:
        intensities: Spectral intensities, shape (W,) or (N, W).
        max_half_window: Maximum half-window size. Controls how broad
            the features that get clipped can be.
        decreasing: If True, iterate from max_half_window down to 1.

    Returns:
        Estimated baseline, same shape as intensities.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.
    """
    if max_half_window < 1:
        raise ValueError(f"max_half_window must be >= 1, got {max_half_window}")

    intensities = ensure_float64(intensities)
    validate_1d_or_2d(intensities)
    warn_if_not_finite(intensities)

    return apply_along_spectra(
        _baseline_snip_1d,
        intensities,
        max_half_window=max_half_window,
        decreasing=decreasing,
    )

spectrakit.baseline.baseline_polynomial

baseline_polynomial(
    intensities: ndarray,
    degree: int = DEFAULT_DEGREE,
    max_iter: int = DEFAULT_MAX_ITER,
    tol: float = DEFAULT_TOL,
    return_info: bool = False,
) -> np.ndarray | ConvergenceInfo

Estimate baseline using iterative polynomial fitting.

Fits a polynomial, then iteratively removes points above it (peaks) and refits until convergence.

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (W,) or (N, W).

required
degree int

Polynomial degree. Higher = more complex baselines.

DEFAULT_DEGREE
max_iter int

Maximum iterations for peak-removal loop.

DEFAULT_MAX_ITER
tol float

Convergence tolerance (fraction of points changed).

DEFAULT_TOL
return_info bool

If True, return a :class:ConvergenceInfo object. Only supported for 1-D input.

False

Returns:

Type Description
ndarray | ConvergenceInfo

Estimated baseline (same shape as input), or

ndarray | ConvergenceInfo

class:ConvergenceInfo if return_info=True.

Raises:

Type Description
SpectrumShapeError

If input is not 1-D or 2-D.

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/baseline/polynomial.py
def baseline_polynomial(
    intensities: np.ndarray,
    degree: int = DEFAULT_DEGREE,
    max_iter: int = DEFAULT_MAX_ITER,
    tol: float = DEFAULT_TOL,
    return_info: bool = False,
) -> np.ndarray | ConvergenceInfo:
    """Estimate baseline using iterative polynomial fitting.

    Fits a polynomial, then iteratively removes points above it
    (peaks) and refits until convergence.

    Args:
        intensities: Spectral intensities, shape (W,) or (N, W).
        degree: Polynomial degree. Higher = more complex baselines.
        max_iter: Maximum iterations for peak-removal loop.
        tol: Convergence tolerance (fraction of points changed).
        return_info: If ``True``, return a :class:`ConvergenceInfo`
            object. Only supported for 1-D input.

    Returns:
        Estimated baseline (same shape as input), or
        :class:`ConvergenceInfo` if ``return_info=True``.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.
    """
    if degree < 0:
        raise ValueError(f"degree must be non-negative, got {degree}")
    if max_iter < 1:
        raise ValueError(f"max_iter must be >= 1, got {max_iter}")
    if tol <= 0:
        raise ValueError(f"tol must be positive, got {tol}")

    intensities = ensure_float64(intensities)
    validate_1d_or_2d(intensities)
    warn_if_not_finite(intensities)

    if return_info:
        if intensities.ndim != 1:
            raise ValueError("return_info=True is only supported for 1-D input")
        return _baseline_polynomial_1d_info(intensities, degree=degree, max_iter=max_iter, tol=tol)

    return apply_along_spectra(
        _baseline_polynomial_1d, intensities, degree=degree, max_iter=max_iter, tol=tol
    )

spectrakit.baseline.baseline_rubberband

baseline_rubberband(intensities: ndarray) -> np.ndarray

Estimate baseline using the rubberband (convex hull) method.

Computes the lower convex hull of the spectrum and interpolates between the hull vertices to form the baseline.

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (W,) or (N, W).

required

Returns:

Type Description
ndarray

Estimated baseline, same shape as intensities.

Raises:

Type Description
SpectrumShapeError

If input is not 1-D or 2-D.

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/baseline/rubberband.py
def baseline_rubberband(intensities: np.ndarray) -> np.ndarray:
    """Estimate baseline using the rubberband (convex hull) method.

    Computes the lower convex hull of the spectrum and interpolates
    between the hull vertices to form the baseline.

    Args:
        intensities: Spectral intensities, shape (W,) or (N, W).

    Returns:
        Estimated baseline, same shape as intensities.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.
    """
    intensities = ensure_float64(intensities)
    validate_1d_or_2d(intensities)
    warn_if_not_finite(intensities)

    return apply_along_spectra(_baseline_rubberband_1d, intensities)