В первом учебном 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

   

Имена непустые и разные; их содержательный смысл ещё нужно проверить.

Учебная схема трёх CSV-заголовков: повтор score в позициях 2 и 3, пустое имя в позиции 2, разные имена. В каждом варианте 3 поля.
Авторская учебная схема, заголовки и данные вымышлены. Сначала проверяют имена и позиции, затем преобразуют строки в словарь. Названия before/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 и сверке определений полей по кодировочной книге.

Если требуется подготовить собственную выгрузку к исследовательскому расчёту, можно обсудить консультацию по статистическому анализу. Заранее согласуйте исследовательский вопрос, структуру исходного файла и объём помощи. Успешное чтение файла само по себе не подтверждает достоверность измерений.

Источники