В первом учебном CSV ниже три столбца: id, score и score. После чтения его строки через DictReader в показанном коде словарь содержит два ключа: id и score. В этом примере причина в повторяющемся заголовке: одному имени нельзя одновременно сопоставить два независимых значения в обычном словаре Python. Проверьте исходные имена до преобразования строк в записи по ключам.
Ниже приведены три вымышленных файла с одной записью A01 и значениями 1 и 2. Это учебные строки, а не результаты реального исследования. В первом файле два одинаковых имени score; во втором имя второго столбца пустое; в третьем три разных имени: id, score_before и score_after. Смысл этих значений нужно подтвердить описанием источника: нельзя автоматически решить, что они измерены «до» и «после». Пустое имя и разные имена ниже проверяются отдельно; результат repeated с двумя ключами не следует переносить на другие варианты.
Три варианта заголовка
Заголовок учебного CSV. | Что здесь требует внимания. |
|---|---|
id;score;score | Имя score встречается дважды. |
id;;score | Имя второго поля является пустой строкой. |
id;score_before;score_after | Имена непустые и разные; их содержательный смысл ещё нужно проверить. |

В каждом файле строка данных одинакова: A01;1;2. Сначала полезно получить список полей через csv.reader. В нём сохраняются обе позиции с именем score. DictReader, напротив, строит словарь по именам. Его описание и правила выбора fieldnames приведены в документации csv. Само преобразование не заменяет контроль схемы.
Полный пример: проверить заголовок перед словарём
import csv
import io
from collections import Counter
samples = {
"repeated": "id;score;score\nA01;1;2\n",
"empty": "id;;score\nA01;1;2\n",
"distinct": "id;score_before;score_after\nA01;1;2\n",
}
def inspect_header(text):
rows = list(csv.reader(io.StringIO(text), delimiter=";"))
header = rows[0]
counts = Counter(header)
duplicates = sorted(k for k, n in counts.items() if n > 1)
empty = [i + 1 for i, name in enumerate(header) if name == ""]
width_ok = all(len(row) == len(header) for row in rows[1:])
return duplicates, empty, width_ok
for name, text in samples.items():
print(name, inspect_header(text))
lost = list(csv.DictReader(io.StringIO(samples["repeated"]),
delimiter=";"))[0]
print("dict_after_repeated =", lost)
assert lost == {"id": "A01", "score": "2"}
assert inspect_header(samples["repeated"]) == (["score"], [], True)
assert inspect_header(samples["empty"]) == ([], [2], True)
assert inspect_header(samples["distinct"]) == ([], [], True)
Вывод для приведённого учебного примера:
repeated (['score'], [], True)
empty ([], [2], True)
distinct ([], [], True)
dict_after_repeated = {'id': 'A01', 'score': '2'}
В учебном повторяющемся варианте DictReader вернул {"id": "A01", "score": "2"}. Первое значение score, строка "1", не получило отдельного ключа в словаре. Оно осталось в сохранённом исходном CSV, но восстановить его из этого словаря уже невозможно. Это наблюдаемый результат именно показанного кода и входных данных.
Три независимые проверки
Проверка | Результат и ограничение |
|---|---|
Повторы имён | Counter считает точные совпадения. У repeated найдено score. Регистр и пробелы не нормализуются. |
Пустые имена | У empty обнаружена позиция 2 при нумерации от единицы. Имя из одних пробелов код не считает пустой строкой. |
Число полей | У всех трёх файлов width_ok=True: количество полей совпало с длиной заголовка. Это не отменяет повтор и пустое имя. |
Показанный код рассчитан на непустой CSV с заголовком и заранее выбранным разделителем «;». Для произвольного файла отдельно обработайте отсутствие записей, согласуйте кодировку и правила кавычек. Диагностика заголовка не подтверждает допустимость отрицательных значений, уникальность ID или единицы измерения.
Исправляйте по схеме источника, сохраняя позиции
- Остановите преобразование в словарь, если обнаружен повтор имени или пустое имя.
- Сохраните полный исходник и позиции столбцов, например score во втором и третьем столбцах.
- Найдите описание экспорта или уточните назначение каждого столбца.
- Составьте явное соответствие исходной позиции и подтверждённого имени.
- После согласованного исправления снова проверьте имена, ширину строк и значения.
Названия score_before и score_after в третьем учебном файле заданы автором. Их нельзя автоматически присваивать двум неизвестным score реальной выгрузки. Даже имена score_1 и score_2 только устраняют техническое совпадение и не объясняют переменные. Журнал преобразования должен показывать, откуда взялось каждое название.
Проверки в примере сравнивают исходные строки буквально. Если проект требует считать «Score» и «score» одним именем либо убирать пробелы по краям, заранее задайте это правило и проверьте, не создаст ли нормализация новые совпадения. Не меняйте исходный заголовок молча.
Итог: одинаковая ширина строк не гарантирует корректную схему. В трёх учебных файлах по три поля, но лишь третий проходит наши проверки на точные повторы и пустые строки в именах. Затем можно переходить к проверке типов при импорте CSV и сверке определений полей по кодировочной книге.
Если требуется подготовить собственную выгрузку к исследовательскому расчёту, можно обсудить консультацию по статистическому анализу. Заранее согласуйте исследовательский вопрос, структуру исходного файла и объём помощи. Успешное чтение файла само по себе не подтверждает достоверность измерений.