Shanraq.org Shanraq.org
Своя инфляция: ваша корзина против официальной
IT

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

Своя инфляция: ваша корзина против официальной

Тридцать девятый урок курса по Python. «Инфляция 8 %, а у меня всё подорожало вдвое» — обе фразы бывают правдой одновременно, и разводят их веса. Корзина, доля каждой позиции в расходах, вклад в рост — и почему простое среднее по позициям обещает 84.6 % там, где корзина выросла на 69.8 %.

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

Официальный индекс считает корзину среднего домохозяйства. Вашей корзины среди них нет: у вас своя аренда, свой проезд, свои дети и своя кошка. Поэтому «официальные 65 %» и «у меня выросло на 70 %» — не спор, а два разных вопроса.

Посчитать свою инфляцию можно за вечер, если есть чеки. Считается она не средним по ценникам, а весами: сколько каждая позиция занимает в ваших деньгах.

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

Файл korzina.py. Официальный множитель здесь настоящий — индекс потребительских цен Казахстана за 2019–2024 годы по данным Всемирного банка. Сама корзина — пример: подставьте свои чеки, арифметика не изменится.

"""Урок 39: своя инфляция — ваша корзина против официальной.

Официальный множитель здесь настоящий: индекс потребительских цен Казахстана
за 2019–2024 годы по данным Всемирного банка. Корзина — пример: подставьте
свои чеки, арифметика не изменится.
"""

import pandas as pd

# Официальный индекс (2010 = 100) на начало и конец пятилетки.
OFFICIAL_2019 = 189.30
OFFICIAL_2024 = 312.53

# Пример корзины: что покупается за месяц, по какой цене тогда и сейчас.
basket = pd.DataFrame(
    [
        ("хлеб, буханка", 20, 90, 180),
        ("молоко, литр", 15, 260, 480),
        ("яйца, десяток", 8, 350, 1000),
        ("мясо, кг", 4, 1600, 3200),
        ("проезд, поездка", 40, 80, 100),
        ("интернет, месяц", 1, 5000, 6500),
        ("аренда, месяц", 1, 90000, 150000),
    ],
    columns=["позиция", "сколько", "было", "стало"],
)

# Стоимость позиции в корзине тогда и сейчас.
basket["тогда"] = basket["сколько"] * basket["было"]
basket["сейчас"] = basket["сколько"] * basket["стало"]
# Вес — доля позиции в расходах базового года. Веса дают сумму 1.
basket["вес"] = basket["тогда"] / basket["тогда"].sum()
basket["выросла в"] = basket["стало"] / basket["было"]

print("== Корзина")
print(basket[["позиция", "сколько", "было", "стало", "выросла в"]].round(3).to_string(index=False))

print()
print("== Веса: из чего на самом деле состоит ваш месяц")
weights = basket[["позиция", "вес"]].sort_values("вес", ascending=False)
print(weights.assign(**{"вес": (weights["вес"] * 100).round(1)}).to_string(index=False))

print()
print("== Три ответа на один вопрос")
personal = basket["сейчас"].sum() / basket["тогда"].sum()
simple = basket["выросла в"].mean()
official = OFFICIAL_2024 / OFFICIAL_2019
print("  корзина целиком:", round(personal, 3), "→", round((personal - 1) * 100, 1), "%")
print("  простое среднее по позициям:", round(simple, 3), "→", round((simple - 1) * 100, 1), "%")
print("  официальный индекс:", round(official, 3), "→", round((official - 1) * 100, 1), "%")

print()
print("== Кто поднял вашу корзину")
basket["вклад"] = basket["вес"] * (basket["выросла в"] - 1)
contribution = basket[["позиция", "вес", "выросла в", "вклад"]].sort_values("вклад", ascending=False)
print(contribution.round(3).to_string(index=False))
print("  сумма вкладов:", round(basket["вклад"].sum(), 3), "— это и есть рост корзины")

print()
print("== Расходы за месяц")
print("  тогда:", basket["тогда"].sum(), "тенге")
print("  сейчас:", basket["сейчас"].sum(), "тенге")
print("  разница:", basket["сейчас"].sum() - basket["тогда"].sum(), "тенге в месяц")

Выводит:

== Корзина
        позиция  сколько  было  стало  выросла в
  хлеб, буханка       20    90    180      2.000
   молоко, литр       15   260    480      1.846
  яйца, десяток        8   350   1000      2.857
       мясо, кг        4  1600   3200      2.000
проезд, поездка       40    80    100      1.250
интернет, месяц        1  5000   6500      1.300
  аренда, месяц        1 90000 150000      1.667

== Веса: из чего на самом деле состоит ваш месяц
        позиция  вес
  аренда, месяц 79.6
       мясо, кг  5.7
интернет, месяц  4.4
   молоко, литр  3.4
проезд, поездка  2.8
  яйца, десяток  2.5
  хлеб, буханка  1.6

== Три ответа на один вопрос
  корзина целиком: 1.698 → 69.8 %
  простое среднее по позициям: 1.846 → 84.6 %
  официальный индекс: 1.651 → 65.1 %

== Кто поднял вашу корзину
        позиция   вес  выросла в  вклад
  аренда, месяц 0.796      1.667  0.531
       мясо, кг 0.057      2.000  0.057
  яйца, десяток 0.025      2.857  0.046
   молоко, литр 0.034      1.846  0.029
  хлеб, буханка 0.016      2.000  0.016
интернет, месяц 0.044      1.300  0.013
проезд, поездка 0.028      1.250  0.007
  сумма вкладов: 0.698 — это и есть рост корзины

== Расходы за месяц
  тогда: 113100 тенге
  сейчас: 192100 тенге
  разница: 79000 тенге в месяц

Разбор

Корзина — это не список цен, а список расходов

Цена буханки ничего не говорит о вашем месяце. Говорит цена × количество: буханка по 90 тенге двадцать раз в месяц — это 1800 тенге, а аренда один раз — 90 000. В корзине они стоят рядом, и вторая тяжелее первой в пятьдесят раз.

Поэтому первое, что делают с чеками, — превращают их в стоимости, а не в ценники.

Вес — доля позиции в расходах базового года

вес = стоимость позиции ÷ стоимость всей корзины. Веса в сумме дают единицу, и они — весь ответ на вопрос, чья это корзина:

аренда, месяц 79.6
мясо, кг 5.7
интернет, месяц 4.4

Восемьдесят процентов этого месяца — аренда. Значит, инфляция этого человека почти целиком равна росту его аренды, что бы ни случилось с яйцами.

Индекс корзины: стоимость сегодня против стоимости тогда

корзина целиком: 1.698 → 69.8 %

Одно деление: сумма количество × цена сейчас на сумму количество × цена тогда. Количества берутся базового года — те же, что и в знаменателе. Это называется индексом Ласпейреса, и официальная статистика считает свой примерно так же: фиксирует корзину и смотрит, сколько она стоит теперь.

Почему простое среднее врёт

простое среднее по позициям: 1.846 → 84.6 %

Оно считает яйца и аренду одинаково важными. Яйца подорожали в 2.86 раза и весят 2.5 % — их вклад составляет около девяти копеек на рубль вклада аренды (0.046 ÷ 0.531). Но в простом среднем они тянут одинаково, и ответ оказывается на пятнадцать пунктов выше правды.

Правило: по позициям среднее не берут никогда. Берут по деньгам.

Вклад: кто именно поднял корзину

вклад = вес × (во сколько раз выросла − 1). Сумма вкладов в точности равна росту корзины — это видно в выводе: 0.698 и там, и там.

аренда, месяц 0.796  1.667  0.531
яйца, десяток 0.025  2.857  0.046

Аренда выросла скромнее яиц, а подняла корзину в двенадцать раз сильнее. Это самое полезное, что даёт разложение: оно называет позицию, с которой имеет смысл что-то делать.

Почему ваш индекс отличается от официального

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

Расхождение в примере — 4.8 процентного пункта, и оно целиком объясняется весами: съёмное жильё в этой корзине занимает вчетверо больше места, чем жильё в официальной.

Отсюда две честные фразы: «инфляция в стране 65 %» и «моя инфляция 70 %». Обе верны, и обе бессмысленны без слов «чья корзина».

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

Товар исчез из продажи, появился новый, качество изменилось, вы переехали в другой район — в фиксированной корзине для этого места нет. Официальная статистика решает это заменами и пересмотрами весов; как именно — в уроке про чтение официальной статистики, он ещё впереди.

Карта урока

Карта урока: чек, вес и свой индекс

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

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

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

Разминка

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

1. Предскажите. Что напечатает эта программа?

import pandas as pd

two = pd.DataFrame(
    [("спички", 100, 30, 90), ("аренда", 1, 90000, 99000)],
    columns=["позиция", "сколько", "было", "стало"],
)
two["тогда"] = two["сколько"] * two["было"]
two["сейчас"] = two["сколько"] * two["стало"]
print("простое среднее:", round((two["стало"] / two["было"]).mean(), 3))
print("по корзине:", round(two["сейчас"].sum() / two["тогда"].sum(), 3))

2. Заполните пропуск. Вместо ... посчитайте веса: долю каждой позиции в расходах.

# три строки расходов за месяц, в тенге
import pandas as pd

cost = pd.Series([1800, 3900, 90000], index=["хлеб", "молоко", "аренда"])
weights = ...
print((weights * 100).round(1).to_dict())

3. Почините. Программа считает «вашу инфляцию» по ценникам, забыв про количества.

# хлеб берут двадцать раз в месяц, аренду — один
import pandas as pd

basket = pd.DataFrame(
    [("хлеб", 20, 90, 180), ("аренда", 1, 90000, 150000)],
    columns=["позиция", "сколько", "было", "стало"],
)
print("ваша инфляция:", round(((basket["стало"] / basket["было"]).mean() - 1) * 100, 1), "%")

Задание

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

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

== Вклад в рост, по убыванию
        позиция   вес  выросла в  вклад
  аренда, месяц 0.796      1.667  0.531
       мясо, кг 0.057      2.000  0.057
  яйца, десяток 0.025      2.857  0.046
   молоко, литр 0.034      1.846  0.029
  хлеб, буханка 0.016      2.000  0.016
интернет, месяц 0.044      1.300  0.013
проезд, поездка 0.028      1.250  0.007

== Итог
  моя корзина: 1.698 → 69.8 %
  официальный индекс: 1.651 → 65.1 %
  разница: 4.8 процентного пункта

== Одна позиция объясняет
  позиция: аренда, месяц
  её вес: 79.6 % корзины
  её вклад: 75.9 % всего роста

== Проверка
  сумма вкладов: 0.6985
  рост корзины: 0.6985
  сходится: True

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

На своих данных. Для индекса Ласпейреса возьмите количества базового периода и цены тех же позиций тогда и сейчас. Если у вас есть только сегодняшние количества, честно назовите результат стоимостью сегодняшней корзины в старых и новых ценах (индексом Пааше), а не Ласпейресом. Посчитайте индекс и сравните с официальным за тот же период. Потом посмотрите на разложение по вкладам: одна-две позиции почти наверняка объяснят больше половины. Это первые позиции для анализа, но остальные расходы тоже реальны и не являются «шумом».

По желанию.

  • Посчитайте индекс, взяв количества сегодняшнего года вместо базового (это индекс Пааше), и сравните с Ласпейресом.
  • Уберите из корзины аренду и посмотрите, как меняются и веса, и итог: это ответ на вопрос «а если бы жильё было своим».
  • Разбейте позиции на группы — еда, жильё, транспорт, связь — и посчитайте вклад каждой группы вместо каждой позиции.

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

Шага нет: сводка читает официальные ряды по странам, а корзина — ваш личный файл, и в общей сводке ей делать нечего.

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

Долг, который курс объявляет здесь: месячных данных и весов по группам у сводки нет. С ними появился бы вопрос, какая группа подняла индекс — тот же разбор по вкладам, только на уровне страны. Это придёт в уроке про чтение официальной статистики.

Ответы

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

На вопросы

  1. Потому что среднее по позициям считает все позиции одинаково важными, а они не одинаковы: яйца весят 2.5 % расходов, аренда — 79.6 %. На примере простое среднее даёт 84.6 % вместо настоящих 69.8 %.
  2. Вес — доля позиции в расходах базового года: стоимость позиции, делённая на стоимость всей корзины. Он берётся не из цен, а из цена × количество — то есть из того, сколько денег на эту позицию уходит.
  3. Потому что различаются не цены, а веса. Официальная корзина — усреднённая структура расходов по стране; в вашей аренда может занимать восемьдесят процентов, а в средней — заметно меньше. Одни и те же ценники, разложенные по разным весам, дают разные индексы.

К разминке

  1. Спички подорожали втрое, аренда — на десятую часть. Простое среднее берёт по половине от каждого, корзина — по деньгам, а в деньгах спички почти ничего не весят.
простое среднее: 2.05
по корзине: 1.161
  1. cost / cost.sum(). Веса — доли, их сумма равна единице, и считаются они из стоимостей.
import pandas as pd

cost = pd.Series([1800, 3900, 90000], index=["хлеб", "молоко", "аренда"])
weights = cost / cost.sum()
print((weights * 100).round(1).to_dict())
{'хлеб': 1.9, 'молоко': 4.1, 'аренда': 94.0}
  1. Считать по стоимостям: умножить цены на количества и поделить суммы.
import pandas as pd

basket = pd.DataFrame(
    [("хлеб", 20, 90, 180), ("аренда", 1, 90000, 150000)],
    columns=["позиция", "сколько", "было", "стало"],
)
then = basket["сколько"] * basket["было"]
now = basket["сколько"] * basket["стало"]
print("ваша инфляция:", round((now.sum() / then.sum() - 1) * 100, 1), "%")
ваша инфляция: 67.3 %

К заданию

Разложение по вкладам держится на том, что сумма вкладов равна росту корзины. Это не совпадение: Σ вес × (рост − 1) — то же самое выражение, что и (Σ сейчас ÷ Σ тогда) − 1, просто записанное по позициям. Поэтому проверка в конце задания сходится точно, а не приблизительно, и любое расхождение означает ошибку в весах.

Сравнение с официальным индексом даёт разницу в 4.8 пункта. Полезно не само число, а то, что его можно объяснить: в этой корзине аренда весит 79.6 %, и весь спор с официальной цифрой — про долю жилья, а не про цены на хлеб.

Источники

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

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

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

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

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

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