[nonimo]
EN
Download the app

Redact your CSV: remove personal data before AI sees it

Your document

Your document, with the personal data covered

· Updated

Joaquín Trapero

in Founder of Nonimo, writing on AI and privacy.

Column plan to mask data in a CSV file: names tokenised, emails deleted, birth dates cut to the year, notes read by hand

A CSV has no hidden sheets, no cell comments and no formatting. Everything in it sits in plain view, which makes it simple to clean and just as simple for an AI tool to read in full. The risk lives in the columns nobody looked at and in the notes field.

This page covers which columns go, how to swap names for tokens that line up across rows, and how to check the result. In my judgement, that check outweighs any clever token scheme. If your data starts life in a workbook, tidy the spreadsheet in Excel first and export it afterwards.

Quick answer

Work on a copy, delete the columns the task doesn't need, and replace each name or ID with a random token that is the same on every row. Keep the token list in a separate file, cut dates to the year and postcodes to their first characters, then read it as plain text.

Which columns to delete, tokenise or keep

Begin with the header row, not the data. An export from a CRM, a booking system or payroll often writes every field the record holds: internal IDs, the account owner’s email, a last-contact date nobody asked for. For each column, ask whether the question you will put to the AI needs it.

Direct identifiers are the easy part. Names, emails, phone numbers, card and account numbers, national ID numbers: if the analysis runs without them, delete the column. If rows must still be told apart, swap the value for a token. The longer list of details that point to a person covers the less obvious ones.

Keeping a column “just in case” is my first cut. One that never leaves your computer needs no token, no key and no second check. Staff and health files deserve the hardest cut; for a US payroll export, what HR can pass to an AI tool is a sensible first stop.

What to do with each column

Filter by action. Notes columns are marked because no column rule cleans them.

ColumnActionWhy
Full nameTokenRows still need to be grouped by person
Email, phoneDeleteA task rarely needs them, and they identify on their own
Customer or account numberTokenOnly if you must join this file with another later
Card, bank or national ID numberDeleteNo analysis needs the digits
Street addressDeletePoints at one household
Date of birthGeneraliseYear or five-year band
Postcode or ZIPGeneraliseFirst part only
Rare job titleGeneralise"Night charge nurse" becomes "nursing"
Notes, comments, descriptionsRead by hand, or deleteNames sit inside sentences
Plan, product, amount, statusKeepUsually what the question is about

Our default for a client export; your question may need a different mix. Direct and quasi-identifiers as NIST SP 800-188 (2023) describes them. For US health records, see where PHI stops and PII begins.

Free-text fields: where names hide in a notes column

Column rules stop at the notes field. “Spoke to Mrs Penhaligon, her son Rafe will bring the forms” puts two people in one cell, and neither sits under a Name header. Ticket bodies, call summaries and order comments behave the same way, and they often hold the only health detail in the file.

Searching works only for details with a fixed shape. Emails, card numbers, phone numbers written with a country code and some national ID formats can be found by pattern. Names, addresses and nicknames cannot. Paste a row from your masked copy into the box below to see what still has a recognisable format.

If the question doesn’t need the notes, I’d cut the column rather than read four thousand of them. When it does, cut the file down to the rows that matter before reading. Free text is also how client details reach AI tools through routes nobody planned for.

Check a row from your masked file

The example is an invented row where the notes field leaked.

The check happens on this page in your browser and the row goes nowhere. It spots card numbers that pass the Luhn test, IBANs with valid check digits, SSN-shaped and NI-shaped numbers, IPv4 addresses, common API key prefixes, emails and phone numbers that start with + and a country code. It cannot see names, so Wilmette above goes unflagged.

Consistent tokens, and a lookup table kept somewhere else

A token stands in for a value: P-8304370a instead of Aurelio Penhaligon. It must be the same on every row, and in every file you send in that conversation, or the AI cannot tell that rows 4 and 19 are one customer. Totals per person, repeat visits and churn all depend on it.

NIST’s guidance on de-identifying datasets says that when a lookup table maps people to pseudonyms, the pseudonyms must be assigned at random and the table must be highly protected. So never build a token from initials, a birth date or the last digits of an ID: anyone holding the original could rebuild it.

Under the GDPR (EU and UK law) this is pseudonymisation, and Recital 26 treats such data as still relating to an identifiable person. The key, to my mind, belongs in another folder, with fewer people able to open it. Why de-identified data isn’t automatically anonymous goes into the legal side.

Two files, stored in two places

Only the left one goes to the AI.

clients_masked.csv

name,date_of_birth,postcode,planP-8304370a,1979,021,GoldP-f2b1c7ea,1990,200,SilverP-8304370a,1979,021,GoldP-b26d8413,1958,205,NA

key_keep_apart.csv

name,tokenAurelio Penhaligon,P-8304370aBronwen Keatley,P-f2b1c7eaMatthias Quarrington,P-b26d8413

Invented rows for a fictional optician, Thistlecombe Opticians, produced by the script further down on 10 November 2026; tokens change on every run. Tools that keep tokens consistent across many files include the open-source Presidio anonymiser and masking APIs such as Protecto. The EDPB's January 2025 guidelines on pseudonymisation are still a consultation draft.

Does hashing a column make the data anonymous?

Hashing looks like a shortcut: run each email through SHA-256 and you get a string that never changes. That stability is the flaw. In July 2024 the FTC’s Office of Technology wrote that hashes still track a person, and that hashed emails and phone numbers can be reversed by guessing.

We ran that guess on a fictional number: a plain Python loop, one thread on an Apple M4 computer, hashed every 11-digit number starting 0770, the shape of a UK mobile, ten million of them, and found our target in under four seconds. Emails are easier still: hash a mailing list and compare.

NIST adds that an unkeyed hash generally gives no security, and it advises against hashing direct identifiers at all: one leaked key exposes the whole file. To my eye, a hash is the weaker pick where a random token would do. A keyed HMAC is a fallback for when no lookup table can be kept.

Delete, token or keyed hash?

Answer for one identifier column at a time.

Does the AI need to tell rows apart by person?

Will you join this file with a later export, person by person?

Can the same lookup table be kept safely until the next export?

Delete the column. Nothing left to protect or leak.
Random tokens from a lookup table, one per person. Destroy the table when the job ends if you'll never need the names back.
Reuse the same lookup table and add new people to it with fresh random tokens.
A keyed hash (HMAC-SHA-256) with a long random secret stored away from both files. Never a plain hash.

Our reading of NIST SP 800-188, sections 4.3.1 and 4.3.2, and the FTC Office of Technology post of 24 July 2024. Test: Python 3.9.6 hashlib SHA-256, one thread on an Apple M4, 10 November 2026; the time depends on the machine. For how US state privacy laws draw the line, see anonymising data before AI under US rules.

Generalising dates of birth, postcodes and ages

Remove every name and a row can still point at one person. A birth date, a postcode and a sex together can be enough. NIST calls these quasi-identifiers and measures the risk with k-anonymity: how many rows share each combination. A combination that only one row holds is a name in disguise.

The fixes are blunt and they work. Cut dates of birth to the year or a five-year band, keep the first part of a postcode and group ages above a ceiling. The US HIPAA safe harbor is one worked example: years only, three-digit ZIPs for areas over 20,000 people, everyone over 89 grouped.

For a file of a few hundred rows, I’d generalise harder than feels comfortable. Our rule of thumb, as in our Excel guide, is at least three rows per combination; it is not a legal threshold. Whether the result counts as anonymous where you work is for a lawyer to judge.

2,000 invented rows, coarsened step by step

How many rows stay alone in their combination of birth date, postcode and sex.

What the file keepsCombinationsRows aloneRows in groups under three
Full birth date, full postcode, sex1,9991,9982,000
Birth year, full postcode, sex1,9181,8391,993
Birth year, postcode district, sex1,6691,3781,894
Five-year band, postcode district, sex908307879
Five-year band, broad postcode area, sex222315

Our measurement on 10 November 2026: a seeded Python script made 2,000 fictional rows with birth dates spread evenly from 1940 to 2005, 40 postcode districts of four sectors each, grouped into eight broad areas, and two values for sex. Real files bunch up differently, so count your own. Method: k-anonymity as NIST SP 800-188 describes it. Outside HIPAA, Washington's consumer health data law also reaches health details.

Opening a CSV in Excel without losing leading zeros

Double-clicking a CSV hands it to Excel, which guesses a type for every column. Microsoft’s support page says Excel removes leading zeros, shows long numbers in scientific notation such as 1.23E+15, and keeps only 15 significant digits, so a 16-digit card or account number has its last digit turned into a zero.

That bites twice. Customer number 000412 becomes 412 and no longer matches your key file, and if you save the masked copy from Excel, the change is written into it. Import instead: Data > From Text/CSV, then Edit, then set the identifier columns to Text, the route Microsoft documents for this.

Scripts guess too. In our test, pandas read 000412 as 412 and turned a plan literally called NA into an empty cell; with dtype=str and keep_default_na=False, both survived. Before any program re-saves the file, I’d check one row against the original in a text editor.

Three values, three ways of opening the file

In the fileExcel, double-clickpandas, defaultspandas, dtype=str, keep_default_na=False
000412412412000412
41111111111111114.11111E+15, stored as 411111111111111041111111111111114111111111111111
NA (a plan name)Not covered by Microsoft's pageMissing (NaN), saved as an empty cellNA

Excel column from Microsoft Support, Keeping leading zeros and large numbers. pandas columns: our test with pandas 2.3.3, invented file, 10 November 2026. Accented names open cleanly in Excel when the UTF-8 file has a byte order mark, Microsoft notes. Data that still lives in a workbook has its own Excel walkthrough.

Masking a CSV with Python: pandas and csvkit

For a file you mask every month, a script beats hand edits: same steps each time, and a record of them. The one below uses pandas and Python’s secrets module, which Python’s docs prefer over random for tokens. Try any script on ten invented rows first; I fear a silent failure more than a crash.

It reads every column as text, drops four columns, gives each distinct name a random token, cuts the date of birth to its year and the postcode to three characters, then writes the masked copy and the key separately. Exact matching treats “A. Penhaligon” as a new person, so tidy spellings before you run it.

Without Python, csvkit does the column work: csvcut -n lists headers, csvcut -C drops columns, and csvgrep -r finds notes with Mr, Mrs or an @. It makes no tokens, so for names the script wins. If the CSV feeds an app that calls a model, masking inside the API call is the other half.

mask.py, and the same column cut with csvkit

Change the column names to yours. Dates must be written year first (1979-03-14).

import secrets
import pandas as pd
df = pd.read_csv("clients.csv", dtype=str, keep_default_na=False)
df = df.drop(columns=["email", "phone", "account_no", "notes"])
people = df["name"].unique()
key = pd.DataFrame({
    "name": people,
    "token": ["P-" + secrets.token_hex(4) for _ in people],
})
df["name"] = df["name"].map(dict(zip(key["name"], key["token"])))
df["date_of_birth"] = df["date_of_birth"].str[:4]
df["postcode"] = df["postcode"].str[:3]
df.to_csv("clients_masked.csv", index=False)
key.to_csv("key_keep_apart.csv", index=False)
csvcut -n clients.csv
csvcut -C email,phone,account_no,notes clients.csv > clients_cut.csv
csvgrep -c notes -r "Mr|Mrs|@" clients.csv

Run on 10 November 2026 with Python 3.9.6, pandas 2.3.3 and csvkit 2.2.0 against an invented four-row file. Options from the pandas read_csv reference and the csvkit csvcut documentation; token_hex from Python's secrets module.

Checking the masked file before ChatGPT, Claude or Copilot sees it

Open the masked copy in Notepad, TextEdit or any plain editor, not in a spreadsheet. What you see there is exactly what the AI receives, every comma included. Count the fields in a few rows against the header: one stray quote mark can shift a value under the wrong heading.

Then search for @, for a plus sign followed by digits, for long runs of digits, for Mr, Mrs and Dr, and for every surname in your key file. One surname left in a note undoes that person’s token everywhere. What OpenAI does with an uploaded file is the next question.

I’d keep the masked copy, dated, with the job it was made for. If anyone later asks what went to the assistant, you open a file instead of reconstructing it from memory. Retention after upload depends on the provider and plan; how Anthropic handles chats for training has its own settings.

Before you upload

Tick what you have done to the masked copy.

0 of 7

Not ready yet: the unticked checks are where leftovers usually turn up.

Our own list for a masked export. Microsoft's chat assistant has its own training rules: Copilot and your prompts. In a US lawsuit, masking raises separate questions about redaction and legal privilege.

A UK mobile number replaced by [TOKEN_1]; in our test, a hashed number like it was guessed back in under four seconds

The tool on this page, the app, or a script of your own

The redaction tool above this article takes .csv and .tsv files up to 200 KB, plus .txt, .md, .odt and .rtf. It sends the file to our server, which finds names and identifiers and returns a redacted copy with a list of what it replaced. Our security page explains how uploads are handled.

Untick a type in that list to leave it visible, then copy the result or download it. A word processor file is a different animal: an .odt or .rtf can carry edits that were only hidden, never removed, so settle those before uploading.

For a 40-row file, installing anything is overkill in my opinion. A weekly export, or one over the limit, is where the Nonimo app makes sense: the swap runs on your own computer, so the column of real names stays there. Get it from the Nonimo home page.

Which route fits your file

Tool on this page

  • A one-off CSV or TSV up to 200 KB
  • Processed on our server
  • Copy or download the result

Nonimo app

  • Regular or larger exports
  • Runs on your own computer
  • Real names come back in the reply; plans are listed here

Your own script

  • A fixed pipeline with fixed columns
  • Tokens that must last across months
  • You maintain it and its key

Limits and formats as the web tool states them. Other files need other steps: a PDF, a Word document, a slide deck and author names in file properties. US teams can see the same step for insurance agencies, CPA firms, real estate brokerages and healthcare practices.

Sources

Accessed 10 November 2026.

Related guides

Common questions

Is hashed data anonymous?

No, a plain hash is still a stable identifier. FTC technology staff note that hashed emails and phone numbers can be reversed by guessing. Deleting the column, or random tokens with a separate key, protects more.

How do I stop Excel removing leading zeros from a CSV?

Import the file rather than double-clicking it. Use Data > From Text/CSV, choose Edit and set the ID columns to Text. Excel keeps only 15 significant digits, so long numbers need Text as well.

Is masked data still personal data?

For you, yes, as long as the token key exists. Under the GDPR (EU and UK), that is pseudonymised data. Whether it is personal for an AI provider that never sees the key is a narrower legal question.

Should the same person get the same token on every row?

Yes, within one file and one conversation. Otherwise the AI counts one customer as several. Assign tokens at random from a lookup table instead of building them from initials or birth dates.

How can I mask a CSV without Excel?

Use a script or a command-line tool. The csvkit tool csvcut drops columns by name, and pandas can swap names for tokens. Read every column as text so IDs keep their leading zeros.

How should I generalise dates of birth and postcodes?

Keep the year, or a five-year band, and only the first part of the postcode. Then check that every combination of the remaining columns covers at least three rows.

Can I upload a masked CSV to ChatGPT?

Yes, once you have read the masked copy as plain text. Check the notes column for names, and keep the token key out of the folder you upload from.