Pandas: работа с таблицами КБК
Зачем и для кого
Инструкция для аналитиков и разработчиков, которые склеивают фактические выгрузки (исполнение, закупки, реестры) со справочником наименований КБК: нужно единообразно привести код к 20 знакам в виде строки, не потерять ведущие нули и отловить строки без пары в справочнике. Смысл кода и тип (доход / расход / источники финансирования дефицита) — в глоссарии КБК; разбор позиций по полям года — в расшифровке КБК.
Входные данные
- Таблица операций с колонкой реквизита КБК (как в выгрузках бюджетного контура или ЕИС): часто смесь строк, целых и float (
1.23e+19), иногда пробелы по краям и пустые значения. Код может быть записан с пробелами между группами цифр (как в печатных формах), например386 0110 47 4 01 92072 611 12— перед join такие значения приводят к сплошной строке из 20 цифр; если после удаления пробелов получается не 20 знаков, это сигнал проверить поле (не тот реквизит, обрезка колонки, лишняя группа). - Справочник КБК на тот же финансовый год и тот же тип кода, что и в факте — машиночитаемые наборы ищите на стороне открытых данных Минфина (загрузка — см. how-to по API Минфина); при работе только с порталом budget.gov.ru ориентируйтесь на поля паспортов наборов и доступ к API.
- Зафиксируйте год наблюдения и дату выгрузки — перечень кодов и расшифровки меняются между годами (обзор классификации).
Инструменты
- Python 3.10+ и pandas 2.x (
pip install "pandas>=2.0"). - По желанию — pyarrow для parquet; для больших CSV —
dtype=strпри чтении колонки с КБК.
Шаги
- Импортируйте факт и справочник в
DataFrame; колонку с кодом из факта не превращайте заранее вfloat, если есть риск потери точности для длинных целых — читайте как строку (dtype,convertersилиengine="python"при необходимости). - Нормализуйте значения в одну строковую колонку
kbk: обрежьте края, удалите все пробельные символы внутри значения (\s+→ пусто), затем артефакт.0у чисел из Excel; пустое иnan— в пропуск; оставшиеся строки из цифр длиной не больше 20 приведите к длине 20 черезstr.zfill(20); если после снятия пробелов цифр больше 20 или встречаются нецифровые символы — отложите строку в контроль ошибок. - В справочнике приведите ключ к тому же виду (строка длины 20); убедитесь, что не смешиваете разные типы КБК в одном
merge. - Выполните
merge(..., how="left", indicator=True)поkbk; посчитайте долюleft_only, выгрузите их в контрольный файл. - Проверьте суммы до и после join по ключу периода (год, месяц) — join не должен дублировать строки; при дубликатах в справочнике сначала дедуплицируйте справочник по
kbk.
Воспроизводимый пример
Минимальный синтетический пример без сети (в проекте подставьте свои CSV и колонки):
import pandas as pd
def normalize_kbk(series: pd.Series, *, width: int = 20) -> pd.Series:
"""Края, все пробелы внутри кода, хвост .0; только цифры, длина <= width -> zfill."""
s = series.astype("string").str.strip()
s = s.replace({"": pd.NA})
s = s.str.replace(r"\s+", "", regex=True) # "386 0110 47 …" -> без пробелов
s = s.str.replace(r"\.0$", "", regex=True) # хвост Excel float в CSV
out = pd.Series(pd.NA, index=series.index, dtype="string")
m = s.notna()
digits = m & s.str.fullmatch(rf"[0-9]{{1,{width}}}", na=False)
too_long = m & s.str.fullmatch(rf"[0-9]{{{width + 1},}}", na=False)
if too_long.any():
raise ValueError(
"После удаления пробелов длина КБК > 20 цифр — проверьте поле и лишние группы: "
+ repr(s.loc[too_long].head(2).tolist())
)
bad = m & ~digits & ~too_long & (s.str.len() > 0)
if bad.any():
raise ValueError("Нецифровые символы после очистки: " + repr(s.loc[bad].head(2).tolist()))
out.loc[digits] = s.loc[digits].str.zfill(width)
return out
facts = pd.DataFrame(
{
"kbk_raw": [
"07104010201081000110",
" 07104010201081000110 ",
"07104010201081000110.0",
"071 04 01 0201 0810 0011 0", # те же 20 цифр, с пробелами между группами
],
"amount": [1_000_000.0, 2_000_000.0, 500_000.0, 250_000.0],
}
)
dict_kbk = pd.DataFrame(
{
"kbk": ["07104010201081000110"],
"kbk_name": ["Условное наименование строки (пример)"],
}
)
facts["kbk"] = normalize_kbk(facts["kbk_raw"])
merged = facts.merge(dict_kbk, on="kbk", how="left", indicator=True)
orphan_rows = merged.loc[merged["_merge"] == "left_only"]
print(merged.drop(columns=["_merge"]))
print("Строк без расшифровки:", len(orphan_rows))
Проверка результата
- Длина ключа: все непустые
kbk— ровно 20 цифр; значения вида386 0110 47 …сначала дают 22 цифры без пробелов — это не нормализуемый КБК без отдельного разбора источника. - Сверка сумм:
facts["amount"].sum()совпадает сmerged["amount"].sum()при отсутствии дубликатов в справочнике. - Контроль
left_only: доля нулевая или объяснена (новые коды года, другой тип КБК, срез по ведомству). - Смысл: расшифровка из справочника того же года и того же типа кода, что и в факте — иначе совпадения по строке будут вводящими в заблуждение.
Ограничения и типовые ошибки
- Пробелы «для читаемости» внутри кода не должны попадать в ключ как есть — всегда снимайте
\s+до проверки длины; не путайте с разделителями тысяч в суммах в соседней колонке. - Числовой тип в Excel/CSV превращает КБК в научную запись или обрезает точность — храните и джойните как строку (заметки в глоссарии).
mergeразмножает строки, если в справочнике несколько строк на одинkbk— сначалаdrop_duplicates(subset=["kbk"], keep="first")или осмысленная агрегация.- Смешение доходного и расходного кода в одной колонке без поля «тип» даёт ложные наименования — разделяйте контуры до join.
- Разные годы без переходных таблиц дают «пропавшие» коды — ведите версию справочника в метаданных пайплайна.
Связанные страницы
- КБК (глоссарий) — 20 знаков, типы кодов, ведущие нули.
- Расшифровка КБК — позиции внутри кода и справочники Минфина.
- Открытые данные Минфина России — машиночитаемые классификаторы по годам.
- API и открытые данные Минфина — загрузка справочников и паспортов наборов.
- Обзор бюджетной классификации — связь КБК с КВР, КОСГУ и формами.
- Работа с API budget.gov.ru — если факт или справочник берётся с портала ГИИС «Электронный бюджет».