Skip to content

Scatter Correction

spectrakit.scatter.scatter_msc

scatter_msc(
    intensities: ndarray, reference: ndarray | None = None
) -> np.ndarray

Apply Multiplicative Scatter Correction.

Each spectrum is corrected by fitting a linear regression against a reference spectrum (default: mean spectrum), then subtracting the intercept and dividing by the slope.

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (N, W) for a batch or (W,) for a single spectrum (requires reference).

required
reference ndarray | None

Reference spectrum, shape (W,). If None, uses the mean of the batch.

None

Returns:

Type Description
ndarray

MSC-corrected intensities, same shape as input.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

ValueError

If a single spectrum is provided without a reference.

Source code in src/spectrakit/scatter/msc.py
def scatter_msc(
    intensities: np.ndarray,
    reference: np.ndarray | None = None,
) -> np.ndarray:
    """Apply Multiplicative Scatter Correction.

    Each spectrum is corrected by fitting a linear regression against a
    reference spectrum (default: mean spectrum), then subtracting the
    intercept and dividing by the slope.

    Args:
        intensities: Spectral intensities, shape ``(N, W)`` for a batch
            or ``(W,)`` for a single spectrum (requires ``reference``).
        reference: Reference spectrum, shape ``(W,)``. If ``None``,
            uses the mean of the batch.

    Returns:
        MSC-corrected intensities, same shape as input.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.
        ValueError: If a single spectrum is provided without a reference.
    """
    intensities = ensure_float64(intensities)
    validate_1d_or_2d(intensities)
    warn_if_not_finite(intensities)

    if intensities.ndim == 1:
        if reference is None:
            raise ValueError(
                "reference is required for single-spectrum MSC. "
                "Pass a batch (N, W) array or provide a reference spectrum."
            )
        reference = ensure_float64(reference)
        return _msc_single(intensities, reference)

    if reference is None:
        reference = np.mean(intensities, axis=0)
    else:
        reference = ensure_float64(reference)

    return apply_along_spectra(_msc_single, intensities, reference=reference)

spectrakit.scatter.scatter_emsc

scatter_emsc(
    intensities: ndarray,
    reference: ndarray | None = None,
    poly_order: int = DEFAULT_POLY_ORDER,
) -> np.ndarray

Apply Extended Multiplicative Signal Correction.

Extends MSC by also modeling polynomial baseline variations. Fits each spectrum as a linear combination of the reference spectrum plus orthogonal polynomial (Legendre) terms, then corrects by removing the polynomial and scatter contributions.

Uses Legendre polynomials instead of monomials for improved numerical conditioning of the design matrix.

Parameters:

Name Type Description Default
intensities ndarray

Spectral intensities, shape (N, W) for a batch or (W,) for a single spectrum (requires reference).

required
reference ndarray | None

Reference spectrum, shape (W,). If None, uses the mean of the batch.

None
poly_order int

Maximum polynomial order for baseline modeling. Set to 0 to disable polynomial correction (equivalent to MSC).

DEFAULT_POLY_ORDER

Returns:

Type Description
ndarray

EMSC-corrected intensities, same shape as input.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

ValueError

If a single spectrum is provided without a reference, or if poly_order is negative.

Source code in src/spectrakit/scatter/emsc.py
def scatter_emsc(
    intensities: np.ndarray,
    reference: np.ndarray | None = None,
    poly_order: int = DEFAULT_POLY_ORDER,
) -> np.ndarray:
    """Apply Extended Multiplicative Signal Correction.

    Extends MSC by also modeling polynomial baseline variations.
    Fits each spectrum as a linear combination of the reference
    spectrum plus orthogonal polynomial (Legendre) terms, then
    corrects by removing the polynomial and scatter contributions.

    Uses Legendre polynomials instead of monomials for improved
    numerical conditioning of the design matrix.

    Args:
        intensities: Spectral intensities, shape ``(N, W)`` for a batch
            or ``(W,)`` for a single spectrum (requires ``reference``).
        reference: Reference spectrum, shape ``(W,)``. If ``None``,
            uses the mean of the batch.
        poly_order: Maximum polynomial order for baseline modeling.
            Set to 0 to disable polynomial correction (equivalent to MSC).

    Returns:
        EMSC-corrected intensities, same shape as input.

    Raises:
        SpectrumShapeError: If input is not 1-D or 2-D.
        EmptySpectrumError: If input has zero elements.
        ValueError: If a single spectrum is provided without a reference,
            or if ``poly_order`` is negative.
    """
    if poly_order < 0:
        raise ValueError(f"poly_order must be non-negative, got {poly_order}")

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

    if intensities.ndim == 1:
        if reference is None:
            raise ValueError(
                "reference is required for single-spectrum EMSC. "
                "Pass a batch (N, W) array or provide a reference spectrum."
            )
        reference = ensure_float64(reference)
        return _emsc_single(intensities, reference, poly_order)

    if reference is None:
        reference = np.mean(intensities, axis=0)
    else:
        reference = ensure_float64(reference)

    return apply_along_spectra(
        _emsc_single, intensities, reference=reference, poly_order=poly_order
    )