Сравнение двух CSV-расшифровок по ID: как найти добавленные, удалённые и изменённые реплики
Отчёт Python по стабильному replica_id. Отдельный контроль текста, говорящего и таймкода без ложных изменений из-за перестановки строк.
Когда сравнение по строкам недостаточно
После редактуры таблица может быть пересортирована, и обычный текстовый diff покажет перемещения как множество правок. Если у каждой реплики есть стабильный ID, сравнение по ключу отвечает на другой вопрос: какие записи появились, исчезли и какие поля изменились.
Используйте before.csv и after.csv одного интервью с колонками replica_id, speaker, timecode, text. Значение ID не должно меняться при исправлении текста. Не создавайте ключ заново по текущему порядковому номеру: после вставки строки это разрушит соответствие версий.
Текстовую основу можно подготовить через Транскрибатор. Согласованные идентификаторы и CSV-версии здесь формируются отдельно; не предполагается наличие встроенного отчёта сравнения по ID.
Проверьте ключи до сопоставления
Сначала установите, что оба файла относятся к одной временной шкале и одному набору участников. Одинаковый ID в разных интервью не означает одинаковую реплику. Для нескольких интервью нужен составной ключ, например interview_id вместе с replica_id.
Скрипт ниже отклоняет пустые и повторные ID и несогласованный заголовок. Он также останавливается при лишнем или недостающем поле. Без этих проверок запись словаря по повторному ключу могла бы незаметно заменить предыдущую реплику.
Не удаляйте пробелы из ID автоматически: это может объединить два разных значения. Если ключи действительно испорчены, сначала согласуйте исправление и сохраните таблицу соответствия. Документируйте изменение правила отдельно от сравнения текста.
Запустите отчёт по трём полям
Код использует стандартную библиотеку Python без pandas. Положите два согласованных файла рядом со скриптом. Чтение utf-8-sig допускает начальную метку BOM; newline="" сохраняет корректный разбор многострочных полей.
Объединение ключей даёт полный список записей. Для общего ID отдельно сравниваются speaker, timecode и text. Один ID может дать несколько строк changed, если исправлены несколько полей. Количество строк отчёта поэтому не равно числу затронутых реплик.
Для added и removed пример выводит текст, а поле field остаётся пустым. Полные значения других полей этих записей сверяйте в исходных CSV. Результат changes.csv создаётся в режиме x и не перезаписывает прежний отчёт.
Отделите техническую разницу от смысла
Пример сравнивает строки точно. Пробел, регистр, перенос или другое представление таймкода также считаются изменением. Это полезно для контроля передачи, но не означает смысловую правку. Не нормализуйте значения перед первым отчётом, иначе можно скрыть важную деталь.
Если нужно проверить общий порядок таймкодов, используйте отдельную сортировку по времени. Сравнение ключей не выявляет перестановку реплик при неизменных полях: порядок здесь намеренно не является частью сравнения.
При смене ключа старая запись попадёт в removed, новая — в added. Из отчёта нельзя автоматически заключить, что участник произнёс новую фразу или что две записи нужно склеить. Сверьте историю разметки и запись.
Разберите спорные изменения
Проверьте смены говорящего, цифры, отрицания и уточнения по аудио. Исправление «не согласен» на «согласен» технически выглядит как замена текста, но требует внимательной проверки смысла. Не принимайте after.csv за правильную версию только из-за более поздней даты.
Для сравнения непрерывного TXT полезен отчёт difflib. Для структурированных реплик сохраните changes.csv, две версии и решения по каждому спорному ID.
Учебный тест подтвердил три состояния: added, removed и changed, а также отсутствие изменений при перестановке неизменных записей. Это проверка механики, а не точности реального интервью. Для больших таблиц нужен отдельный процесс, поскольку обе версии загружаются в память.
Учебный пример изменений по ID
Условные записи иллюстрируют значения статуса.
| ID | Разница | Статус |
|---|---|---|
| r01 | Текст изменился, ID сохранён | changed / text |
| r02 | Есть только в первой версии | removed |
| r03 | Есть только во второй версии | added |
| r04 | Строка переместилась, поля прежние | Не включается в отчёт |
Не объединяйте added и removed только по сходству текста: сначала установите историю ID и источник записи.
Скрипт отчёта двух CSV-версий
import csv
fields = ["replica_id", "speaker", "timecode", "text"]
def read(path):
result = {}
with open(path, encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
if reader.fieldnames != fields:
raise ValueError("Заголовки: " + path)
for row in reader:
if None in row or any(v is None for v in row.values()):
raise ValueError("Число полей: " + path)
key = row["replica_id"]
if not key or key in result:
raise ValueError("Пустой или повторный ID: " + path)
result[key] = row
return result
old, new = read("before.csv"), read("after.csv")
changes = []
for key in sorted(set(old) | set(new)):
if key not in old:
changes.append([key, "added", "", "", new[key]["text"]])
elif key not in new:
changes.append([key, "removed", "", old[key]["text"], ""])
else:
for field in fields[1:]:
if old[key][field] != new[key][field]:
changes.append([key, "changed", field,
old[key][field], new[key][field]])
with open("changes.csv", "x", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["replica_id", "status", "field", "before", "after"])
writer.writerows(changes)
print("Строк отчёта:", len(changes))Частые вопросы
Перестановка строк попадёт в отчёт?
Нет, если ID и сравниваемые поля не изменились. Порядок проверяется отдельно.
Почему один ID встречается несколько раз?
Каждое изменённое поле даёт отдельную строку changed.
Поздняя версия обязательно правильная?
Нет. Спорные изменения проверяйте по записи и решениям редактора.
Можно сравнить разные интервью?
Только с согласованным составным ключом; пример рассчитан на одно интервью.