Inflation#
Inflation is the rate at which the general level of prices for goods and sevices changes.
Contents. Definition and inflation indices, with examples of indices used in Italy: NIC, FOI, IPCA; components of inflation, with details of IPCA in Italy; correlation with other macroeconomic quantities; who controls inflation; origin of inflation
Show code cell source
# Load libraries
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import plotly.io as pio
pio.renderers.default = 'iframe' # plotly_mimetype+notebook' # or 'iframe'
import requests
from io import BytesIO
Show code cell source
# Import data
folder = 'https://raw.githubusercontent.com/Basics2022/bbooks-financial-edu/master/code/data/'
#> Files
# FOI-prices: 2016-.../2025-...
# IPCA-weights: 2018/2025
# IPCA-prices: 2018-.../2025-...
# NIC-weights: 2018/2025
# NIC-prices: 2018-.../2025-...
filen = {
'FOI-prices' : folder+'monthly-FOI.xlsx', # folder+'monthly-FOI.xlsx' ,
'IPCA-weights': folder+'Classificazione%20Ecoicop%20(4%20cifre)%20(IT1%2C168_6_DF_DCSP_IPCA3_1%2C1.0).xlsx', # folder+'Classificazione Ecoicop (4 cifre) (IT1,168_6_DF_DCSP_IPCA3_1,1.0).xlsx',
'IPCA-prices' : folder+'Classificazione%20Ecoicop%20(4%20cifre)%20(IT1%2C168_760_DF_DCSP_IPCA1B2015_1%2C1.0).xlsx', #folder+'Classificazione Ecoicop (4 cifre) (IT1,168_760_DF_DCSP_IPCA1B2015_1,1.0).xlsx',
'NIC-weights' : folder+'Classificazione%20Ecoicop%20(5%20cifre)%20(IT1%2C167_743_DF_DCSP_NIC3B2015_3%2C1.0).xlsx', # folder+'Classificazione Ecoicop (5 cifre) (IT1,167_743_DF_DCSP_NIC3B2015_3,1.0).xlsx',
'NIC-prices' : folder+'Classificazione%20Ecoicop%20(5%20cifre)%20(IT1%2C167_744_DF_DCSP_NIC1B2015_4%2C1.0).xlsx' # folder+'Classificazione Ecoicop (5 cifre) (IT1,167_744_DF_DCSP_NIC1B2015_4,1.0).xlsx'
}
Show code cell source
df_ipca = {
'prices' : pd.read_excel(filen['IPCA-prices'], sheet_name='data', decimal=',', engine='openpyxl'),
'weights': pd.read_excel(filen['IPCA-weights'], sheet_name='data', decimal=',', engine='openpyxl')
}
for kdf, idf in df_ipca.items():
idf.columns = idf.columns.str.strip() # strip whitespaces
idf = idf.set_index(['Tempo'])
idf.index = idf.index.str.strip()
idf = idf.transpose()
idf = idf.rename(columns={'index': 'Tempo'})
Inflation Indices (e.g. in Italy)#
Overall inflation is the the weighted average of inflation on different classes of goods and services, weighted for their share of expenses.
Everyone perceives its own inflation, depending on its expenses. Different indices are usually used within an economy to track inflation for some “average individual”.
Different indices may differ on values of weights, and other “details” like the effect of discounts and public transfers.
As an example, three indices are used in Italy:
NIC (Prezzi al Consumo per l’intera Collettività Nazionale), usually the general
FOI (Prezzi al Consumo per Famiglie di Operai e Impiegati), usually used for contracts, pension and inflation-linked contracts, ex-tobacco and lotteries.
IPCA (Indice Armonizzato dei Prezzi al Consumo, HIPC Harmonized Index of Consumer Prices), used for comparison and statistics in the EU
Show code cell source
# Compare IPCA, NIC and FOI
# ...
Weights and Price Indices of Classes of Goods and Services - Italy IPCA#
National and International Institutions for Statistics (in Italy, ISTAT) provide open-access databases collecting statistics about society and economics, including data about price.
ISTAT. As an example, Italian ISTAT provides data at https://esploradati.istat.it/databrowser/#/it/dw
All the data we need here is available under the category “Prezzi” - Prices. In order to reach a reasonable stability of the notebook, data have been downloaded, cleaned and stored in a folder on the repository of the project.
Inspect Data#
Before producing plots, price indices and weights of level-4 categories are visually inspected. Data are usually collected in tables.
Category Price Indices - Level-4 IPCA#
Show code cell source
df_ipca['prices']
Show code cell output
| Tempo | 2018-01 | 2018-02 | 2018-03 | 2018-04 | 2018-05 | 2018-06 | 2018-07 | 2018-08 | 2018-09 | ... | 2024-09 | 2024-10 | 2024-11 | 2024-12 | 2025-01 | 2025-02 | 2025-03 | 2025-04 | 2025-05 | 2025-06 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | [00] Indice generale | 100.6 | 100.1 | 102.4 | 102.9 | 103.2 | 103.4 | 102 | 101.8 | 103.5 | ... | 123 | 123.4 | 123.3 | 123.4 | 122.4 | 122.5 | 124.4 | 124.9 | 124.8 | 125 |
| 1 | [01] -- prodotti alimentari e bevande analcoli... | 103.9 | 103 | 103.2 | 103.6 | 104.3 | 103.9 | 103.1 | 103.1 | 103 | ... | 130.8 | 132.3 | 133.3 | 132.6 | 133.8 | 133.7 | 133.8 | 134.8 | 135.4 | 135.7 |
| 2 | [011] Prodotti alimentari | 104 | 103.2 | 103.4 | 103.7 | 104.5 | 104.2 | 103.2 | 103.2 | 103.2 | ... | 131.2 | 132.9 | 133.8 | 133 | 134.1 | 134 | 134 | 134.9 | 135.5 | .. |
| 3 | [0111] Pane e cereali | 101.6 | 100.6 | 101.1 | 101.8 | 101.4 | 102 | 101.7 | 102.1 | 101.3 | ... | 127.2 | 127.6 | 127.8 | 127.8 | 128.5 | 128.2 | 128.4 | 129.2 | 129.3 | .. |
| 4 | [0112] Carni | 102.9 | 102.4 | 102.6 | 102.9 | 102.7 | 102.8 | 102.8 | 102.7 | 103 | ... | 125.6 | 125.9 | 126.7 | 127 | 127.9 | 128 | 128.7 | 129.5 | 130.2 | .. |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 145 | [SERVTRANS_5DG] Servizi relativi ai trasporti ... | 101.1 | 102.6 | 104.4 | 104.5 | 104.7 | 107 | 107.6 | 112.6 | 107 | ... | 123 | 123 | 122.8 | 124.3 | 121.8 | 121.5 | 122.9 | 127.1 | 125 | .. |
| 146 | [SERVMISC_5DG] Servizi vari (dettaglio 5-digit) | 99.8 | 100.1 | 100.3 | 100.6 | 101 | 101.1 | 101.3 | 101.3 | 101.4 | ... | 112.4 | 112.9 | 113 | 113 | 113.3 | 113.5 | 113.8 | 113.9 | 114 | .. |
| 147 | [00XEFOODUNP_5DG] Componente di fondo (core in... | 99.9 | 99.5 | 102.2 | 103 | 103.1 | 103.3 | 101.5 | 101.3 | 103.2 | ... | 117.9 | 118 | 117.8 | 117.9 | 116.1 | 116.1 | 118.2 | 119.7 | 119.9 | 120.4 |
| 148 | [00XEFOOD_5DG] Indice generale al netto dell'e... | 99.4 | 99 | 102.2 | 103 | 103.1 | 103.4 | 101.1 | 100.9 | 103.2 | ... | 115.8 | 116 | 115.6 | 115.8 | 113.5 | 113.3 | 115.9 | 117.6 | 117.7 | 118.1 |
| 149 | [00XE_5DG] Indice generale esclusi energetici ... | 100.4 | 99.9 | 102.4 | 103.2 | 103.4 | 103.6 | 101.7 | 101.5 | 103.3 | ... | 119 | 119.4 | 119.2 | 119.3 | 117.7 | 117.6 | 119.7 | 121.2 | 121.4 | 121.8 |
150 rows × 91 columns
Category Weights - Level-4 IPCA#
Show code cell source
df_ipca['weights']
Show code cell output
| Tempo | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | [00] Indice generale | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 | 1000000 |
| 1 | [01] -- prodotti alimentari e bevande analcoli... | 175648 | 176326 | 175240 | 175418 | 173257 | 172583 | 205912 | 194554 | 181443 | 181801 | 181425 |
| 2 | [011] Prodotti alimentari | 162005 | 162805 | 161810 | 161903 | 159432 | 158644 | 189091 | 179008 | 166582 | 167112 | 166336 |
| 3 | [0111] Pane e cereali | 30036 | 30342 | 29853 | 29558 | 29717 | 29778 | 35767 | 33586 | 31994 | 31422 | 31513 |
| 4 | [0112] Carni | 41803 | 40944 | 40876 | 39914 | 39286 | 38162 | 45695 | 43159 | 39770 | 40253 | 40080 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 144 | [1252] Assicurazione connessa all'abitazione | .. | .. | 1102 | 456 | 469 | 398 | 525 | 466 | 470 | 517 | 487 |
| 145 | [1253] Servizi assicurativi connessi alla salu... | 467 | 584 | 530 | 560 | 605 | 720 | 903 | 781 | 728 | 748 | 692 |
| 146 | [1254] Assicurazioni sui mezzi di trasporto | 13095 | 13767 | 13191 | 12641 | 12033 | 12020 | 14649 | 12737 | 12773 | 13259 | 12486 |
| 147 | [126] Servizi finanziari n.a.c. | 13060 | 14353 | 13628 | 12614 | 15124 | 15559 | 17041 | 14343 | 14862 | 14946 | 15195 |
| 148 | [127] Altri servizi n.a.c. | 19820 | 19403 | 19046 | 19833 | 21045 | 21951 | 18066 | 20537 | 19186 | 19297 | 20422 |
149 rows × 12 columns
Show code cell source
# Useful new dataframe to match weights (yearly) and prices (monthly) later
#> Extract code and label
ddf = {}
ddf = pd.DataFrame()
ddf['code' ] = df_ipca['weights']['Tempo'].str.extract(r'(\[\d+\])')
ddf['label'] = df_ipca['weights']['Tempo'].str.strip()
#> Determine parent code
def get_parent_code(code):
if code is None or pd.isna(code):
return None
code_str = str(code).strip('[]')
if len(code_str) <= 2:
return None # no parent
elif len(code_str) == 3:
return f"[{code_str[:2]}]"
elif len(code_str) == 4:
return f"[{code_str[:3]}]"
else:
return None
#> Determine value
def get_value(code):
if code is None or pd.isna(code):
return None
code_str = str(code).strip('[]')
if len(code_str) == 4:
return int(1)
else:
return int(0)
#> Determine parents and values
ddf['parent'] = ddf['code'].map( get_parent_code )
years = [str(y) for y in range(2018, 2026)] # list of year strings
for year in years:
ddf[str(year)] = df_ipca['weights'][str(year)] / 1e4
#> Drop [00] Indice generale
ddf = ddf[ddf['code'] != '[00]']
# ddf.head(5)
Plots#
Category weights - Level-2 IPCA#
The weights assigned to IPCA (Harmonized Index of Consumer Prices) categories represent the average expenditure share of households on each category of goods and services. These weights reflect how important each category is in the consumption basket.
These weights are revised annually to account for changing consumer behavior, as one can easily realize acting on the slider of the picture below. They are the weights used in computing the overall inflation \(i\) index, as the weighted sum of inflation \(i_c\) of IPCA categories,
Show code cell source
import plotly.graph_objects as go
# Create the initial figure for the first year
fig = go.Figure()
fig.add_trace(go.Sunburst(
ids=ddf['code'],
labels=ddf['code'],
parents=ddf['parent'],
values=ddf['2025'], # initial year
hoverinfo='label+value+text',
branchvalues="total", # "toatal" or "remainder"
sort=False,
text=ddf['label'],
textinfo='text+percent parent'
))
fig.update_layout(title="IPCA weights - 2025")
# Create one frame per year
frames = []
for year in years:
frames.append(go.Frame(
data=[go.Sunburst(
ids=ddf['code'],
labels=ddf['code'],
parents=ddf['parent'],
values=ddf[year],
branchvalues="total", # "toatal" or "remainder"
sort=False,
text=ddf['label'],
textinfo='text+percent parent'
)],
name=year,
layout=go.Layout(title_text=f"IPCA weights - {year}")
))
fig.frames = frames
# Add slider steps for each year
steps = []
for year in years:
steps.append(dict(
method='animate',
args=[[year], # frame name
dict(mode='immediate',
frame=dict(duration=500, redraw=True),
transition=dict(duration=300))],
label=year
))
# Layout with slider
fig.update_layout(
# width=800, height=800,
margin=dict(t=50, l=0, r=0, b=0),
sliders=[dict(
active=years.index('2025'),
currentvalue={"prefix": "Year: "},
pad={"t": 50},
steps=steps
)],
)
fig.show()
Category Prices - Level-2 IPCA#
Some categories in IPCA are subject to strong seasonal effects, meaning prices follow recurring patterns during the year.
As an example:
Clothing and Footwear: in July–August, retailers apply seasonal discounts (saldi) in Italy and prices in IPCA do include these discounts when they are actually applied in stores, as it’s shown by seasonal July/August price drops
Fresh fruits and vegetables: prone to seasonal availability, leading to fluctuating prices.
Travel and tourism: prices rise in summer and holidays.
Seasonality can obscure underlying inflation trends: that’s why seasonally adjusted inflation is evaluated, see below.
Show code cell source
#> Classes
class_names = ddf[ ddf['parent'].isnull() ]['label'].tolist()
df_ipca['prices']['Tempo'] = df_ipca['prices']['Tempo'].str.strip()
# Add a trace for each row (now a column in df_T)
# df_classes = df['prices'][ df['prices']['Tempo'].isin(class_names) ]
df_classes = df_ipca['prices'][df_ipca['prices']['Tempo'].str.match(r'^\[\d{2}\]')].set_index('Tempo').transpose()
# Create a figure
fig = go.Figure()
df_classes
for column in df_classes.columns:
fig.add_trace(go.Scatter(
x=df_classes.index,
y=df_classes[column],
mode='lines',
name=column[:20]
))
# Customize layout
fig.update_layout(
title='IPCA price indices - 2015:100',
xaxis_title='Time',
yaxis_title='Value',
# xaxis_tickangle=-45,
hovermode='x unified',
# template='plotly_white',
# height=600,
# width=1000,
# legend=dict(orientation="v", x=1.02, y=1)
)
fig.show()
Category Price Changes (Inflation) - Level-2 IPCA#
The 12-month inflation rate (year-on-year or YoY) compares prices in a given month to the same month the year before. It’s already less prone to seasonal effects than the month-to-month rate.
However, even YoY rates can exhibit seasonal patterns, especially in volatile components like food, energy, and clothing. In order to reduce volatility of inflation indices, it’s possible to use:
Core inflation, as a measure of inflation that excludes the most volatile items (e.g., unprocessed food, energy), in order to provide a smoothed measure of inflation trends.
Statistical filtering, and moving averages
Energy post-2022
Since 2022, prices in the energy and utility sectors have shown exceptional volatility. Different causes may have contributed, like geopolitical tensions (notably, the war in Ukraine), “liberalized” electricity/gas markets in Italy where price caps were adjusted or removed. Inflation in energy and electricity was also influenced by a base effect (e.g., very low prices in 2020–2021 followed by spikes in 2022).
Policy interventions like tax reductions and bonuses - that are not “free” -, which may or may not be reflected in consumer prices, depending on implementation.
The use of core inflation in 2022–2023 was arguable, as energy prices didn’t just spiked and reverted, but was/is quite a long-term shock (war, sanctions, market and supply restructuring,…); as energy price influences many other sectors, food price rose as well, due to input cost shocks /fretilizers, transports,…) not as a result of seasonality only. Using core inflation and excluding energy and food components masked the true cost-of living impact on households.
Show code cell source
df_inflation = df_classes.pct_change(periods=12).dropna(how='any') # percent
# Create a figure
fig = go.Figure()
for column in df_inflation.columns:
fig.add_trace(go.Scatter(
x=df_inflation.index,
y=df_inflation[column],
mode='lines',
name=column[:50]
))
# Customize layout
fig.update_layout(
title='IPCA inflation by categories - 12 months price difference',
xaxis_title='Time',
yaxis_title='Value',
# xaxis_tickangle=-45,
hovermode='x unified',
# template='plotly_white',
# height=600,
# width=1000,
# legend=dict(orientation="v", x=1.02, y=1)
legend=dict(
orientation="h",
yanchor="top",
y=-0.2,
xanchor="center",
x=0.5
)
)
fig.show()
Category contributions to overall inflation - Level-2 IPCA#
Show code cell source
#> Need for aligning data with different time intevals
df_inflation.index = pd.to_datetime(df_inflation.index)
df_inflation.index = df_inflation.index.to_period('M')
# df_inflation
df_weights = df_ipca['weights'][ df_ipca['weights']['Tempo'].str.match(r'^\[\d{2}\]')].set_index('Tempo').transpose() / 1e6
#> Add a row 2026, just to use the same weights for all the months of 2025
row_2026 = df_weights.loc[['2025']].copy()
row_2026.index = ['2026']
df_weights = pd.concat([df_weights, row_2026],)
df_weights.index = pd.to_datetime(df_weights.index)
df_weights = df_weights.asfreq('YS')
# # df_weights_monthly = df_weights
df_weights = df_weights.resample('MS').ffill()
df_weights.index = df_weights.index.to_period('M')
# print(df_weights.index)
# print(df_inflation.index)
common_periods = df_inflation.index.intersection(df_weights.index)
df_monthly_weights = df_weights.loc[common_periods]
# df_monthly_weights
# df_inflation
df_monthly_weights.columns = df_monthly_weights.columns.str.strip()
df_inflation.columns = df_inflation.columns.str.strip()
df_contributions = df_monthly_weights * df_inflation
df_contributions = df_contributions.drop('[00] Indice generale', axis=1)
df_contributions.index = df_contributions.index.to_timestamp()
# df_contributions
Show code cell source
# df_plot = df_contributions.reset_index()
df_plot = df_contributions.reset_index().rename(columns={'Tempo': 'Time'})
# df_plot['Tempo'] = pd.to_datetime(df_plot['Tempo'])
df_plot.index = pd.to_datetime(df_plot.index)
df_plot
# print(df_plot.columns.to_list())
# # print(df_plot.head())
# print(df_plot.dtypes)
# Create a new DataFrame by resetting the index and renaming the index column to 'Time'
df_new = df_contributions.reset_index()
# If the old index had no name, the column will be called 'index', so rename it:
if 'index' in df_new.columns:
df_new = df_new.rename(columns={'index': 'Time'})
else:
# If it has a name, replace that with 'Time'
old_name = df_contributions.index.name
if old_name is not None and old_name in df_new.columns:
df_new = df_new.rename(columns={old_name: 'Time'})
else:
# If the index has no name and no 'index' column, just add one:
df_new['Time'] = df_contributions.index
# Now df_new has a clean 'Time' column and a fresh integer index
# print(df_new.head())
df_new
Show code cell output
| Tempo | Time | [01] -- prodotti alimentari e bevande analcoliche | [02] -- bevande alcoliche e tabacchi | [03] -- abbigliamento e calzature | [04] -- abitazione, acqua, elettricità, gas e altri combustibili | [05] -- mobili, articoli e servizi per la casa | [06] -- servizi sanitari e spese per la salute | [07] -- trasporti | [08] -- comunicazioni | [09] -- ricreazione, spettacoli e cultura | [10] -- istruzione | [11] -- servizi ricettivi e di ristorazione | [12] -- altri beni e servizi |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2019-01-01 | 0.000834 | 0.000754 | -0.000187 | 0.004464 | 0.0 | 0.000341 | 0.001504 | -0.001614 | -0.000238 | 0.0 | 0.001366 | 0.001651 |
| 1 | 2019-02-01 | 0.003028 | 0.001132 | -0.000199 | 0.00446 | 0.000154 | 0.00034 | 0.000895 | -0.001936 | -0.000178 | 0.0 | 0.001114 | 0.001749 |
| 2 | 2019-03-01 | 0.001679 | 0.001058 | 0.00023 | 0.00446 | -0.000076 | 0.000255 | 0.001778 | -0.001873 | -0.00012 | 0.000013 | 0.001106 | 0.001636 |
| 3 | 2019-04-01 | 0.000334 | 0.000772 | 0.000148 | 0.003974 | -0.000076 | 0.000297 | 0.00399 | -0.002419 | -0.00012 | 0.000013 | 0.001935 | 0.001916 |
| 4 | 2019-05-01 | 0.000664 | 0.000676 | 0.000222 | 0.003861 | 0.0 | 0.000297 | 0.002936 | -0.002491 | -0.00006 | 0.000013 | 0.00132 | 0.00153 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 73 | 2025-02-01 | 0.004449 | 0.00079 | -0.000433 | 0.003935 | 0.000513 | 0.001076 | -0.00013 | -0.001019 | 0.001017 | 0.000292 | 0.003506 | 0.002718 |
| 74 | 2025-03-01 | 0.00473 | 0.001011 | 0.000905 | 0.007548 | 0.000448 | 0.001188 | -0.00142 | -0.000944 | 0.001073 | 0.000292 | 0.004101 | 0.002699 |
| 75 | 2025-04-01 | 0.005978 | 0.000567 | 0.000458 | 0.005752 | 0.000318 | 0.001146 | -0.001411 | -0.000976 | 0.00075 | 0.000292 | 0.00492 | 0.002683 |
| 76 | 2025-05-01 | 0.005951 | 0.000621 | 0.000516 | 0.004934 | 0.000446 | 0.001183 | -0.003094 | -0.000877 | 0.00053 | 0.000292 | 0.004356 | 0.002681 |
| 77 | 2025-06-01 | 0.006797 | 0.000702 | 0.000458 | 0.002547 | 0.000509 | 0.001257 | -0.001422 | -0.000852 | 0.000584 | 0.000292 | 0.004312 | 0.002762 |
78 rows × 13 columns
Show code cell source
y_cols = [col for col in df_new.columns if col != 'Time']
df_long = df_new.melt(id_vars='Time', value_vars=y_cols, var_name='Category', value_name='Value')
fig = px.bar(
df_long,
x='Time', # 'x',
y='Value',
color='Category',
title='Category contributions to inflation'
)
fig.update_layout(
barmode='stack',
legend=dict(
orientation="h",
yanchor="top",
y=-0.2,
xanchor="center",
x=0.5
)
) # relative')
fig.show()
# df_contributions
Correlations in macroeconomics with inflation#
Some correlations exist1 between inflation and other macroeconocmics quantitites.
Phillips Curve: inverse relation between inflation and unemployment (in the short-run)
Money supply in the long-run “Inflation is a monetary phenomenon”, M.Friedman.
Control of Inflation#
Control of inflation is one of the goals of central banks, like the FED and the ECB.
Central banks aims at controlling inflation, matching target inflation (usaully set as 2%) by means of monetary policy:
interest rates (cost of money)
non-conventional actions, like quantitative easing (QE)/tightening (QT)
A goverment may indirectly influence inflation with fiscal policy, as taxation and government spending can influence demand.
Credibility of targets, and actors through their actions and forward guidance may influence inflation as well: expectations influences inflation.
Origin of inflation#
Origin of inflation?
short-run, medium-run: cost-push, demand-pull, built-in (triangle model)
long-run: “inflation is always and everywhere a monetary phenomenon” M.Friedman
- 1
…