Parent
#105
What to build
Add all new schema elements introduced by Scope 16 to the Phase 1 data model. No computation is implemented here — this slice is the pure type / field / config foundation that every other Scope 16 slice builds on.
New types (_numeric_config.py)
BimodalStats dataclass with fields: dip_statistic: float, dip_p_value: float, center1: float, center2: float
TailAsymmetryTag StrEnum with members: Symmetric = "symmetric", RightHeavy = "right_heavy", LeftHeavy = "left_heavy"
NumericFlag.Bimodal = "bimodal" and NumericFlag.HighOutlierDensity = "high_outlier_density" added to the existing StrEnum
NumericStats new fields
bimodal_stats: Optional[BimodalStats] = None
tail_asymmetry_tag: Optional[TailAsymmetryTag] = None
tail_asymmetry_ratio: Optional[float] = None
outlier_density: Optional[float] = None
to_dict() and from_dict() extended to round-trip all four new fields. BimodalStats serialises/deserialises as a nested dict (None when absent).
NumericProfileConfig five new threshold fields
bimodal_dip_p_value_threshold: float = 0.05
tail_asymmetry_right_threshold: float = 2.0
tail_asymmetry_left_threshold: float = 0.5
outlier_sigma_threshold: float = 3.0
high_outlier_density_threshold: float = 0.05
to_dict() and from_dict() extended for all five fields.
Acceptance criteria
Blocked by
None — can start immediately.
Parent
#105
What to build
Add all new schema elements introduced by Scope 16 to the Phase 1 data model. No computation is implemented here — this slice is the pure type / field / config foundation that every other Scope 16 slice builds on.
New types (
_numeric_config.py)BimodalStatsdataclass with fields:dip_statistic: float,dip_p_value: float,center1: float,center2: floatTailAsymmetryTagStrEnum with members:Symmetric = "symmetric",RightHeavy = "right_heavy",LeftHeavy = "left_heavy"NumericFlag.Bimodal = "bimodal"andNumericFlag.HighOutlierDensity = "high_outlier_density"added to the existing StrEnumNumericStatsnew fieldsbimodal_stats: Optional[BimodalStats] = Nonetail_asymmetry_tag: Optional[TailAsymmetryTag] = Nonetail_asymmetry_ratio: Optional[float] = Noneoutlier_density: Optional[float] = Noneto_dict()andfrom_dict()extended to round-trip all four new fields.BimodalStatsserialises/deserialises as a nested dict (Nonewhen absent).NumericProfileConfigfive new threshold fieldsbimodal_dip_p_value_threshold: float = 0.05tail_asymmetry_right_threshold: float = 2.0tail_asymmetry_left_threshold: float = 0.5outlier_sigma_threshold: float = 3.0high_outlier_density_threshold: float = 0.05to_dict()andfrom_dict()extended for all five fields.Acceptance criteria
BimodalStatsdataclass exists with the four specified fields and is importable fromdataforge_ml.profiling._numeric_configTailAsymmetryTagStrEnum exists withSymmetric,RightHeavy,LeftHeavyNumericFlag.BimodalandNumericFlag.HighOutlierDensityare present in theNumericFlagStrEnumNumericStatscarries the four new fields, all defaulting toNoneNumericStats.to_dict()serialisesbimodal_statsas a nested dict when present andNoneotherwise;from_dict()reconstructs aBimodalStatsfrom the nested dictNumericStats.to_dict()/from_dict()round-tripstail_asymmetry_tag,tail_asymmetry_ratio, andoutlier_densitycorrectlyNumericProfileConfigexposes all five new threshold fields with the specified defaultsNumericProfileConfig.to_dict()/from_dict()round-trips all five new fields; missing keys fall back to defaultsNumericStatsandNumericProfileConfiground-trip tests pass without modification (no regressions)Blocked by
None — can start immediately.