Shanraq.org Shanraq.org
Как читать официальную статистику
IT

Python: от данных до своей сводки Урок 42 из 56

Как читать официальную статистику

Сорок первый урок курса по Python. В одной строке официальной таблицы — пять чисел об одних и тех же ценах, и все пять верные. На какой вопрос отвечает каждое, как достать веса официальной корзины, которых в публикации нет, и почему у Всемирного банка выходит не то число, что в новостях.

Зачем это нужно

Два прошлых урока оставили долг. В тридцать девятом мы посчитали свою инфляцию и сравнили её с официальной, пообещав разобраться, из чего сложена официальная корзина. В сороковом сводка сказала «цены выросли в 1.6 раза» — и это число оказалось не тем, которое называют в новостях.

Этот урок закрывает оба долга. Работа здесь не столько программистская, сколько читательская: открыть публикацию, понять, что в ней написано, и вытащить то, что в ней есть, но не напечатано.

Сразу целиком

Файл statistika.py. Все числа из одной публикации — Бюро национальной статистики, «Индекс потребительских цен и производные показатели», август 2026 года. Ряд для смены базы — Всемирный банк, FP.CPI.TOTL.

"""Урок 41: как читать официальную статистику.

Все числа из одной публикации: Бюро национальной статистики, «Индекс
потребительских цен и производные показатели», август 2026 года. Ряд для
смены базы — Всемирный банк, FP.CPI.TOTL, база 2010 = 100.
"""

import pandas as pd

# Таблица 1, строка «Товары и услуги»: один и тот же месяц, разные базы.
INDEKSY = [
    ("к июлю 2026 года", 100.6, "что случилось за месяц"),
    ("к декабрю 2025 года", 106.4, "сколько накопилось с начала года"),
    ("к августу 2025 года", 109.8, "год к году — это «инфляция» из новостей"),
    ("к декабрю 2020 года", 185.8, "за пять лет и восемь месяцев"),
    ("январь–август к январю–августу", 110.8, "среднее к среднему — так считает Всемирный банк"),
]

# Таблица 6: прирост с начала года и вклад раздела в этот прирост.
# Названия разделов сокращены, полные — в публикации.
RAZDELY = [
    ("Продукты питания и напитки", 4.9, 1.90),
    ("Алкоголь и табак", 10.4, 0.15),
    ("Одежда и обувь", 6.6, 0.61),
    ("Жильё, вода, энергия", 8.6, 0.83),
    ("Дом и быт", 6.0, 0.33),
    ("Здравоохранение", 11.5, 0.67),
    ("Транспорт", 3.6, 0.32),
    ("Связь", 6.5, 0.31),
    ("Отдых и культура", 10.4, 0.35),
    ("Образование", 2.7, 0.07),
    ("Рестораны и гостиницы", 6.1, 0.11),
    ("Страхование и финансы", 3.0, 0.02),
    ("Личный уход и прочее", 9.6, 0.68),
]
ZAGOLOVOK = 6.4          # прирост «товаров и услуг» с начала года, таблица 6

# Таблица 3: базовая инфляция, август к августу.
BAZOVAYA = [
    ("без фруктов, овощей, бензина и угля", 110.7),
    ("без фруктов и овощей", 110.6),
    ("без них же, ЖКУ, транспорта и связи", 111.2),
]

# Таблица 2: тот же месяц по регионам, август к августу.
REGIONY = [("Республика Казахстан", 109.8),
           ("Северо-Казахстанская", 112.0),
           ("Карагандинская", 108.1)]

# Всемирный банк, FP.CPI.TOTL: индекс цен, 2010 = 100.
RYAD = pd.Series({2019: 189.30, 2020: 202.02, 2021: 218.27, 2022: 251.07,
                  2023: 287.54, 2024: 312.53, 2025: 348.12})

print("== Одна строка, пять чисел")
for base, value, question in INDEKSY:
    print(f"  {value:6.1f}  {base:32}{question}")

print()
print("== Из чего сложена корзина")
# Веса в публикации не напечатаны, но их видно: вклад = вес × прирост,
# значит вес = вклад ÷ прирост.
basket = pd.DataFrame(RAZDELY, columns=["раздел", "прирост", "вклад"])
basket["вес"] = (basket["вклад"] / basket["прирост"] * 100).round(1)
print(basket.sort_values("вес", ascending=False)[["раздел", "вес", "прирост", "вклад"]]
      .to_string(index=False))
print("  сумма весов:", round(basket["вес"].sum(), 1), "% — корзина целиком")

print()
print("== Кто поднял индекс с начала года")
top = basket.sort_values("вклад", ascending=False).head(3)
print("  три раздела из тринадцати:", ", ".join(top["раздел"]))
print("  их вклад:", round(top["вклад"].sum(), 2), "из", round(basket["вклад"].sum(), 2))
print("  сумма вкладов:", round(basket["вклад"].sum(), 2),
      "против заголовка", ZAGOLOVOK, "— расхождение от округления")

print()
print("== Базовая инфляция: то же самое без овощей и бензина")
print(f"  {REGIONY[0][1]:6.1f}  индекс целиком")
for name, value in BAZOVAYA:
    print(f"  {value:6.1f}  {name}")

print()
print("== Одна страна, разные места")
places = pd.DataFrame(REGIONY, columns=["место", "индекс"])
print(places.to_string(index=False))
print("  разброс:", round(places["индекс"].max() - places["индекс"].min(), 1), "пункта")

print()
print("== Смена базы: уровень меняется, рост — нет")
rebased = (RYAD / RYAD[2019] * 100).round(2)
print(pd.DataFrame({"2010 = 100": RYAD, "2019 = 100": rebased}).to_string())
print("  рост 2019 → 2025 по первой базе:", round(RYAD[2025] / RYAD[2019], 3))
print("  по второй базе:", round(rebased[2025] / rebased[2019], 3))

Вывод:

== Одна строка, пять чисел
   100.6  к июлю 2026 года                 — что случилось за месяц
   106.4  к декабрю 2025 года              — сколько накопилось с начала года
   109.8  к августу 2025 года              — год к году — это «инфляция» из новостей
   185.8  к декабрю 2020 года              — за пять лет и восемь месяцев
   110.8  январь–август к январю–августу   — среднее к среднему — так считает Всемирный банк

== Из чего сложена корзина
                    раздел  вес  прирост  вклад
Продукты питания и напитки 38.8      4.9   1.90
      Жильё, вода, энергия  9.7      8.6   0.83
            Одежда и обувь  9.2      6.6   0.61
                 Транспорт  8.9      3.6   0.32
      Личный уход и прочее  7.1      9.6   0.68
           Здравоохранение  5.8     11.5   0.67
                 Дом и быт  5.5      6.0   0.33
                     Связь  4.8      6.5   0.31
          Отдых и культура  3.4     10.4   0.35
               Образование  2.6      2.7   0.07
     Рестораны и гостиницы  1.8      6.1   0.11
          Алкоголь и табак  1.4     10.4   0.15
     Страхование и финансы  0.7      3.0   0.02
  сумма весов: 99.7 % — корзина целиком

== Кто поднял индекс с начала года
  три раздела из тринадцати: Продукты питания и напитки, Жильё, вода, энергия, Личный уход и прочее
  их вклад: 3.41 из 6.35
  сумма вкладов: 6.35 против заголовка 6.4 — расхождение от округления

== Базовая инфляция: то же самое без овощей и бензина
   109.8  индекс целиком
   110.7  без фруктов, овощей, бензина и угля
   110.6  без фруктов и овощей
   111.2  без них же, ЖКУ, транспорта и связи

== Одна страна, разные места
               место  индекс
Республика Казахстан   109.8
Северо-Казахстанская   112.0
      Карагандинская   108.1
  разброс: 3.9 пункта

== Смена базы: уровень меняется, рост — нет
      2010 = 100  2019 = 100
2019      189.30      100.00
2020      202.02      106.72
2021      218.27      115.30
2022      251.07      132.63
2023      287.54      151.90
2024      312.53      165.10
2025      348.12      183.90
  рост 2019 → 2025 по первой базе: 1.839
  по второй базе: 1.839

Разбор

Пять чисел — это не пять мнений

100.6  к июлю 2026 года
106.4  к декабрю 2025 года
109.8  к августу 2025 года
185.8  к декабрю 2020 года
110.8  январь–август к январю–августу

Одна строка таблицы, один месяц, одно ведомство. Никто не спорит и никто не ошибся: у индекса всегда есть база, то есть момент, с которым сравнивают, и вопрос «какая инфляция» без базы не имеет ответа.

Дальше это надо просто держать в голове. «Инфляция 9.8 %» — это август к августу. «Цены выросли на 6.4 % с начала года» — это август к декабрю. «За пять лет цены почти удвоились» — это 185.8 к декабрю 2020-го. Все три фразы об одной таблице, и ни одна не опровергает другие.

Веса не напечатаны, но их видно

В публикации есть таблица вкладов: по каждому разделу — прирост цен и вклад этого раздела в общий прирост. Весов в ней нет. Для учебной оценки используем отношение вклад ≈ вес × прирост. Опубликованные значения округлены, а официальная методика может быть цепной, поэтому полученное делением число — приблизительно восстановленный, а не официальный точный вес:

                    раздел  вес  прирост  вклад
Продукты питания и напитки 38.8      4.9   1.90
      Жильё, вода, энергия  9.7      8.6   0.83

Сумма восстановленных весов 99.7 % — полезная проверка порядка величин и согласованности с целой корзиной. Она не доказывает точность каждого веса: исходные приросты и вклады опубликованы с округлением.

Вот и ответ на вопрос тридцать девятого урока. В официальной корзине еда занимает 38.8 %, а жильё с коммунальными услугами — 9.7 %. У человека, который снимает квартиру, доля жилья в четыре-восемь раз больше, поэтому его инфляция и не обязана совпадать с этой.

Кто поднял индекс

  три раздела из тринадцати: Продукты питания и напитки, Жильё, вода, энергия, Личный уход и прочее
  их вклад: 3.41 из 6.35

Тот же разбор по вкладам, что и на своей корзине, только на уровне страны — и результат такой же по форме: половину прироста объясняют три раздела из тринадцати. Заметьте, что здравоохранение подорожало сильнее всех (11.5 %), а в приросте стоит четвёртым: вес 5.8 % не даёт ему подняться выше.

Сумма вкладов — 6.35 против заголовочных 6.4. Это не ошибка и не подлог: и то и другое напечатано округлённым до одного знака, а сумма тринадцати округлений не обязана совпасть с округлением суммы. Разница в 0.05 пункта — цена печати.

Базовая инфляция: что убирают и зачем

109.8  индекс целиком
110.7  без фруктов, овощей, бензина и угля
111.2  без них же, ЖКУ, транспорта и связи

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

Обратите внимание на направление: без овощей и бензина получается больше, а не меньше. Привычка думать, что «базовая» значит «поменьше и покрасивее», здесь не работает: в этот месяц как раз волатильные позиции тянули индекс вниз.

«Инфляция в стране» — это ещё и среднее по местам

Республика Казахстан   109.8
Северо-Казахстанская   112.0
      Карагандинская   108.1

Разброс между областями — 3.9 пункта, и это тот же самый месяц. Ведомство считает и публикует региональные индексы отдельно; страновой — их свод по весам областей. Так что «инфляция 9.8 %» усредняет не только по товарам, но и по географии.

Что делает пересмотр

Пересмотр — это когда меняется не цена, а то, чем её мерили. Две вещи меняются регулярно.

База. Нынешний индекс построен на «декабрь 2020 = 100», у Всемирного банка тот же Казахстан лежит на «2010 = 100». Арифметика простая и в программе показана целиком: на другой базе меняются все уровни, а рост между двумя датами не меняется вовсе — 1.839 и там и там. Если два источника дают разные «индексы», первое, что надо сравнить, — базы; второе — период.

Корзина. Веса пересматривают, потому что люди меняют то, что покупают. Об этом говорит сам источник — вот определение, которое Всемирный банк даёт своему показателю инфляции: «…the cost to the average consumer of acquiring a basket of goods and services that may be fixed or changed at specified intervals, such as yearly» — корзины, которая может быть закреплённой либо меняться с установленной периодичностью, например ежегодно.

Что это значит для чисел, видно из той же формулы. Если вклад — это вес × прирост, то при тех же самых ценах другие веса дают другой заголовок. Сравнить две редакции весов в этом уроке не на чем: у нас на руках одна публикация. Но знать, что заголовок зависит не только от цен, — обязательно.

Почему число Всемирного банка не равно заголовку

Последняя из пяти колонок — «январь–август к январю–августу», 110.8. Это среднее за часть года против той же части прошлого года. Показатель Всемирного банка FP.CPI.TOTL.ZG также сравнивает средние уровни, но за два полных календарных года, поэтому частичный период нельзя считать его точной копией.

Отсюда расхождение, которое иначе выглядит как чья-то ошибка. Сводка проекта берёт ряд Всемирного банка и говорит одно число, новости берут «декабрь к декабрю» и говорят другое. Оба про один год и одну страну. Разные вопросы — разные ответы.

Чего этот счёт не умеет

Восстановленные веса — это веса на сегодняшнюю публикацию, и годятся они для того, чтобы понять структуру, а не для того, чтобы пересчитывать историю. Точность у них ровно та, что позволяет округление до одного знака: вес раздела с маленьким приростом получается из деления двух коротких чисел, и ошибка там больше, чем у крупных разделов. Проверка суммой — единственное, что защищает от грубой ошибки.

Карта урока

Карта урока: одна таблица, пять вопросов, веса из вкладов

Скажите своими словами

Ответьте вслух или на бумаге, не подглядывая. Ответы — в конце урока.

  1. Почему пять чисел в одной строке официальной таблицы не противоречат друг другу?
  2. Как достать вес раздела, если он не напечатан?
  3. Что меняется, а что не меняется при смене базы индекса?

Разминка

Три коротких шага перед заданием: предсказать, дополнить, починить. Ответы — в конце урока, но сначала ответьте сами.

1. Предскажите. Что напечатает программа и одинаковыми ли будут два «роста»?

index = {2019: 189.30, 2025: 348.12}
print("рост:", round(index[2025] / index[2019], 3))
rebased = {year: value / index[2019] * 100 for year, value in index.items()}
print("на базе 2019:", {year: round(value, 2) for year, value in rebased.items()})
print("рост:", round(rebased[2025] / rebased[2019], 3))

2. Заполните пропуск. Вместо ... восстановите вес раздела из его вклада и прироста.

# жилищные услуги: прирост с начала года и вклад в общий прирост
rate, contribution = 8.6, 0.83
weight = ...
print("вес раздела:", round(weight, 1), "%")

3. Почините. Программа берёт индекс «август 2026 к декабрю 2020» и называет его годовой инфляцией.

index = 185.8
print("годовая инфляция:", round(index - 100, 1), "%")

Задание

Обязательное. По таблице вкладов восстановите веса разделов и напечатайте их по убыванию вклада. Посчитайте, скольких разделов сверху хватает, чтобы перекрыть половину прироста, и какую долю прироста они дают. Сравните сумму вкладов с заголовочным приростом и назовите расхождение. И отдельно: переведите ряд Всемирного банка на базу 2019 года и убедитесь, что рост от этого не изменился.

Ожидаемый вывод:

== Веса, восстановленные из вкладов
                    раздел  вес  прирост  вклад
Продукты питания и напитки 38.8      4.9   1.90
      Жильё, вода, энергия  9.7      8.6   0.83
      Личный уход и прочее  7.1      9.6   0.68
           Здравоохранение  5.8     11.5   0.67
            Одежда и обувь  9.2      6.6   0.61
          Отдых и культура  3.4     10.4   0.35
                 Дом и быт  5.5      6.0   0.33
                 Транспорт  8.9      3.6   0.32
                     Связь  4.8      6.5   0.31
          Алкоголь и табак  1.4     10.4   0.15
     Рестораны и гостиницы  1.8      6.1   0.11
               Образование  2.6      2.7   0.07
     Страхование и финансы  0.7      3.0   0.02
  сумма весов: 99.7 %

== Половина прироста
  разделов хватило: 3
  это: Продукты питания и напитки, Жильё, вода, энергия, Личный уход и прочее
  их доля в приросте: 53.7 %

== Заголовок и сумма вкладов
  сумма вкладов: 6.35
  заголовок: 6.4
  расхождение: 0.05 пункта — округление

== Смена базы
  база 2010: 1.839
  база 2019: 1.839
  рост не изменился: True

Готово — это: вывод совпадает построчно; веса считаются из вкладов, а не берутся откуда-то ещё; «половина прироста» ищется по накопленной сумме, а не подбирается глазами; расхождение с заголовком печатается числом.

На своих данных. Скачайте свежую публикацию ИПЦ и повторите расчёт на её числах: точные фиксированные веса должны получаться примерно одинаковыми; небольшие расхождения могут появиться из-за округления вкладов, цепной методики и пересмотров. Само различие восстановленных отношений ещё не доказывает, что веса изменились. Сравните полученные веса с теми, что в уроке, и посмотрите, какие разделы двигаются сильнее всего. Затем возьмите свою корзину из тридцать девятого урока и поставьте её веса рядом с официальными — разница между вашей инфляцией и заголовком живёт именно там.

По желанию.

  • Посчитайте, каким был бы заголовок, если бы у еды был вес 20 %, а у жилья 30 %, при тех же приростах по разделам.
  • Достаньте из публикации региональную таблицу и найдите область, которая дальше всех от страны, — в обе стороны.
  • Проверьте на базовой инфляции то же самое, что в уроке на общей: посчитайте, на сколько пунктов она отличается от индекса целиком в каждой из трёх версий.

Куда это встанет в проекте

Шаг двадцатый, и он про честность отчёта, а не про новые числа. Сводка печатает годовую инфляцию с первого шага и ни разу не сказала, какую именно.

Теперь говорит. derekkoz.about() спрашивает у Всемирного банка, что такое его же показатель, и кладёт ответ рядом с рядами — в data/indicators.json; bet.py печатает эти определения в подвале страницы, под всем, что они объясняют. Определение путешествует вместе с числом, а не живёт в чьей-то голове.

Заодно шаг показывает, как обходиться с необязательным: справка — это украшение, а не данные. Источник, который не ответил, стоит странице подвала и больше ничего: в журнале появляется строка о том, чего не хватило, а отчёт собирается. И --offline остаётся обещанием: без сети и без кэша сводка не пойдёт в сеть за абзацем текста.

Ответы

Показать ответы

На вопросы

  1. Потому что у каждого своя база: месяц к месяцу, к декабрю, год к году, к декабрю 2020-го и среднее за период к среднему. Индекс без базы не число, а половина числа.
  2. Вес = вклад ÷ прирост. Вклад раздела в общий прирост — это его вес, умноженный на его прирост, поэтому обратное действие возвращает вес. Проверка — сумма весов около 100 %.
  3. Меняются уровни: на базе 2019 года ряд начинается со ста, а не со 189.3. Не меняется рост между любыми двумя датами: 1.839 остаётся 1.839. Поэтому «индекс» из двух источников сравнивать бесполезно, пока не приведены базы, а темпы роста сравнивать можно.

К разминке

  1. Оба роста одинаковы. Смена базы — это деление всего ряда на одно и то же число, а отношение двух точек от этого не меняется.
рост: 1.839
на базе 2019: {2019: 100.0, 2025: 183.9}
рост: 1.839
  1. contribution / rate * 100. Вклад и прирост даны в процентах, а вес нужен в процентах корзины — отсюда сотня.
# жилищные услуги: прирост с начала года и вклад в общий прирост
rate, contribution = 8.6, 0.83
weight = contribution / rate * 100
print("вес раздела:", round(weight, 1), "%")
вес раздела: 9.7 %
  1. Это не год, а 68 месяцев. За весь срок цены выросли на 85.8 %; чтобы получить «в среднем за год», нужен корень, а не деление — множитель возводится в степень 12/68.
# индекс «август 2026 к декабрю 2020» — это 68 месяцев, а не год
index = 185.8
months = 68
print("за весь срок:", round(index - 100, 1), "%")
print("в среднем за год:", round(((index / 100) ** (12 / months) - 1) * 100, 1), "%")
за весь срок: 85.8 %
в среднем за год: 11.6 %

К заданию

Половину прироста перекрывают три раздела из тринадцати — 53.7 %. Ищется это по накопленной сумме: сортируем по вкладу, идём сверху и считаем, сколько шагов нужно, чтобы перейти половину. Глазами получилось бы то же самое, но только на таблице из тринадцати строк; на сотне позиций глаза кончаются.

Расхождение суммы вкладов с заголовком — 0.05 пункта. Такое расхождение надо уметь отличать от ошибки: оно меньше единицы младшего разряда и не растёт. Если бы сумма разошлась с заголовком на полпункта, дело было бы не в округлении, а в том, что мы сложили не то.

Источники

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

Проверить задание

Сначала решите и запустите в VS Code — редактор покажет ошибку на месте. Готовое решение вставьте сюда. Проверяет модель: она укажет на ошибку, но не даст готовый ответ.

Чтобы проверить, нужно войти. Войти

Комментарии (0)

Пока нет комментариев. Будьте первым.