
Коды товаров в pandas: как не потерять ведущие нули при загрузке


Почему код товара нельзя читать как число
Когда столбец содержит идентификатор фиксированной длины, например 001234, перед нами не количество и не измерение, а метка объекта. Для такой строки важна каждая позиция, включая ведущие нули. Если pandas прочитает этот столбец как число, значение превратится в 1234, и смысл кода изменится: это уже другой идентификатор.
Проблема становится заметнее, если в столбце встречаются пропуски. Тогда библиотека может подобрать числовой тип с поддержкой пропусков, и коды начинают выглядеть как 1234.0 или 17.0. Для аналитика это не просто неудобный формат. После такой загрузки уже нельзя отличить исходный 000017 от 17, если только длина не хранится отдельно и вы не восстанавливаете ее вручную.
Поэтому для товарных кодов полезно заранее принять простое правило: если по столбцу не предполагаются арифметические действия, его лучше читать как текст. У pandas для этого есть прямой механизм: в read_csv можно заранее задать тип нужной колонки через параметр dtype. Это избавляет от догадок парсера и сохраняет исходную форму значения.
Схема типов нужна до чтения файла, а не после
Самая частая ошибка выглядит так: сначала файл загружают «как получится», потом замечают, что нули пропали, и начинают приводить столбец обратно к строке. Но обратное преобразование уже не возвращает потерянные символы. Если 001234 стало 1234, перевод числа в строку даст лишь "1234".
Надежнее определить схему сразу: какие поля являются кодами, какие — числовыми значениями, какие — датами. Для нашей задачи это обычно означает словарь dtype, где код товара объявлен строковым типом, а числовые поля оставлены числовыми. Тогда столбец с идентификатором не будет интерпретирован как число даже в тех строках, где код состоит только из цифр.
Еще одна тонкость — обработка пустых ячеек. В документации read_csv указано, что строки, распознанные как пропуски, будут считаться отсутствующими значениями, а поведение регулируют na_values и keep_default_na. Для кодов это важно: нужно решить, хотите ли вы на этапе чтения сохранить пустую ячейку как пустую строку или сразу превратить ее в пропуск. Оба варианта допустимы, если они выбраны сознательно, а не получились случайно.
Стоит помнить и про converters. Если вы задаете и dtype, и converters для одной и той же колонки, преобразователи имеют приоритет. Это полезно, когда формат сложный, но в простой задаче с фиксированной длиной чаще достаточно явного строкового типа и отдельной проверки после загрузки.
import pandas as pd
df = pd.read_csv(
"products.csv",
dtype={"product_code": "string", "qty": "Int64"},
keep_default_na=False
)
df["product_code"] = df["product_code"].replace("", pd.NA)Такой шаблон удобен тем, что код загружается как текст без потери нулей, а пустые значения вы отмечаете уже своим правилом.
После загрузки надо проверять не только тип, но и длину кода
Строковый тип сам по себе решает только половину задачи. Он сохраняет форму значения, но не доказывает, что код корректный. Если поставщик прислал 12345 вместо 012345, pandas честно сохранит строку "12345", а ошибка останется в данных. Поэтому следующий шаг — валидация длины и состава символов.
Для кодов фиксированной длины удобно проверять три состояния по отдельности: корректный код, пропуск, некорректный код. Это полезнее, чем просто искать «ошибки в столбце». Пропуск означает, что идентификатор отсутствует. Некорректная длина или лишние символы означают, что идентификатор есть, но не соответствует ожидаемому формату. Для дальнейшей сверки и исправления это разные случаи.
Если код должен состоять ровно из шести цифр, можно проверить длину через str.len(), а полный формат — через str.fullmatch(r'\d{6}'). В результате вы получите не общий сигнал «что-то не так», а список конкретных строк для проверки источника. Это особенно важно перед объединением таблиц: даже один пропущенный ноль ломает совпадение ключей и дает пустые результаты в merge.
df["code_len"] = df["product_code"].str.len()
df["is_valid_code"] = df["product_code"].str.fullmatch(r"\d{6}", na=False)
missing_count = int(df["product_code"].isna().sum())
invalid_count = int((df["product_code"].notna() & ~df["is_valid_code"]).sum())Здесь пропуски и ошибки считаются раздельно, поэтому итог легко объяснить и проверить руками.
Учебный пример: как одна неверная загрузка меняет коды
Учебный пример. Представим CSV-файл с шестизначными кодами товаров. В нем шесть строк: четыре корректных кода, один код без ведущего нуля и одна пустая ячейка. Среда выполнения — Python с pandas в Jupyter Notebook.
from io import StringIO
import pandas as pd
csv_text = """product_code,name,qty
001234,Чашка,12
010045,Ложка,5
120000,Термос,3
000017,Набор,8
12345,Фильтр,4
,Ремень,2
"""
naive = pd.read_csv(StringIO(csv_text))
safe = pd.read_csv(
StringIO(csv_text),
dtype={"product_code": "string", "qty": "Int64"},
keep_default_na=False
)
safe["product_code"] = safe["product_code"].replace("", pd.NA)
safe["is_valid_code"] = safe["product_code"].str.fullmatch(r"\d{6}", na=False)
valid_count = int(safe["is_valid_code"].sum())
missing_count = int(safe["product_code"].isna().sum())
invalid_count = int((safe["product_code"].notna() & ~safe["is_valid_code"]).sum())Если посмотреть на naive, первая и четвертая строки уже потеряли смысл: 001234 стало 1234.0, а 000017 — 17.0. Пятая строка 12345 выглядит почти так же «нормально», хотя это дефект длины. Получается опасная ситуация: корректные коды испорчены, а ошибочный код визуально почти не выделяется.
В safe исходная форма сохраняется. Проверка дает понятный итог: из 6 строк корректны 4, пропущен 1 код, еще 1 код некорректен по длине. Это проверяемый результат: 4 + 1 + 1 = 6, сумма категорий сходится с числом строк.
| Строка | Исходный код | После наивной загрузки | После безопасной загрузки |
|---|---|---|---|
| 1 | 001234 | 1234.0 | 001234 |
| 2 | 010045 | 10045.0 | 010045 |
| 4 | 000017 | 17.0 | 000017 |
| 5 | 12345 | 12345.0 | 12345 |
Практическая польза такого подхода в том, что вы не «чините» данные на глаз, а сразу разделяете задачи: сохранить форму кода, найти реальные ошибки источника и только потом принимать решение по исправлению. Если поставщик действительно имел в виду 012345, это должно подтверждаться внешним правилом, а не угадываться из длины строки.
Как сверить выгрузку, чтобы нули не потерялись на обратном пути
Даже если загрузка выполнена правильно, полезно проверить и выгрузку. На практике потеря нулей часто обнаруживается не в момент чтения, а позже: после сохранения промежуточного CSV, передачи файла коллеге или повторного чтения в другой ноутбук. Поэтому надежная схема включает круговую проверку: сохранить файл, снова открыть его с той же схемой типов и сравнить коды построчно.
export_text = safe[["product_code", "name", "qty"]].to_csv(index=False)
roundtrip = pd.read_csv(
StringIO(export_text),
dtype={"product_code": "string", "qty": "Int64"},
keep_default_na=False
)
roundtrip["product_code"] = roundtrip["product_code"].replace("", pd.NA)
same_codes = safe["product_code"].fillna("<NA>").equals(
roundtrip["product_code"].fillna("<NA>")
)Если same_codes равно True, это не доказывает качество всех данных, но подтверждает важную вещь: ваши ведущие нули сохранились после записи и повторного чтения. Дальше стоит дополнительно сверить число пропусков, число некорректных кодов и длину всех непустых значений. Это быстрые проверки, которые ловят большую часть ошибок до объединения с каталогом или формирования отчета.
Короткий рабочий чек-лист можно свести к четырем вопросам: какой тип должен быть у колонки до чтения; как должны обрабатываться пустые значения; какой шаблон у корректного кода; совпал ли столбец после выгрузки и повторной загрузки. Если на каждый вопрос есть явный ответ в коде, ведущие нули перестают быть случайной проблемой и становятся обычной частью схемы данных.
Если хотите потренировать этот подход в учебной среде, на странице курса в каталоге «Аналитика данных на Python: pandas, numpy, визуализация» приведена программа с темами по загрузке и выгрузке данных в pandas, чтению и записи Excel, JSON и CSV, а также по первичной обработке и очистке данных. При выборе обучения по этой теме полезно самому проверить три вещи: есть ли в программе работа со схемой типов при чтении файлов, будет ли самостоятельная практика на реальных таблицах и как вы будете проверять результат — не глазами, а повторной загрузкой и валидацией кодов.





















