| """Repairer for categorical-normalization variants. | |
| Maps a minority spelling/format variant to the dominant exact form of its | |
| cluster (the value carried in ``issue.expected`` by the detector). This is a | |
| majority-vote canonicalization analogous to the FD repairer; it abstains when | |
| no expected canonical is present. Every proposal still passes the SMT verifier | |
| and the safety constitution. | |
| """ | |
| from __future__ import annotations | |
| from dataforge.detectors.base import Issue, Schema | |
| from dataforge.repairers.base import ProposedFix, RetryContext | |
| from dataforge.table import TableLike, cell_value | |
| from dataforge.transactions.txn import CellFix | |
| class CategoricalNormalizationRepairer: | |
| """Canonicalizes a minority categorical variant to its cluster's dominant form.""" | |
| def propose( | |
| self, | |
| issue: Issue, | |
| df: TableLike, | |
| schema: Schema | None, | |
| retry_context: RetryContext | None = None, | |
| ) -> ProposedFix | None: | |
| """Propose mapping the variant to the dominant canonical form.""" | |
| del retry_context, schema | |
| if issue.issue_type != "categorical_normalization" or not issue.expected: | |
| return None | |
| old_value = cell_value(df, issue.row, issue.column) | |
| if issue.expected == old_value: | |
| return None | |
| return ProposedFix( | |
| fix=CellFix( | |
| row=issue.row, | |
| column=issue.column, | |
| old_value=old_value, | |
| new_value=issue.expected, | |
| detector_id="categorical_normalization", | |
| operation="update", | |
| ), | |
| reason=(f"Normalized '{old_value}' to dominant cluster form '{issue.expected}'."), | |
| confidence=issue.confidence, | |
| provenance="deterministic", | |
| ) | |