Project 2: U.S. Inflation Rates, 2000 to 2026

Author

Supriya P.

Published

October 11, 2026

Introduction

This dataset was posted by Samantha Mesa in the Week 5 discussion. It comes from the “Current US Inflation Rates: 2000-2026” table on the U.S. Inflation Calculator website (https://www.usinflationcalculator.com/inflation/current-inflation-rates/), which reports annual inflation rates from the Bureau of Labor Statistics Consumer Price Index (all items, U.S. city average, all urban consumers, not seasonally adjusted). Each value is the percent change in prices compared to the same month one year earlier.

The site has no download option, so I copied the table into a CSV in its original layout.

Raw Data Structure

Code
library(tidyverse)
library(knitr)

raw <- read_csv(
  "https://raw.githubusercontent.com/0pree/symmetrical-robot/refs/heads/main/us_inflation_rates.csv",
  col_types = cols(.default = col_character())
)

dim(raw)
[1] 27 14
Code
kable(head(raw, 5))
Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Ave
2026 2.4 2.4 3.3 3.8 4.2 3.5 3.4 3.4 Avail. Oct. 14 NA NA NA NA
2025 3.0 2.8 2.4 2.3 2.4 2.7 2.7 2.9 3.0 – (*) 2.7 2.7 2.6
2024 3.1 3.2 3.5 3.4 3.3 3.0 2.9 2.5 2.4 2.6 2.7 2.9 2.9
2023 6.4 6.0 5.0 4.9 4.0 3.0 3.2 3.7 3.7 3.2 3.1 3.4 4.1
2022 7.5 7.9 8.5 8.3 8.6 9.1 8.5 8.3 8.2 7.7 7.1 6.5 8.0

The table has 27 rows and 14 columns. Each row is a year, each month has its own column, and the last column is the annual average. A few things make it untidy:

  • The month is stored in the column headers instead of in its own column.
  • The annual average is a summary value sitting next to the monthly values.
  • Some cells contain text instead of numbers. September 2026 says “Avail. Oct. 14” because it had not been released yet, and October 2025 shows a dash with an asterisk, which a footnote on the site explains was unavailable due to the 2025 lapse in government appropriations.
  • October through December 2026 and the 2026 annual average are blank.

I read every column as text so these cells load without errors, and converted them on purpose in the next step.

Tidying the Data

Code
inflation <- raw %>%
  select(-Ave) %>%
  pivot_longer(
    cols = -Year,
    names_to = "month",
    values_to = "rate_text"
  ) %>%
  mutate(
    year = as.integer(Year),
    month = factor(month, levels = month.abb),
    rate = if_else(str_detect(rate_text, "^-?[0-9.]+$"), as.numeric(rate_text), NA_real_)
  ) %>%
  select(year, month, rate_text, rate) %>%
  arrange(year, month)

kable(head(inflation, 6))
year month rate_text rate
2000 Jan 2.7 2.7
2000 Feb 3.2 3.2
2000 Mar 3.8 3.8
2000 Apr 3.1 3.1
2000 May 3.2 3.2
2000 Jun 3.7 3.7

I removed the annual average column because it can be recalculated from the monthly values, and keeping it would mix summary values in with the monthly ones. After reshaping, each row is one year and one month.

To convert the rates to numbers, I only kept values that look like a number (an optional minus sign, then digits and a decimal point). Everything else becomes NA. I did not use parse_number() here, because it would have turned “Avail. Oct. 14” into 14, which is not a real inflation rate.

Code
inflation %>%
  filter(is.na(rate)) %>%
  kable()
year month rate_text rate
2025 Oct – (*) NA
2026 Sep Avail. Oct. 14 NA
2026 Oct NA NA
2026 Nov NA NA
2026 Dec NA NA

These five months are the only missing values: one from the 2025 shutdown, one that had not been released yet, and three that have not happened yet. I kept them as NA rather than filling them in, so the averages below are based only on months with real data.

Code
tidy_inflation <- inflation %>%
  select(-rate_text)

Analysis

Yearly Summary

Code
yearly <- tidy_inflation %>%
  group_by(year) %>%
  summarize(
    months_reported = sum(!is.na(rate)),
    avg_rate = round(mean(rate, na.rm = TRUE), 2),
    low = min(rate, na.rm = TRUE),
    high = max(rate, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(range = high - low)

kable(yearly, col.names = c("Year", "Months Reported", "Average Rate (%)", "Lowest Month (%)", "Highest Month (%)", "Range"))
Year Months Reported Average Rate (%) Lowest Month (%) Highest Month (%) Range
2000 12 3.38 2.7 3.8 1.1
2001 12 2.82 1.6 3.7 2.1
2002 12 1.58 1.1 2.4 1.3
2003 12 2.28 1.8 3.0 1.2
2004 12 2.68 1.7 3.5 1.8
2005 12 3.38 2.5 4.7 2.2
2006 12 3.23 1.3 4.3 3.0
2007 12 2.87 2.0 4.3 2.3
2008 12 3.85 0.1 5.6 5.5
2009 12 -0.35 -2.1 2.7 4.8
2010 12 1.62 1.1 2.6 1.5
2011 12 3.17 1.6 3.9 2.3
2012 12 2.08 1.4 2.9 1.5
2013 12 1.48 1.0 2.0 1.0
2014 12 1.63 0.8 2.1 1.3
2015 12 0.12 -0.2 0.7 0.9
2016 12 1.27 0.8 2.1 1.3
2017 12 2.12 1.6 2.7 1.1
2018 12 2.45 1.9 2.9 1.0
2019 12 1.82 1.5 2.3 0.8
2020 12 1.23 0.1 2.5 2.4
2021 12 4.70 1.4 7.0 5.6
2022 12 8.02 6.5 9.1 2.6
2023 12 4.13 3.0 6.4 3.4
2024 12 2.96 2.4 3.5 1.1
2025 11 2.69 2.3 3.0 0.7
2026 8 3.30 2.4 4.2 1.8
Code
ggplot(tidy_inflation, aes(x = make_date(year, as.integer(month)), y = rate)) +
  geom_line(color = "steelblue") +
  geom_hline(yintercept = 2, linetype = "dashed", color = "gray40") +
  labs(
    title = "Monthly U.S. Inflation Rate, 2000 to 2026",
    subtitle = "Dashed line marks 2%",
    x = "Date", y = "Inflation Rate (%)"
  ) +
  theme_minimal()

The highest monthly rate in the data is 9.1% in June 2022, and the lowest is negative 2.1% in July 2009, when prices were falling during the recession. Every month above 5% falls in either 2008 or the 2021 to 2023 spike.

Comparing Periods

Code
periods <- tidy_inflation %>%
  mutate(period = case_when(
    year <= 2007 ~ "2000 to 2007",
    year <= 2019 ~ "2010 to 2019",
    year >= 2021 & year <= 2023 ~ "2021 to 2023",
    year >= 2024 ~ "2024 to 2026"
  )) %>%
  filter(!is.na(period)) %>%
  group_by(period) %>%
  summarize(avg_rate = round(mean(rate, na.rm = TRUE), 2), .groups = "drop")

kable(periods, col.names = c("Period", "Average Monthly Rate (%)"))
Period Average Monthly Rate (%)
2000 to 2007 2.78
2010 to 2019 1.77
2021 to 2023 5.62
2024 to 2026 2.95

Inflation averaged 1.78% between 2010 and 2019, then jumped to 5.62% from 2021 to 2023. Since 2024 it has averaged 2.95%, which is lower than the spike but still above both the 2010s and the 2% line. I left out 2008, 2009, and 2020 from this comparison because they were unusual years (the financial crisis and the start of the pandemic) that would blur the comparison between normal periods.

Does the Annual Average Hide Monthly Swings?

Code
yearly %>%
  slice_max(range, n = 5) %>%
  kable(col.names = c("Year", "Months Reported", "Average Rate (%)", "Lowest Month (%)", "Highest Month (%)", "Range"))
Year Months Reported Average Rate (%) Lowest Month (%) Highest Month (%) Range
2021 12 4.70 1.4 7.0 5.6
2008 12 3.85 0.1 5.6 5.5
2009 12 -0.35 -2.1 2.7 4.8
2023 12 4.13 3.0 6.4 3.4
2006 12 3.23 1.3 4.3 3.0

In some years the annual average hides a lot. In 2021, the average was 4.7%, but the rate climbed from 1.4% in January to 7.0% in December. In 2008, the average was 3.85%, but the rate went from 5.6% in July down to 0.1% in December. In calmer years like 2019, every month stayed within a 0.8 point range.

Checking the Data

Code
check <- yearly %>%
  left_join(
    raw %>% transmute(year = as.integer(Year), site_average = as.numeric(Ave)),
    by = "year"
  ) %>%
  mutate(difference = round(avg_rate - site_average, 2))

kable(check %>% select(year, avg_rate, site_average, difference),
      col.names = c("Year", "My Average (%)", "Site Average (%)", "Difference"))
Year My Average (%) Site Average (%) Difference
2000 3.38 3.4 -0.02
2001 2.82 2.8 0.02
2002 1.58 1.6 -0.02
2003 2.28 2.3 -0.02
2004 2.68 2.7 -0.02
2005 3.38 3.4 -0.02
2006 3.23 3.2 0.03
2007 2.87 2.8 0.07
2008 3.85 3.8 0.05
2009 -0.35 -0.4 0.05
2010 1.62 1.6 0.02
2011 3.17 3.2 -0.03
2012 2.08 2.1 -0.02
2013 1.48 1.5 -0.02
2014 1.63 1.6 0.03
2015 0.12 0.1 0.02
2016 1.27 1.3 -0.03
2017 2.12 2.1 0.02
2018 2.45 2.4 0.05
2019 1.82 1.8 0.02
2020 1.23 1.2 0.03
2021 4.70 4.7 0.00
2022 8.02 8.0 0.02
2023 4.13 4.1 0.03
2024 2.96 2.9 0.06
2025 2.69 2.6 0.09
2026 3.30 NA NA

My yearly averages are within 0.1 percentage points of the site’s annual averages for every year, which confirms the reshaping did not drop or duplicate any values. The small differences come from rounding, since the site likely calculates its averages from unrounded index values. For 2025, my average also leaves out the missing October value.

Conclusions

The main tidying challenge was the text values mixed in with the numbers. Converting only cells that looked like numbers kept those placeholders from turning into fake rates. Once tidy, the data shows that inflation peaked at 9.1% in June 2022 and has come down since, but at about 3% it is still above what was typical in the 2010s. It also shows that a single annual average can hide large changes within the year, like in 2021.

A next step would be to compare inflation with wage growth over the same period to see whether wages kept up with prices, or to break inflation down by category (food, housing, energy) to see what drove the 2022 spike.

AI Citation

Anthropic. (2026). Claude Opus 5.5 [Large language model]. https://claude.ai. Accessed October 2026.