fix(structured): anonymize columns whose name is not an identifier#2142
fix(structured): anonymize columns whose name is not an identifier#2142uwezkhan wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes structured anonymization for pandas DataFrames when column names are not valid Python identifiers (e.g., contain spaces or hyphens), ensuring those columns are correctly read and anonymized instead of failing or being skipped due to itertuples() field renaming.
Changes:
- Update
PandasDataProcessor._processto read/write cells by DataFrame label (data.at[index, key]) rather thangetattr()onitertuples()rows. - Add a unit test covering anonymization of a column with a non-identifier name (e.g.,
"Full Name").
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| presidio-structured/presidio_structured/data/data_processors.py | Switch per-cell access from itertuples()+getattr to label-based .at to support any column names. |
| presidio-structured/tests/data/test_data_transformers.py | Add regression test validating anonymization for non-identifier column names. |
| # Column names holding PII are often not valid Python identifiers | ||
| # ("Full Name", "e-mail", ...). itertuples renames those to positional | ||
| # fields, so a name-based getattr lookup skips them and the PII is left | ||
| # in place. |
There was a problem hiding this comment.
Reworded. The old path raised AttributeError and aborted mid-run, it didn't silently skip, so the comment now says that.
|
@uwezkhan I think we can preserve the correctness fix while avoiding a performance regression by iterating the column Series directly instead of doing a scalar for key, operator_callable in key_to_operator_mapping.items():
self.logger.debug(f"Operating on column {key}")
for index, text_to_operate_on in data[key].items():
operated_text = self._operate_on_text(text_to_operate_on, operator_callable)
data.at[index, key] = operated_textI tried this locally against this PR branch. The existing structured data tests still pass, including the new On a synthetic no-op operator benchmark with identifier columns, this avoided the slowdown from the current implementation:
|
|
Makes sense, done. Switched to iterating |
Change Description
PandasDataProcessor._processreads each cell withgetattr(row, key)while iteratingDataFrame.itertuples(). itertuples renames any column whose name is not a valid Python identifier (a space, a hyphen, a leading digit, a keyword) into a positional field like_1, so the lookup raisesAttributeErrorfor the very columns that usually hold PII, such as "Full Name" or "e-mail". The mapping comes straight fromdf.columnsvia the analysis builder, soStructuredEngine.anonymizehits this on ordinary input.Before: only columns named as Python identifiers were anonymized; a "Full Name" column aborted the run, and because the frame is mutated column by column, any columns processed earlier were redacted while the failing PII column was left untouched. After: the cell is read by label with
data.at[index, key], the same way the result is already written back, so read and write agree and the column name no longer matters. The tradeoff is a per-cell.atlookup in place of a namedtuple field access, negligible next to the operator call already run on every cell.Issue reference
No linked issue.
Checklist