Skip to content

Normalization

spectrakit.normalize.normalize_snv

normalize_snv(intensities: ndarray) -> np.ndarray

Apply Standard Normal Variate normalization.

Centers each spectrum to zero mean and unit variance. Removes multiplicative scatter effects common in diffuse reflectance data.

Parameters:

Name Type Description Default
intensities ndarray

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

required

Returns:

Type Description
ndarray

SNV-normalized intensities, same shape as input.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/normalize/snv.py
def normalize_snv(intensities: np.ndarray) -> np.ndarray:
    """Apply Standard Normal Variate normalization.

    Centers each spectrum to zero mean and unit variance. Removes
    multiplicative scatter effects common in diffuse reflectance data.

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

    Returns:
        SNV-normalized intensities, same shape as input.

    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)

    if intensities.ndim == 1:
        mean = np.mean(intensities)
        std = np.std(intensities)
        if std < EPSILON:
            logger.warning("SNV: near-zero std (%.2e), returning zero-centered", std)
            return intensities - mean  # type: ignore[no-any-return]
        return (intensities - mean) / std  # type: ignore[no-any-return]

    means = np.mean(intensities, axis=1, keepdims=True)
    stds = np.std(intensities, axis=1, keepdims=True)
    degenerate = stds < EPSILON
    stds = np.where(degenerate, 1.0, stds)
    n_constant = int(np.sum(degenerate))
    if n_constant > 0:
        warnings.warn(
            f"SNV: {n_constant} spectrum/spectra have near-zero std and "
            "will be zero-centered only (not variance-scaled).",
            stacklevel=2,
        )

    return (intensities - means) / stds  # type: ignore[no-any-return]

spectrakit.normalize.normalize_minmax

normalize_minmax(intensities: ndarray) -> np.ndarray

Scale intensities to the [0, 1] range per spectrum.

Parameters:

Name Type Description Default
intensities ndarray

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

required

Returns:

Type Description
ndarray

Min-max normalized intensities, same shape.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/normalize/minmax.py
def normalize_minmax(intensities: np.ndarray) -> np.ndarray:
    """Scale intensities to the [0, 1] range per spectrum.

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

    Returns:
        Min-max normalized intensities, same shape.

    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)

    if intensities.ndim == 1:
        mn = intensities.min()
        mx = intensities.max()
        rng = mx - mn
        if rng < EPSILON:
            warnings.warn(
                "Min-max: constant spectrum (range < epsilon), returning zeros.",
                stacklevel=2,
            )
            return np.zeros_like(intensities)
        return (intensities - mn) / rng  # type: ignore[no-any-return]

    mins = intensities.min(axis=1, keepdims=True)
    maxs = intensities.max(axis=1, keepdims=True)
    rngs = maxs - mins
    degenerate = rngs < EPSILON
    rngs = np.where(degenerate, 1.0, rngs)
    n_constant = int(np.sum(degenerate))
    if n_constant > 0:
        warnings.warn(
            f"Min-max: {n_constant} spectrum/spectra have near-zero range and "
            "will be returned as zeros.",
            stacklevel=2,
        )

    return (intensities - mins) / rngs  # type: ignore[no-any-return]

spectrakit.normalize.normalize_area

normalize_area(
    intensities: ndarray, wavenumbers: ndarray | None = None
) -> np.ndarray

Normalize spectra so that the area under each curve equals 1.

Uses the trapezoidal rule for integration. If wavenumbers are not provided, assumes unit spacing.

Parameters:

Name Type Description Default
intensities ndarray

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

required
wavenumbers ndarray | None

X-axis values, shape (W,). Used for proper integration spacing. None assumes unit spacing.

None

Returns:

Type Description
ndarray

Area-normalized intensities, same shape.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/normalize/area.py
def normalize_area(
    intensities: np.ndarray,
    wavenumbers: np.ndarray | None = None,
) -> np.ndarray:
    """Normalize spectra so that the area under each curve equals 1.

    Uses the trapezoidal rule for integration. If wavenumbers are not
    provided, assumes unit spacing.

    Args:
        intensities: Spectral intensities, shape (W,) or (N, W).
        wavenumbers: X-axis values, shape (W,). Used for proper
            integration spacing. None assumes unit spacing.

    Returns:
        Area-normalized intensities, same shape.

    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)

    if intensities.ndim == 1:
        area = np.trapezoid(np.abs(intensities), x=wavenumbers)
        if area < EPSILON:
            warnings.warn(
                "Area normalization: near-zero area, returning spectrum unchanged.",
                stacklevel=2,
            )
            return intensities
        return intensities / area  # type: ignore[no-any-return]

    areas = np.trapezoid(np.abs(intensities), x=wavenumbers, axis=1).reshape(-1, 1)
    degenerate = areas < EPSILON
    areas = np.where(degenerate, 1.0, areas)
    n_zero = int(np.sum(degenerate))
    if n_zero > 0:
        warnings.warn(
            f"Area normalization: {n_zero} spectrum/spectra have near-zero area "
            "and will be returned unchanged.",
            stacklevel=2,
        )

    return intensities / areas  # type: ignore[no-any-return]

spectrakit.normalize.normalize_vector

normalize_vector(intensities: ndarray) -> np.ndarray

Normalize each spectrum to unit L2 norm.

Parameters:

Name Type Description Default
intensities ndarray

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

required

Returns:

Type Description
ndarray

L2-normalized intensities, same shape.

Raises:

Type Description
SpectrumShapeError

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

EmptySpectrumError

If input has zero elements.

Source code in src/spectrakit/normalize/vector.py
def normalize_vector(intensities: np.ndarray) -> np.ndarray:
    """Normalize each spectrum to unit L2 norm.

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

    Returns:
        L2-normalized intensities, same shape.

    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)

    if intensities.ndim == 1:
        norm = np.linalg.norm(intensities)
        if norm < EPSILON:
            warnings.warn(
                "Vector normalization: near-zero L2 norm, returning spectrum unchanged.",
                stacklevel=2,
            )
            return intensities
        return intensities / norm  # type: ignore[no-any-return]

    norms = np.linalg.norm(intensities, axis=1, keepdims=True)
    degenerate = norms < EPSILON
    norms = np.where(degenerate, 1.0, norms)
    n_zero = int(np.sum(degenerate))
    if n_zero > 0:
        warnings.warn(
            f"Vector normalization: {n_zero} spectrum/spectra have near-zero norm "
            "and will be returned unchanged.",
            stacklevel=2,
        )

    return intensities / norms  # type: ignore[no-any-return]