Format-agnostic spectral data container.
Wraps intensity values and optional wavenumber/wavelength axis with
metadata. All processing functions accept and return numpy arrays,
but this container provides a convenient way to keep data and
metadata together.
Attributes:
| Name |
Type |
Description |
intensities |
ndarray
|
Spectral intensity values, shape (W,) for a single
spectrum or (N, W) for a collection.
|
wavenumbers |
ndarray | None
|
X-axis values (cm^-1, nm, etc.), shape (W,).
None if not available.
|
metadata |
dict[str, Any]
|
Arbitrary key-value metadata from the source file.
|
source_format |
str
|
Original file format (e.g., "jcamp", "spc", "csv").
|
label |
str
|
Human-readable label for display/logging.
|
Examples:
>>> spec = Spectrum(
... intensities=np.array([0.1, 0.5, 0.9, 0.5, 0.1]),
... wavenumbers=np.array([400, 800, 1200, 1600, 2000]),
... label="ethanol_ir",
... )
>>> spec.n_points
5
Source code in src/spectrakit/spectrum.py
| @dataclass
class Spectrum:
"""Format-agnostic spectral data container.
Wraps intensity values and optional wavenumber/wavelength axis with
metadata. All processing functions accept and return numpy arrays,
but this container provides a convenient way to keep data and
metadata together.
Attributes:
intensities: Spectral intensity values, shape (W,) for a single
spectrum or (N, W) for a collection.
wavenumbers: X-axis values (cm^-1, nm, etc.), shape (W,).
None if not available.
metadata: Arbitrary key-value metadata from the source file.
source_format: Original file format (e.g., "jcamp", "spc", "csv").
label: Human-readable label for display/logging.
Examples:
>>> spec = Spectrum(
... intensities=np.array([0.1, 0.5, 0.9, 0.5, 0.1]),
... wavenumbers=np.array([400, 800, 1200, 1600, 2000]),
... label="ethanol_ir",
... )
>>> spec.n_points
5
"""
intensities: np.ndarray
wavenumbers: np.ndarray | None = None
metadata: dict[str, Any] = field(default_factory=dict)
source_format: str = "unknown"
label: str = ""
def __post_init__(self) -> None:
"""Validate array shapes on construction."""
if not isinstance(self.intensities, np.ndarray):
self.intensities = np.asarray(self.intensities, dtype=np.float64)
if self.wavenumbers is not None:
if not isinstance(self.wavenumbers, np.ndarray):
self.wavenumbers = np.asarray(self.wavenumbers, dtype=np.float64)
if self.intensities.ndim == 1:
if self.wavenumbers.shape[0] != self.intensities.shape[0]:
raise ValueError(
f"wavenumbers length {self.wavenumbers.shape[0]} != "
f"intensities length {self.intensities.shape[0]}"
)
elif self.intensities.ndim == 2:
if self.wavenumbers.shape[0] != self.intensities.shape[1]:
raise ValueError(
f"wavenumbers length {self.wavenumbers.shape[0]} != "
f"intensities width {self.intensities.shape[1]}"
)
@property
def n_points(self) -> int:
"""Number of spectral data points (W)."""
if self.intensities.ndim == 1:
return int(self.intensities.shape[0])
return int(self.intensities.shape[1])
@property
def n_spectra(self) -> int:
"""Number of spectra (N). Returns 1 for single spectrum."""
if self.intensities.ndim == 1:
return 1
return int(self.intensities.shape[0])
@property
def shape(self) -> tuple[int, ...]:
"""Shape of the intensities array."""
return tuple(self.intensities.shape)
def copy(self) -> Spectrum:
"""Return a deep copy."""
return Spectrum(
intensities=self.intensities.copy(),
wavenumbers=self.wavenumbers.copy() if self.wavenumbers is not None else None,
metadata=copy.deepcopy(self.metadata),
source_format=self.source_format,
label=self.label,
)
|
n_points
property
Number of spectral data points (W).
n_spectra
property
Number of spectra (N). Returns 1 for single spectrum.
shape
property
Shape of the intensities array.
__post_init__
Validate array shapes on construction.
Source code in src/spectrakit/spectrum.py
| def __post_init__(self) -> None:
"""Validate array shapes on construction."""
if not isinstance(self.intensities, np.ndarray):
self.intensities = np.asarray(self.intensities, dtype=np.float64)
if self.wavenumbers is not None:
if not isinstance(self.wavenumbers, np.ndarray):
self.wavenumbers = np.asarray(self.wavenumbers, dtype=np.float64)
if self.intensities.ndim == 1:
if self.wavenumbers.shape[0] != self.intensities.shape[0]:
raise ValueError(
f"wavenumbers length {self.wavenumbers.shape[0]} != "
f"intensities length {self.intensities.shape[0]}"
)
elif self.intensities.ndim == 2:
if self.wavenumbers.shape[0] != self.intensities.shape[1]:
raise ValueError(
f"wavenumbers length {self.wavenumbers.shape[0]} != "
f"intensities width {self.intensities.shape[1]}"
)
|
copy
Return a deep copy.
Source code in src/spectrakit/spectrum.py
| def copy(self) -> Spectrum:
"""Return a deep copy."""
return Spectrum(
intensities=self.intensities.copy(),
wavenumbers=self.wavenumbers.copy() if self.wavenumbers is not None else None,
metadata=copy.deepcopy(self.metadata),
source_format=self.source_format,
label=self.label,
)
|