Praneshrajan15's picture
Deploy DataForge playground API
13fe504 verified
Raw
History Blame Contribute Delete
2.17 kB
"""Detector for exact duplicate rows.
Flags rows that are byte-for-byte duplicates of an earlier row. Each duplicate
occurrence after the first is reported (on the first column) as a
``duplicate_row`` issue for review. It is detection-only: removing rows is a
destructive operation the safety constitution forbids auto-applying, so no
repairer is registered - duplicates are surfaced, never silently deleted.
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, row_count
class DuplicateRowDetector:
"""Flags exact duplicate rows (detection-only; never auto-deletes).
Example:
>>> import pandas as pd
>>> df = pd.DataFrame({"a": ["1", "2", "1"], "b": ["x", "y", "x"]})
>>> issues = DuplicateRowDetector().detect(df)
>>> issues[0].row
2
"""
def detect(self, df: TableLike, schema: Schema | None = None) -> list[Issue]:
"""Detect exact duplicate rows."""
columns = column_names(df)
if not columns:
return []
first_column = columns[0]
seen: dict[tuple[str, ...], int] = {}
issues: list[Issue] = []
for row_idx in range(row_count(df)):
signature = tuple(str(df.at[row_idx, col]) for col in columns)
original = seen.get(signature)
if original is None:
seen[signature] = row_idx
continue
issues.append(
Issue(
row=row_idx,
column=first_column,
issue_type="duplicate_row",
severity=Severity.REVIEW,
confidence=0.95,
actual=signature[0],
reason=(
f"Row {row_idx} is an exact duplicate of row {original}; "
"review before removing (deletion is never auto-applied)."
),
)
)
return issues