Download dataforge/detectors/missing_value.py from Praneshrajan15/dataforge-playground: direct link, hf CLI and curl.
- Browser
- Download file 4.15 kB
-
https://huggingface.co/spaces/Praneshrajan15/dataforge-playground/resolve/main/dataforge/detectors/missing_value.py
- Command line
-
hf download hf://spaces/Praneshrajan15/dataforge-playground/dataforge/detectors/missing_value.py
-
curl -L -o missing_value.py https://huggingface.co/spaces/Praneshrajan15/dataforge-playground/resolve/main/dataforge/detectors/missing_value.py
4.15 kB
| """Detector for missing values in predominantly-populated columns. | |
| A blank or sentinel cell is only an *error* when the column is otherwise | |
| populated (a mostly-empty column is legitimately sparse, not broken). This | |
| detector flags missing/sentinel cells in columns whose populated rate clears a | |
| threshold, or that a schema declares non-null. | |
| Missing-value *correction* is intentionally limited: a correct value generally | |
| cannot be invented. The companion repairer fills a value only when it is | |
| derivable from a declared functional dependency; otherwise the issue is | |
| detection-only (surfaced for review, never silently filled). | |
| The detector is pure: no LLM calls, no I/O, no side effects. | |
| """ | |
| from __future__ import annotations | |
| from dataforge.detectors.base import Issue, Schema, Severity | |
| from dataforge.table import TableLike, column_names, column_values | |
| # Values that read as missing. | |
| _MISSING_SENTINELS = frozenset( | |
| {"", "n/a", "na", "null", "none", "nan", "nil", "-", "unknown", "not available", "?"} | |
| ) | |
| # Minimum non-missing fraction for a column to be considered "populated". | |
| _POPULATED_THRESHOLD = 0.5 | |
| # Minimum rows for the populated-rate estimate to be meaningful. | |
| _MIN_VALUES = 8 | |
| def is_missing(value: str) -> bool: | |
| """Return whether a value reads as missing or a null sentinel.""" | |
| return value.strip().lower() in _MISSING_SENTINELS | |
| class MissingValueDetector: | |
| """Flags missing/sentinel cells in columns that are predominantly populated. | |
| Example: | |
| >>> import pandas as pd | |
| >>> detector = MissingValueDetector() | |
| >>> df = pd.DataFrame({"city": ["NY", "LA", "", "SF", "BOS", "DC", "LA", "NY"]}) | |
| >>> issues = detector.detect(df) | |
| >>> issues[0].row | |
| 2 | |
| """ | |
| def detect(self, df: TableLike, schema: Schema | None = None) -> list[Issue]: | |
| """Detect missing-value issues across predominantly-populated columns.""" | |
| non_null_columns = self._schema_non_null_columns(schema) | |
| issues: list[Issue] = [] | |
| for col_name in column_names(df): | |
| issues.extend(self._check_column(df, str(col_name), non_null_columns)) | |
| return issues | |
| def _schema_non_null_columns(schema: Schema | None) -> frozenset[str]: | |
| """Return columns a schema declares non-null, if any.""" | |
| if schema is None: | |
| return frozenset() | |
| columns = getattr(schema, "not_null_columns", None) or getattr(schema, "non_null", None) | |
| if not columns: | |
| return frozenset() | |
| return frozenset(str(c) for c in columns) | |
| def _check_column( | |
| self, df: TableLike, col_name: str, non_null_columns: frozenset[str] | |
| ) -> list[Issue]: | |
| """Flag missing cells in one column when the column is populated.""" | |
| values = [str(v) for v in column_values(df, col_name)] | |
| if len(values) < _MIN_VALUES: | |
| return [] | |
| missing_rows = [i for i, v in enumerate(values) if is_missing(v)] | |
| if not missing_rows: | |
| return [] | |
| populated = len(values) - len(missing_rows) | |
| populated_rate = populated / len(values) | |
| declared_non_null = col_name in non_null_columns | |
| if not declared_non_null and populated_rate < _POPULATED_THRESHOLD: | |
| return [] # legitimately sparse column | |
| confidence = 0.95 if declared_non_null else round(min(0.9, 0.4 + populated_rate / 2), 2) | |
| issues: list[Issue] = [] | |
| for row_idx in missing_rows: | |
| issues.append( | |
| Issue( | |
| row=row_idx, | |
| column=col_name, | |
| issue_type="missing_value", | |
| severity=Severity.REVIEW, | |
| confidence=confidence, | |
| expected=None, | |
| actual=values[row_idx], | |
| reason=( | |
| f"Missing value in column '{col_name}', which is " | |
| f"{populated_rate:.0%} populated." | |
| ), | |
| ) | |
| ) | |
| return issues | |