Как подготовить копию CSV для ИИ: оставить нужные столбцы и заменить имена кодами на Mac
Создаём отдельный CSV только с row_id и topic, не перезаписываем исходник и вручную проверяем свободный текст перед передачей ИИ.
- Система
- macOS 27.0.1
- Программы
- Python 3.12.14
Подробности проверки
- macOS 27.0.1 (build 26A434), arm64
- Python 3.12.14
- только стандартная библиотека.
- На синтетическом fixture проверены точный output, неизменность исходника, разные row_id для одинаковых имён, отказы при существующем output, одинаковых путях, неверных заголовках и короткой строке.
- Свободный topic переносится дословно и требует ручного просмотра.
Передавать ИИ всю выгрузку только ради одного столбца рискованно и обычно не нужно. Для задачи вроде группировки тем достаточно сделать отдельную копию: убрать name, email и city, а вместо исходной личности дать каждой строке технический row_id. Исходный CSV при этом остаётся нетронутым.
Ниже — воспроизводимый способ для Mac с уже установленным Python 3.9 или новее. Скрипт использует только стандартную библиотеку Python, принимает учебный CSV с заголовками name,email,city,topic и создаёт новый файл только с row_id,topic. Режим записи x не позволяет случайно заменить существующий результат.
Что этот способ меняет — и чего не обещает
- name, email и city не попадают в новую копию.
- row_id создаётся заново по порядку строк: R0001, R0002 и далее.
- Одинаковые имена в разных строках получают разные row_id. Скрипт не решает, относятся ли строки к одному человеку.
- Таблица соответствий имён и кодов не создаётся и не сохраняется.
- topic переносится дословно, поэтому его нужно прочитать вручную перед отправкой.
- Результат не следует называть автоматически анонимным: свободный текст может содержать имя, телефон, адрес, номер заказа или другую чувствительную деталь.
| Поле исходника | Действие | Поле результата |
|---|---|---|
| name | исключить | — |
| исключить | — | |
| city | исключить | — |
| порядок строки | создать локальный код | row_id |
| topic | оставить после ручного просмотра | topic |

Что потребуется
- Mac и приложение «Терминал».
- Уже доступная команда python3 версии 3.9 или новее. Ничего устанавливать ради инструкции не нужно.
- Отдельная копия CSV, а не единственный оригинал.
- CSV в UTF-8 с запятой как разделителем, двойными кавычками для сложных значений и точными заголовками name,email,city,topic в этом порядке.
Шаг 1. Проверьте Python и подготовьте папку
В Терминале проверьте путь и версию Python. Если command -v ничего не выводит, версия ниже 3.9 или macOS предлагает установить дополнительные инструменты, остановитесь: этот гайд не требует и не описывает установку. Затем создайте отдельную учебную папку и перейдите в неё.
command -v python3
python3 --version
mkdir csv-minimize-check
cd csv-minimize-checkДля рабочего файла используйте другое имя папки и сначала скопируйте CSV туда через Finder. Не открывайте скрипту оригинал, если он является единственной копией.
Шаг 2. Создайте учебный CSV
Сохраните следующий текст как sample.csv в UTF-8. Здесь имя «Анна Соколова» повторяется намеренно, но строки имеют разные email, city и topic. Скрипт не будет считать имя уникальным ключом и не станет связывать эти строки.
name,email,city,topic
Анна Соколова,anna@example.test,Казань,Нужен план публикаций на неделю
Иван Петров,ivan@example.test,Пермь,"Сводная таблица: продажи, возвраты"
Анна Соколова,anna2@example.test,Омск,Черновик FAQ по доставкеШаг 3. Сохраните скрипт с белым списком
Сохраните код ниже как prepare_csv_copy.py в той же папке. EXPECTED_FIELDS фиксирует контракт входа, а OUTPUT_FIELDS — единственные два столбца результата. Скрипт сначала полностью читает и проверяет CSV, затем открывает новый выходной файл в режиме x. Если такой файл уже есть, скрипт сообщает об ошибке, а существующие байты не заменяются.
#!/usr/bin/env python3
"""Create a minimized CSV copy with row-local identifiers."""
from __future__ import annotations
import csv
import io
import sys
from pathlib import Path
EXPECTED_FIELDS = ["name", "email", "city", "topic"]
OUTPUT_FIELDS = ["row_id", "topic"]
class CopyError(Exception):
pass
def create_copy(source: Path, destination: Path) -> int:
source = source.expanduser()
destination = destination.expanduser()
try:
source_path = source.resolve(strict=True)
except OSError as error:
raise CopyError(f"не удалось открыть исходный файл: {error}") from error
destination_path = destination.resolve(strict=False)
if source_path == destination_path:
raise CopyError("исходный и выходной пути должны различаться")
if not source_path.is_file():
raise CopyError("исходный путь должен указывать на обычный файл")
if not destination_path.parent.is_dir():
raise CopyError(f"папка результата не существует: {destination_path.parent}")
minimized: list[dict[str, str]] = []
try:
with source_path.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(
stream,
delimiter=",",
quotechar='"',
doublequote=True,
skipinitialspace=False,
strict=True,
)
if reader.fieldnames != EXPECTED_FIELDS:
raise CopyError(
"ожидались заголовки строго в порядке: "
+ ",".join(EXPECTED_FIELDS)
)
for row_number, row in enumerate(reader, 2):
if None in row or any(value is None for value in row.values()):
raise CopyError(
f"логическая строка {row_number}: число полей не совпадает с заголовками"
)
minimized.append(
{
"row_id": f"R{len(minimized) + 1:04d}",
"topic": row["topic"],
}
)
except UnicodeError as error:
raise CopyError(f"CSV не декодируется как UTF-8: {error}") from error
except csv.Error as error:
raise CopyError(f"ошибка CSV: {error}") from error
except OSError as error:
raise CopyError(f"не удалось прочитать исходный CSV: {error}") from error
buffer = io.StringIO(newline="")
writer = csv.DictWriter(
buffer,
fieldnames=OUTPUT_FIELDS,
delimiter=",",
quotechar='"',
doublequote=True,
lineterminator="\n",
extrasaction="raise",
)
writer.writeheader()
writer.writerows(minimized)
try:
with destination_path.open("x", encoding="utf-8", newline="") as output:
output.write(buffer.getvalue())
except FileExistsError as error:
raise CopyError(
f"выходной файл уже существует и не будет заменён: {destination_path}"
) from error
except OSError as error:
raise CopyError(f"не удалось создать выходной CSV: {error}") from error
return len(minimized)
def main(arguments: list[str]) -> int:
if len(arguments) != 3:
print(
"Использование: python3 prepare_csv_copy.py ИСХОДНИК.csv КОПИЯ.csv",
file=sys.stderr,
)
return 2
try:
count = create_copy(Path(arguments[1]), Path(arguments[2]))
except CopyError as error:
print(f"ОШИБКА: {error}", file=sys.stderr)
return 1
print(f"Готово: строк {count}; создан новый файл: {arguments[2]}")
print("Перед передачей вручную просмотрите весь столбец topic.")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))Параметр newline="" передаёт обработку переводов строк модулю csv. DictReader связывает значения с заголовками, но код дополнительно требует точный набор полей и останавливается при лишнем или пропущенном значении. Кодировка входа utf-8-sig принимает обычный UTF-8 и вариант с BOM; выход всегда создаётся в UTF-8.
Шаг 4. Создайте и сравните новую копию
- Запустите скрипт с отдельными путями входа и выхода.
- Убедитесь, что он сообщает о трёх строках и напоминает просмотреть topic.
- Выведите результат и сравните его с ожидаемыми строками ниже.
- Проверьте, что sample.csv по-прежнему содержит четыре исходных столбца.
python3 prepare_csv_copy.py sample.csv share-copy.csv
cat share-copy.csv
cat sample.csvrow_id,topic
R0001,Нужен план публикаций на неделю
R0002,"Сводная таблица: продажи, возвраты"
R0003,Черновик FAQ по доставкеУ двух строк с именем «Анна Соколова» получились R0001 и R0003, потому что код относится к строке, а не к личности. Если изменить порядок строк или обработать другой файл, row_id изменится. Не используйте его для объединения наборов данных или восстановления личности.

Шаг 5. Обязательно прочитайте topic вручную
Удаление столбцов не очищает свободный текст. Откройте share-copy.csv в текстовом редакторе или табличной программе и последовательно прочитайте каждую ячейку topic. Ищите реальные имена, email, телефоны, адреса, номера документов и заказов, внутренние ссылки, секреты, точные даты и редкие сочетания деталей, по которым можно узнать человека.
Если такая деталь нужна задаче, не маскируйте её автоматически по догадке. Остановитесь и решите, можно ли удалить строку, обобщить формулировку в отдельной копии или вообще не передавать материал. После любой ручной правки снова просмотрите весь файл: изменение topic уже находится вне проверки этого скрипта.
Шаг 6. Проверьте защитные отказы
Повторите команду с тем же share-copy.csv. Скрипт должен завершиться с ошибкой «выходной файл уже существует» и не менять файл. Затем попробуйте передать sample.csv одновременно как вход и выход: скрипт должен отказать до записи. Эти проверки важны перед работой с настоящей копией.
python3 prepare_csv_copy.py sample.csv share-copy.csv
echo $?
python3 prepare_csv_copy.py sample.csv sample.csv
echo $?В обоих случаях ожидается код 1. Если нужно создать результат заново, сначала проверьте содержимое и выберите другое имя, например share-copy-2.csv. Такой порядок сохраняет предыдущую версию для сравнения и не требует удалять её вслепую.
Шаг 7. Примените способ к рабочей копии
- Сделайте отдельную копию исходного файла через Finder.
- Проверьте, что заголовки и CSV-диалект совпадают с принятым контрактом. Другие названия столбцов изменяйте только осознанно в EXPECTED_FIELDS.
- Оставьте в OUTPUT_FIELDS и словаре внутри minimized.append только сведения, которые действительно нужны выбранной задаче.
- Запустите скрипт с новым именем результата.
- Вручную прочитайте весь topic и только после этого передавайте созданную копию ИИ.
Ограничения
- Это минимизация полей для конкретной задачи, а не универсальная анонимизация и не юридическая оценка допустимости передачи.
- Скрипт не ищет персональные данные, секреты и косвенные идентификаторы внутри topic.
- row_id стабилен только внутри одной созданной копии и не обозначает уникального человека.
- Скрипт рассчитан на конкретный CSV-диалект и хранит выбранные строки в памяти до записи. Для очень больших файлов нужен другой проверенный процесс.
- Имя самого файла и название папки тоже могут раскрывать контекст; перед отправкой проверьте их отдельно.
- Даже минимальный CSV может быть чувствительным. Передавайте его только тому инструменту и в тот контекст, которые разрешены правилами вашей организации.
Как отменить изменения
Исходный CSV скрипт не меняет. Чтобы отменить результат, сначала выполните pwd и убедитесь, что находитесь в созданной учебной папке. Затем переместите в Корзину только созданные вами share-copy.csv и prepare_csv_copy.py. Не используйте рекурсивное удаление, домашнюю папку, переменную пути или широкий шаблон. Если результат нужно сохранить для аудита, оставьте его локально и не отправляйте.
Когда копия готова
Считайте технический этап завершённым, когда результат содержит только row_id и topic, точный учебный пример воспроизводится, существующий файл не заменяется, вход и выход различаются, а весь свободный текст просмотрен человеком. Это уменьшает объём передаваемых данных, но окончательное решение об отправке зависит от содержания и правил вашей работы.