Overview with examples of common bilateral methods
Bilateral price indices
Author
Serge Goussev
Published
September 27, 2024
Modified
March 11, 2026
Overview
The concept of price index methods
A price index provides an aggregate measure of price change for a particular product segment, industry, or overall economy. … [They compare] the cost of purchasing a set of goods at different points in time. This “set of goods” is often referred to as the “market basket” or the “bundle” of goods. - Aizcorbe (2014)
Example datasets
To illustrate how each index can be calculated, I’ll illustrate using some example datasets and some common price index methods.
NoteSetup with gpindex
gpindex is a convenient R package for price index calculation. It has a simple play dataset built in that we can use to demonstrate how to construct various price indices.
library(gpindex)
Warning: package 'gpindex' was built under R version 4.5.2
Where \(p\) and \(q\) denote prices and quantities, and 0 and 1 denote two points in time.
If \(N\) goods are sold in both periods (note that an overlap is needed), we can compare the the cost of purchasing the same goods we bought in period 1 with a certain period in the future.
We can also write the Laspeyres as the weighted arithmetic average of the price change of the individual products in the index
give the ratio of good \(n\) expenditure to total expenditure, or could also be considered the relative importance or share of the product. There are some key nuances with this approach:
Products sold in both periods are included in both periods, thus new products are omitted.
We fix the relative importance of the goods for both periods based on period 1 weight, thus we do not reflect changes in composition over time (substitution). This can be convenient as we need weights only for the base period.
Most price indices are variants of the Laspeyres, such as the Lowe (which compare the prices from the current month with the previous month, but use weights from a year before that).
NoteExample with gpindex
There are two ways that we can use the package to compare the two periods - one using the laspeyres_index() function, the other using the arithmetic_mean() function.
The Laspeyres and Paasche include prices and quantities in both periods, and both use the same relatives with different expenditure shares.
NoteExample with gpindex
There are two ways that we can use the package to compare the two periods - one using the paasche_index() function, the other using the arithmetic_mean() function.
The Fisher thus uses expenditure from both periods and thus provides relative importance that are more closely aligned with the goods actually sold. As the Fisher satisfies homogeneity, symmetry, and the time reversal test (the price change from the base to the current should be the inverse of the current to the base) - thus it doesn’t matter what period is chosen as the base.
NoteExample with gpindex
There is an out of the box function we can use - the fisher_index() function.
fisher_index(p2, p1, q2, q1)
[1] 1.40105
Törnqvist Index
Similar to the Fisher, however it takes the average of the weights instead of averaging the two indices
There are two ways that we can use the package to compare the two periods - one using the jevons_index() function, the other using the geometric_mean() function.
jevons_index(p2, p1)
[1] 1.24192
geometric_mean(p2 / p1)
[1] 1.24192
Comparison across the larger synthetic dataset
Using the full toy dataset - lets compare the price indices
Show the code
# 1. Use t1 as the base periodp_base <- price6$t1q_base <- quantity6$t1# 2. Loop through the periods to calculate indices for the setresults <-mapply(function(p_curr, q_curr) {# Calculate current value shares (s1) for Törnqvist weights s1 <- (p_curr * q_curr) /sum(p_curr * q_curr) s2 <- (p_base * q_base) /sum(p_base * q_base) w_torn <- (s1 + s2) /2c(Laspeyres =laspeyres_index(p_curr, p_base, q_base),Paasche =paasche_index(p_curr, p_base, q_curr),Fisher =fisher_index(p_curr, p_base, q_curr, q_base),Tornqvist =geometric_mean(p_curr / p_base, w_torn),Jevons =geometric_mean(p_curr / p_base) )}, price6, quantity6)# 3. Convert to a clean dataframedf_indexes <-as.data.frame(t(results))df_indexes$Period <-rownames(df_indexes)## Now lets plot this (for which we need a few additional libraries)library(plotly)library(ggplot2)library(tidyr)# 4. Create the plotfig <-plot_ly(df_indexes, x =~Period, width =800, height =500) %>%add_trace(y =~Laspeyres, name ='Laspeyres', type ='scatter', mode ='lines+markers') %>%add_trace(y =~Paasche, name ='Paasche', type ='scatter', mode ='lines+markers') %>%add_trace(y =~Fisher, name ='Fisher', type ='scatter', mode ='lines+markers',line =list(dash ='dash')) %>%add_trace(y =~Tornqvist, name ='Törnqvist', type ='scatter', mode ='lines+markers',line =list(dash ='dot')) %>%add_trace(y =~Jevons, name ='Jevons', type ='scatter', mode ='lines+markers') %>%layout(title ="Interactive Comparison of Five Price Index Formulas",yaxis =list(title ="Index Value (Base t1 = 1.0)"),xaxis =list(title ="Time Period"),hovermode ="x unified", # Show values in one tooltip when hoveringlegend =list(orientation ='h', y =-0.2) # Move legend to bottom )fig
Show the code
#5. Print the dataframe to have a reference table to the graph abovelibrary(DT)datatable(df_indexes)