Why hierarchical models?

Code
library(tidyverse)
library(targets)
library(scales)
library(patchwork)

tar_config_set(store = here::here('_targets'),
               script = here::here('_targets.R'))

font_opts <- list(extra.preamble = c("\\usepackage{libertine}", 
                                     "\\usepackage{libertinust1math}"),
                  dvisvgm.opts = "--font-format=woff")

# Load data 
# Plotting functions
invisible(list2env(tar_read(graphic_functions), .GlobalEnv))
invisible(list2env(tar_read(misc_funs), .GlobalEnv))

df_country_aid <- tar_read(country_aid_final)
df_country_aid_laws <- filter(df_country_aid, laws)

Hierarchical structure

Our data includes information about 142 countries across 24 years (from 1990–2013). This kind of time series cross-sectional (TSCS) data reflects a natural hierarchical structure, with repeated yearly observations nested within countries (see this whole guide I wrote because of this exact project)

Multilevel panel data structure, with yearly observations of \(y\) nested in countries

The path of the three countries with the biggest changes in civil society repression (Afghanistan, Colombia, and Iraq), with each path beginning in 1990 and ending in 2013:

Code
biggest_movers <- df_country_aid_laws %>% 
  group_by(country) %>% 
  filter(!any(total_oda == 0)) %>% 
  summarize(across(c(v2csreprss, total_oda), lst(min, max, diff = ~max(.) - min(.))))

top_overall <- top_n(biggest_movers, 15, v2csreprss_diff) %>% 
  filter(country %in% top_n(biggest_movers, 15, total_oda_diff)$country) %>% 
  top_n(3, v2csreprss_diff)

df_cs_oda_highlight <- df_country_aid_laws %>% 
  mutate(highlight_country = ifelse(country %in% top_overall$country, 
                                    country, "Other"),
         highlight_country = factor(highlight_country, ordered = TRUE),
         highlight_country = fct_relevel(highlight_country, "Other", after = Inf)) %>% 
  mutate(highlight = highlight_country != "Other")

ggplot(df_cs_oda_highlight, aes(x = v2csreprss, y = total_oda, group = country)) +
  geom_point(size = 0.15, alpha = 0.10) +
  geom_smooth(method = "lm", aes(group = NULL), color = clrs$Prism[6], 
              linewidth = 1.25, linetype = "21", se = FALSE, formula = y ~ x) +
  geom_path(aes(color = highlight_country, size = highlight),
            arrow = arrow(type = "open", angle = 30, length = unit(0.75, "lines")),
            key_glyph = "timeseries") +
  scale_y_continuous(trans = "log1p", breaks = c(1e7, 1e8, 1e9, 1e10, 1e11),
                     labels = label_dollar(scale_cut = cut_short_scale())) +
  scale_size_manual(values = c(0.045, 1.25), guide = "none") +
  scale_color_manual(values = c(clrs$Prism[2], clrs$Prism[7], clrs$Prism[9], "grey50"),
                     guide = guide_legend(override.aes = list(linewidth = 1))) +
  coord_cartesian(ylim = c(1e7, 1e11)) +
  labs(x = "Civil society repression\n(Higher values represent less repression)",
       y = "Total ODA", color = NULL, size = NULL) +
  theme_donors()

Scatterplot of the relationship between CS repression and aid, limited only to 2010 for the sake of simplicity:

Code
df_country_aid_2010 <- df_country_aid_laws %>% 
  filter(year == 2010)

ggplot(df_country_aid_2010, aes(x = v2csreprss, y = total_oda)) +
  geom_point(size = 1, alpha = 0.9) +
  geom_smooth(method = "lm", color = clrs$Prism[6], 
              linewidth = 1.25, linetype = "21", se = FALSE, formula = y ~ x) +
  scale_x_continuous(labels = label_number(style_negative = "minus")) +
  scale_y_continuous(labels = label_dollar(scale_cut = cut_short_scale())) +
  labs(x = "Civil society repression\n(Higher values represent less repression)",
       y = "Total ODA") +
  theme_donors()

Code
df_country_aid_a_few <- df_country_aid_laws %>% 
  filter(year %in% c(1990, 1995, 2000, 2005, 2010, 2013))

ggplot(df_country_aid_a_few, aes(x = v2csreprss, y = total_oda)) +
  geom_point(size = 1, alpha = 0.9) +
  geom_smooth(method = "lm", color = clrs$Prism[6], 
              linewidth = 1.25, linetype = "21", se = FALSE, formula = y ~ x) +
  scale_x_continuous(labels = label_number(style_negative = "minus")) +
  scale_y_continuous(labels = label_dollar(scale_cut = cut_short_scale())) +
  labs(x = "Civil society repression\n(Higher values represent less repression)",
       y = "Total ODA") +
  facet_wrap(vars(year)) +
  theme_donors()