Source code for specmod.smoothing

"""Spectral smoothing.

Separate from estimation, and lossy by design — see
:mod:`specmod.smoothing.base` for why a smoothed spectrum no longer satisfies
the Parseval contract that :mod:`specmod.transforms` guarantees.
"""

from __future__ import annotations

from ..core.spectrum import Spectrum

# `record_smoothing` is re-exported because anything implementing `Smoother`
# needs it to leave the same metadata trail the shipped smoothers do — it is
# to `Smoother` what `prepare_record` is to `SpectralEstimator`.
from .base import NoSmoothing, Smoother, record_smoothing
from .konno_ohmachi import KonnoOhmachi
from .log_bins import LogBinner
from .log_window import WINDOWS, LogWindow
from .savitzky_golay import SavitzkyGolay

#: Name -> smoother. This is what `[smoothing] method` resolves through, and
#: `tests/test_smoothing.py` asserts that every entry is reachable from the
#: configuration and survives the pipeline. The comment here used to claim the
#: resolution and no code performed it; see REFACTOR_PLAN §6.6.
SMOOTHERS: dict[str, type[Smoother]] = {
    "log_bins": LogBinner,
    "konno_ohmachi": KonnoOhmachi,
    "log_window": LogWindow,
    "savitzky_golay": SavitzkyGolay,
    "none": NoSmoothing,
}


[docs] def get_smoother(name: str, **kwargs: object) -> Smoother: """Construct a smoother by name.""" try: cls = SMOOTHERS[name] except KeyError: raise ValueError( f"Unknown smoother {name!r}. Available: {sorted(SMOOTHERS)}." ) from None smoother: Smoother = cls(**kwargs) return smoother
[docs] def is_smoothed(spectrum: Spectrum) -> bool: """Whether any smoother has been applied. Worth checking before comparing a spectrum's energy against a time-domain expectation: smoothing does not preserve it. """ return bool(spectrum.meta.get("smoothing"))
__all__ = [ "SMOOTHERS", "WINDOWS", "KonnoOhmachi", "LogBinner", "LogWindow", "NoSmoothing", "SavitzkyGolay", "Smoother", "get_smoother", "is_smoothed", "record_smoothing", ]