Panel charts with text annotation

Introduction

In this section we will show how to make panel charts with text annotation–the narrative on top of a individual panel charts that you often see in IMF staff reports.

Panel charts of Jamaica’s public finances

This tutorial will guide you through the process of creating panel charts of the public finances of Jamaica. This chart has appeared in the 2022 Jamaica staff report.

Setup

First, we load the required R libraries and source the custom functions that we will use throughout this tutorial.

library(readxl)
library(dplyr)
library(tidyr)
library(here)
library(systemfonts)
library(ragg)
library(textshaping)

source(here("utils/theme_and_colors_IMF_base_R.R"))
source(here("utils/Add_text_to_figure_panel_base_R.R"))

Reading the Data

The data for our charts is stored across multiple sheets within an Excel workbook. We’ll read each sheet into its own R object.

chart_1_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 1 Data")
chart_2_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 2 Data")
chart_3_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 3 Data")
chart_4_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 4 Data")
chart_5_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 5 Data")
chart_6_data <- read_excel(here("databases/SR charts v1.xlsx"), sheet = "Chart 6 Data")

Data Transformation

The data is in a format that is common in IMF spreadsheets:

head(chart_2_data)
# A tibble: 2 × 12
  Year     `2010` `2011` `2012` `2013` `2014` `2015` `2016` `2017` `2018` `2019`
  <chr>     <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1 Total R…   26.8   25.6   25.7   27.1   26.2   27.0   28.0   29.1   30.6   30.6
2 Total E…   33.2   32.0   29.8   27.0   26.7   27.3   28.2   28.6   29.4   29.7
# ℹ 1 more variable: `2020` <dbl>

We need to transform our data into a long format that is suitable for plotting. We’ll define a function make_long for this purpose and apply it to our datasets.

make_long <- function(data) {
  data_long <- data  %>%
    rename(label = Year) %>%
    pivot_longer(-label, names_to = "year", values_to = "value") %>%
    mutate(year = as.numeric(year))
  
  data_long <- data_long %>%
    pivot_wider(names_from = label, values_from = value)
  
  return(data_long)
}

Let’s use this on one file and see the results;

data_2_long <- make_long(chart_2_data)
head(data_2_long)
# A tibble: 6 × 3
   year `Total Revenue` `Total Expenditure`
  <dbl>           <dbl>               <dbl>
1  2010            26.8                33.2
2  2011            25.6                32.0
3  2012            25.7                29.8
4  2013            27.1                27.0
5  2014            26.2                26.7
6  2015            27.0                27.3

Now do all data sets:

data_1_long <- make_long(chart_1_data) 
data_2_long <- make_long(chart_2_data)
data_3_long <- make_long(chart_3_data)
data_4_long <- make_long(chart_4_data)
data_5_long <- make_long(chart_5_data)
data_6_long <- make_long(chart_6_data)

Creating the Plots

With our data in the correct format, we can now create our visualizations using base R.

Let’s first set up the chart theme. We have written functions for you, so all you need to do is to call it before plotting. set_theme is a function that sets the graphical parameters according to the set_imf_theme function.

Optionally, if you want to reset the default settings of R charts, simply run par("default").

set_theme <- set_imf_theme(primary_font, blue)
set_theme()

We then use base R to plot the charts.

Please notice that we created a separate function add_titles(title, subtitle) to add customized titles and subtitles to each plot to keep the overall IMF format consistent.

fig_chart_1_fiscal <- function() {
  set_theme()
  
  barplot(data_1_long$`Fiscal Balance`,
          names.arg = data_1_long$year,
          beside = TRUE,
          col = blue,
          border = "black",
          width = 0.2,
          space = 1,
          ylim = c(-8, 2)
          )
  add_titles("Central Government Balance", "(In percent of GDP)")
  box(lwd = 0.5)
}

fig_chart_1_fiscal()

fig_chart_2_fiscal <- function() {
  set_theme()
  
  plot(data_2_long$year, data_2_long$`Total Revenue`, type = "l", 
       xlim = c(2010, 2020), ylim = c(24, 34), col=blue,lwd=4,
       xlab = "", ylab = "")
  
  add_titles("Central Government Revenues and Expenditures", 
             "(In percent of GDP)")
 
  lines(data_2_long$year, data_2_long$`Total Expenditure`, 
        col = green, lty = "dashed", lwd = 4)

  legend("topright", inset = c(0.02, 0.02), 
         legend = c("Revenue","Expenditure"), 
         col = c(blue, green), 
         lty = c("solid", "dashed"), lwd = 4, bty = "n", cex = 10/12)
  
  axis(1, at = seq(2010, 2020, 1), labels = seq(2010, 2020, 1))
  
  axis(2, at = seq(24, 34, 2), labels = seq(24, 34, 2))
  
  box(lwd = 0.5)
}

fig_chart_2_fiscal()

fig_chart_3_fiscal <- function() {
  set_theme()
  

df_matrix <- t((as.matrix(data_3_long[,c(3,4)])))
colnames(df_matrix) <- data_3_long$year

barplot((df_matrix), beside = FALSE, col = c(green, blue), 
        border = "black", 
        space = 1, ylim = c(0, 30))

legend("top", legend = rownames(df_matrix), fill = c(green, blue), 
       bty = "n", ncol = 2, cex = 10/12)

  
add_titles("Tax Revenue", "(In percent of GDP)")
box(lwd = 0.5)
}

fig_chart_3_fiscal()

fig_chart_4_fiscal <- function() {
  set_theme()
df_matrix <- t((as.matrix(data_4_long[,c(3,4)])))
colnames(df_matrix) <- data_4_long$year



barplot(df_matrix, beside = FALSE, col = c(green, blue), border = "black",
        space = 1, ylim = c(0, 35))

legend("top", legend = rownames(df_matrix), fill = c(green, blue), bty = "n",
       ncol = 1, cex = 10/12)

add_titles("Central Government Current Spending", "(In percent of GDP)")

box(lwd = 0.5)
}

fig_chart_4_fiscal()

fig_chart_5_fiscal <- function() {
  set_theme()
  barplot(data_5_long$`Capital Spending`, names.arg = data_5_long$year, col = blue, 
          border = "black", space = 1, ylim = c(0, 5))
  
  add_titles("Central Government Capital Spending", "(In percent of GDP)")
  
  box(lwd = 0.5)
}

fig_chart_5_fiscal()

fig_chart_6_fiscal <- function() {
  set_theme()
  bp <- barplot(data_6_long$`Debt (Rhs)`, names.arg = data_6_long$year, 
                col = green, border = "black", space = 1, ylim = c(0, 140))
  
  add_titles("Primary Balance and Public Debt", "(In percent of GDP)")

 # data_for_lines <- chart_6_fiscal_long[chart_6_fiscal_long$label != "Debt (Rhs)", ]
  par(new = TRUE)
  
  plot(data_6_long$year, data_6_long$`Primary Balance (Lhs)`, type = "l", 
       col = blue, lwd = 4, xaxt = "n", yaxt = "n", xlab = "", 
       ylab = "", ylim = c(0, 8))
  
  axis(side = 4)
  
  legend("topright", legend = c("Public Debt", "Primary Balance(RHS)"), 
         fill = c(green, blue), border = "black", bty = "n", cex = 10/12)
  
  box(lwd = 0.5)
}

fig_chart_6_fiscal()

Adding captions, combining, and saving charts

Once all individual plots are created, we can combine them into a single figure layout and save it as a .png file.

This step is much simpler in base R than using ggplot. We set up the file to save and the plot grid first, and then plot each of the six charts with proper captions into the grid and the file. After finishing, don’t forget to close the PNG file with dev.off().

# Open a PNG file with specified resolution, width, height, and units.
png("Figures/panel-figure-2-fiscal-baseR.png", res = 600, width = 12.51, height = 15.64, units = "in")

par(oma = c(3, 0, 3, 0),  # Outer margins around the entire figure
    mfrow = c(3, 2)) # Set up a 2x3 plot grid as the panel layout

fig_chart_1_fiscal_text <- 
  "The COVID-19 shock led to a sharp increase in central government's   \nfiscal deficit and debt levels..."
fig_chart_1_fiscal_panel <- AddTextToFigure(fig_chart_1_fiscal,fig_chart_1_fiscal_text)

fig_chart_2_fiscal_text <- 
  "...the revenue-to-GDP ratio declined and the expenditure-to-GDP \nratio increased."
fig_chart_2_fiscal_panel <- AddTextToFigure(fig_chart_2_fiscal,fig_chart_2_fiscal_text)

fig_chart_3_fiscal_text <- 
  "Lower tax revenues in FY 2020 were the result of a decline in GDP and GCT\ntax cuts."
fig_chart_3_fiscal_panel <- AddTextToFigure(fig_chart_3_fiscal,fig_chart_3_fiscal_text)

fig_chart_4_fiscal_text <- 
  "The increase in the spending ratio was largely the result of the decline in \nGDP."
fig_chart_4_fiscal_panel <- AddTextToFigure(fig_chart_4_fiscal,fig_chart_4_fiscal_text)

fig_chart_5_fiscal_text <- 
  "While capital spending was held back to make room for COVID-19 \nrelated spending..."
fig_chart_5_fiscal_panel <- AddTextToFigure(fig_chart_5_fiscal,fig_chart_5_fiscal_text)

fig_chart_6_fiscal_text <- 
  "...public debt rose in FY 2020 but is on a downward path to meet the FRL \ntarget."
fig_chart_6_fiscal_panel <- AddTextToFigure(fig_chart_6_fiscal,fig_chart_6_fiscal_text)


title("Figure 7. Fiscal Sector Developments", outer = TRUE, line = 1.8, 
      cex.main = 1.5, font = 2, adj = 0.5)

mtext("Sources: Bank of Jamaica and IMF staff estimates and projections.", 
      side = 1, outer = TRUE, line = 0, cex = 1, adj = 0.05)

invisible(dev.off())

Conclusion

In this tutorial, we have covered the steps to import, process, and visualize fiscal data in base R. The resulting visualizations provide a compelling graphical representation of fiscal trends and developments.