7  Deflating nominal values to real values

We saw earlier in this section that it’s important to seasonally adjust a time series so we can make meaningful comparisons between time periods. Similarly, nominal time series (those expressed in current prices) should be deflated to real values in order to remove the effect of price changes over time. Knowing how to perform this operation is essential, as many economic time series are not available in real terms from the source.

To convert nominal data into real data, two things are needed. First, we must choose a base (or reference) year. The prevailing prices in that year will serve as the reference for all other years. For example, we can convert a sample of US nominal GDP from 2000 to 2020 using 2010 prices as the reference. In this case, all the years in the sample will be expressed in 2010 prices. This choice is generally arbitrary, but a good practice is to select a year in which prices were close to the sample average.

More importantly, we need to choose an appropriate price index to use as the deflator. Ideally, the deflator should accurately capture the price changes in the basket of goods or services represented by the series we want to adjust. For instance, it would make no sense to deflate a retail sales series using a construction price index. In the absence of a specific price index for the goods in the retail sales series, we could use a broader consumer price index as a proxy.

Let’s look at a practical example of how to deflate a series. For this, we’ll use data on US nominal GDP provided by the Bureau of Economic Analysis (BEA). It’s also a good opportunity to show how to access data from this relevant source using its API.

Data

The data set containing the US nominal GDP and deflator from BEA is available in the R4ER2data package under the name nominal_deflator_gdp_us.

You’ll need to register in order to obtain access to the API. The process only takes a few minutes and requires minimal information. Since this information is personal, I’ve stored my key as an environment variable.

BEA provides a detailed document explaining how to use the API resources. Below, I’ll first retrieve the table names from the NIPA database, where GDP data are stored. Note that I use the pluck function from the purrr package as a substitute for [[. 1 This makes it easier to access lower-level elements in lists.

library(tidyverse)
library(httr)
library(jsonlite)
api_bea_key <- Sys.getenv('api_bea_key')

bea_nipa_tables <- GET(
  glue::glue(
    'https://apps.bea.gov/api/data?UserID={api_bea_key}&method=GetParameterValues&datasetname=NIPA&ParameterName=tablename&ResultFormat=JSON'
  )
) |> 
  content(as = 'text') |> 
  fromJSON() |> 
  pluck(1,2,1)

We can see that the two tables of interest are T10105 (Gross Domestic Product) and T10104 (Price Indexes for Gross Domestic Product). Since the import procedure is the same for both series, it makes sense to create a function with the necessary steps and then use map to apply it to each table name.

bea_tables <- list(
  'Nominal GDP' = 'T10105',
  'Deflator'    = 'T10104'
)
get_bea_data <- function(tablename, api_bea_key) {
  api_bea_request <- glue::glue(
    'https://apps.bea.gov/api/data?UserID={api_bea_key}&method=GetData&DataSetName=NIPA&TableName={tablename}&Frequency=A&Year=ALL&ResultFormat=json'
  )
  gdp_request <- GET(url = api_bea_request)
  gdp_content <- content(gdp_request, as = 'text')
  gdp_list    <- fromJSON(gdp_content, flatten = FALSE)
  gdp_tbl     <- pluck(gdp_list, 1, 2, 4) 
}
bea_data <- map(
  .x = bea_tables,
  .f = function(x) get_bea_data(x, api_bea_key)
)

Next, I arrange the data in tidy format to facilitate further calculations.

bea_data_tbl <- map(
  .x = bea_data,
  .f = function(x){
    x |> 
      filter(LineDescription == 'Gross domestic product') |> 
      select(TimePeriod, DataValue)
  }
) |> 
  plyr::ldply(.id = 'Serie') |> 
  pivot_wider(names_from = Serie, values_from = DataValue) |> 
  mutate(across(c(`Nominal GDP`), ~ str_remove_all(.x, ','))) |> 
  mutate(across(-TimePeriod, ~ .x |> as.numeric())) |> 
  arrange(TimePeriod)
bea_data_tbl
# A tibble: 96 × 3
   TimePeriod `Nominal GDP` Deflator
   <chr>              <dbl>    <dbl>
 1 1929              104556     8.75
 2 1930               92160     8.41
 3 1931               77391     7.58
 4 1932               59522     6.72
 5 1933               57154     6.54
 6 1934               66800     6.86
 7 1935               74241     6.99
 8 1936               84830     7.08
 9 1937               93003     7.34
10 1938               87352     7.20
# ℹ 86 more rows

Now we’re ready to convert nominal GDP into real GDP. To do this, we simply pick an arbitrary year as the reference – say 2005 – and divide the entire price index series by its value in that year. This sets the price index in 2005 equal to 1, with all other values expressed relative to it. Finally, we divide the nominal GDP series by this new price index series.

gdp_real <- bea_data_tbl |> 
  mutate(
    Deflator_2005 = (Deflator / Deflator[which(TimePeriod == 2005)]),
    `Real GDP`    = `Nominal GDP` / Deflator_2005
  )
library(lubridate)
gdp_real |> 
  pivot_longer(-TimePeriod, names_to = 'var', values_to = 'value') |> 
  filter(str_detect(var, 'Deflator', negate = TRUE)) |> 
  mutate(TimePeriod = as_date(TimePeriod, format = '%Y')) |> 
  ggplot(aes(x = TimePeriod)) +
  geom_line(aes(y = value, color = var), lwd = 1) +
  scale_y_continuous(
    labels = scales::dollar_format(
      scale = 1 / 1e6, 
      prefix = "$", 
      suffix = "T"
    )
  ) +
  scale_x_date(date_breaks = '20 years', date_labels = '%Y') +
  theme(legend.position = 'top') +
  labs(
    title = 'US Annual GDP: Nominal vs. Real (2005 Dollars)',
    y = '',
    x = '',
    color = ''
  )

It’s clear that ignoring price changes leads to misinterpretation of the GDP growth trajectory. This is precisely the kind of issue we aim to avoid when adjusting the series for inflation.


  1. pluck(.x, 1,2,1) is equivalent to .x[[1]][[2]][[1]]↩︎