"""Configuration shared by the training and prediction pipelines."""
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import Any
ROUTES = ("norm", "deriv1", "deriv2")
ROUTE_FILES = {
"norm": "combined_norm_data.csv.xz",
"deriv1": "combined_norm_deriv1_data.csv.xz",
"deriv2": "combined_norm_deriv2_data.csv.xz",
}
ROUTE_DESCRIPTIONS = {
"norm": "normalized spectra",
"deriv1": "first derivative of normalized spectra",
"deriv2": "second derivative of normalized spectra",
}
ORDERED_GROUPS = ("PE", "PET", "PP", "PS", "PVC", "Other")
# Coarse 6-group mapping of heterogeneous source labels. These denote spectral families, not
# single-polymer identities: "PET" pools ester-bearing plastics and "PE" includes PEF.
# NOTE: "PPMA" below is a typo for PMMA; since the data label is "PMMA", those spectra currently
# fall through to Other rather than PET. Fixing it would move ~5 rows (Other -> PET) and require
# regenerating results, so it is left as-is here to keep committed results consistent (see the
# grouping note in the manuscript/Methods).
CORE_GROUP = {"PVC"}
PP_MEMBERS = {"PP", "PPL"}
PE_MEMBERS = {"PE", "HDPE", "LDPE", "PEL", "PEF"}
PS_LIKE = {"PS", "ABS"}
PET_LIKE = {"PET", "PLA", "PEVA", "PPMA", "AC", "EAA", "PC", "CA"}
[docs]
@dataclass(frozen=True)
class PreprocessConfig:
"""Notebook preprocessing defaults, exposed as CLI-overridable settings."""
label_column: str = "type"
sample_id_column: str = "sample_id"
unknown_study: str = "kedzierski_2019_u"
# The combined route files carry all datasets; downstream selection is by study.
# labeled_studies -> supervised training/evaluation (load_labeled, train_route);
# external_studies -> independent augmentation pools (pooled explicitly per experiment).
labeled_studies: tuple[str, ...] = (
"jung_2018",
"kedzierski_2019",
"frond_2021",
"villegas_camacho_2024_c4",
)
external_studies: tuple[str, ...] = ("openspecy", "simple", "awi2024")
denoising_method: str = "wavelet"
baseline_method: str = "aspls"
normalization_method: str = "spectral_moments"
interpolate_method: str = "zero"
flat_windows: tuple[tuple[float, float], ...] = (
(1880.0, 1900.0),
(2400.0, 2700.0),
)
exclude_regions: tuple[tuple[float, float], ...] = (
(0.0, 679.0),
(3201.0, 5000.0),
)
# Atmospheric interpolation: only the CO2 asymmetric-stretch band is blanked.
# An empirical sharp-structure check across all five source datasets (RMS of the
# high-pass residual, <~25 cm^-1 features, inside vs. flanking control windows;
# raw data and the denoised+baseline stage) showed:
# - H2O-bend windows (1575-1615, 1620-1660): ratio 0.1-0.8 in every study, i.e.
# no water-vapor rotational lines — the source spectra are already background-
# compensated. Those windows only removed smooth polymer signal (PS aromatic
# ~1600, PET/C=C bands 1620-1660), so they were dropped.
# - CO2 window: ratio 2-22x in every study, with the artifact footprint extending
# below the old 2329 edge (jung ~2325, villegas down to ~2310), so the band is
# kept and widened from (2329, 2369) to (2300, 2380).
interpolate_regions: tuple[tuple[float, float], ...] = (
(2300.0, 2380.0),
)
wn_min: float = 680.0
wn_max: float = 3100.0
resolution: float = 2.0
descending: bool = True
combine_method: str = "pchip"
derivative_window_length: int = 15
derivative_polyorder: int = 3
derivative_delta: float = 1.0
random_state: int = 42
test_size: float = 0.2
n_jobs: int = -1
[docs]
def to_dict(self) -> dict[str, Any]:
return asdict(self)