INGOs and their work

Code
library(tidyverse)
library(targets)
library(sf)
library(countrycode)
library(scales)
library(gt)
library(skimr)
library(ggalluvial)
library(here)

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

tar_load(c(survey_orgs, survey_countries, survey_all))
tar_load(c(world_map))
tar_load(c(summary_activities, models_activities))

# Plotting functions
invisible(list2env(tar_read(graphic_functions), .GlobalEnv))
invisible(list2env(tar_read(table_functions), .GlobalEnv))
set_annotation_fonts()

Where are NGOs based?

How are these NGOs distributed by HQ?

Code
df_hq_countries <- survey_orgs %>% 
  group_by(Q2.2_iso3) %>% 
  summarize(num_ngos = n()) %>% 
  ungroup() %>% 
  mutate(num_ngos_ceiling = ifelse(num_ngos >= 30, 30, num_ngos),
         ngo_presence = ifelse(num_ngos >= 1, "At least one NGO respondent", NA),
         prop = num_ngos / sum(num_ngos, na.rm = TRUE)) %>% 
  mutate(country_name = countrycode(Q2.2_iso3, "iso3c", "country.name")) %>%
  arrange(desc(num_ngos))

df_hq_countries %>% 
  select(country_name, Q2.2_iso3, num_ngos, prop) %>%
  gt() %>% 
  cols_label(
    country_name = "Country name",
    Q2.2_iso3 = "ISO3",
    num_ngos = "NGOs",
    prop = "Proportion"
  ) %>% 
  fmt_percent(columns = prop, decimals = 1) %>% 
  opts_int() %>% opts_theme()

Number of unique HQ countries

Code
df_hq_countries %>% 
  summarize(total = sum(num_ngos >= 1, na.rm = TRUE))
## # A tibble: 1 × 1
##   total
##   <int>
## 1   100

Regional distribution of HQ countries

Code
df_hq_regions <- df_hq_countries %>%
  mutate(region = countrycode(Q2.2_iso3, "iso3c", "continent"),
         region = ifelse(Q2.2_iso3 == "TWN", "Asia", region)) %>%
  mutate(region = recode(region, Oceania = "Asia &\nOceania", Asia = "Asia &\nOceania")) %>%
  group_by(region) %>%
  summarise(num = sum(num_ngos)) %>% ungroup() %>%
  mutate(prop = num / sum(num)) %>%
  arrange(desc(num)) %>%
  mutate(region = fct_rev(fct_inorder(region)))

df_hq_regions %>% 
  gt() %>% 
  cols_label(
    region = "Region",
    num = "NGOs",
    prop = "Proportion"
  ) %>% 
  fmt_percent(columns = prop, decimals = 1) %>% 
  cols_align(align = "left", columns = region) %>% 
  opts_theme() %>% 
  opts_int(use_pagination = FALSE, use_pagination_info = FALSE)
Code
ggplot(df_hq_regions, aes(x = num, y = region)) +
  geom_pointrange(aes(xmin = 0, xmax = num)) +
  scale_x_continuous(sec.axis = sec_axis(~ . / sum(df_hq_regions$num),
                                         labels = label_percent())) +
  labs(x = "Respondents based in region", y = NULL,
       title = "Region of headquarters",
       subtitle = "Q2.2: Where is your organization's headquarters? (summarized by region)") +
  theme_ingo() + 
  theme(panel.grid.major.y = element_blank())

Responses per country (30 NGO ceiling)

Code
map_hq <- world_map %>% 
  left_join(df_hq_countries, by = join_by(ISO_A3 == Q2.2_iso3))

ggplot() +
  geom_sf(data = map_hq, aes(fill = num_ngos_ceiling),
          linewidth = 0.1, color = "grey30") +
  scale_fill_binned(low = clrs$Peach[1], high = clrs$Peach[7], na.value = "grey95",
                    labels = c(1, 10, 20, "30+"), breaks = c(1, 10, 20, 30),
                    name = "Number of NGOs: ",
                    guide = guide_colorsteps(barwidth = 7, barheight = 0.4,
                                             title.vjust = 1)) +
  coord_sf(crs = "+proj=robin") +
  theme_ingo_map()

Countries with at least one response

Code
ggplot() +
  geom_sf(data = map_hq, aes(fill = ngo_presence),
          linewidth = 0.1, color = "grey30") +
  scale_fill_manual(values = c(clrs$Peach[7], "grey95"), 
                    na.value = "grey95", na.translate = FALSE,
                    name = NULL) +
  coord_sf(crs = "+proj=robin") +
  theme_ingo_map()

Where do NGOs work?

How are these NGOs distributed by target country?

Code
possible_countries <- tibble(ISO3 = unique(as.character(world_map$ISO_A3))) %>% 
  filter(ISO3 != "-99")

df_work_countries_all <- survey_orgs %>%
  unnest(Q2.5_iso3) %>%
  group_by(Q2.5_iso3) %>%
  summarise(num_ngos = n()) %>%
  right_join(possible_countries, by = join_by(Q2.5_iso3 == ISO3)) %>% 
  mutate(prop = num_ngos / sum(num_ngos, na.rm = TRUE),
         country_name = countrycode(Q2.5_iso3, "iso3c", "country.name",
                                    custom_match = c("XKX" = "Kosovo")),
         presence = num_ngos >= 1) %>%
  arrange(desc(num_ngos))

df_work_countries_answered <- survey_all %>%
  group_by(Q4.1_iso3) %>%
  summarise(num = n())

df_work_countries_all %>% 
  select(country_name, Q2.5_iso3, num_ngos, prop) %>%
  gt() %>% 
  cols_label(
    country_name = "Country name",
    Q2.5_iso3 = "ISO3",
    num_ngos = "NGOs",
    prop = "Proportion"
  ) %>% 
  fmt_percent(columns = prop, decimals = 1) %>% 
  opts_int() %>% opts_theme()

Number of unique target countries

Respondents indicated working in this many unique countries:

Code
df_work_countries_all %>% 
  summarize(total = sum(num_ngos >= 1, na.rm = TRUE))
## # A tibble: 1 × 1
##   total
##   <int>
## 1   167
Code
df_work_countries_answered %>% 
  nrow()
## [1] 148

Countries per response

Code
countries_per_response <- survey_orgs %>%
  unnest(Q2.5_iso3) %>%
  group_by(clean.id) %>%
  summarise(num = n())

skim_without_charts(countries_per_response$num) %>% 
  select(-c(skim_type, skim_variable, complete_rate)) %>% 
  rename_with(~str_remove(., "numeric\\.")) %>% 
  gt() %>% 
  opts_theme() %>% 
  opt_horizontal_padding(scale = 2)
n_missing mean sd p0 p25 p50 p75 p100
0 15.7 27.1 1 2 6 18 212
Code
ggplot(countries_per_response, aes(x = num)) +
  geom_histogram(binwidth = 2, color = "white", linewidth = 0.1) + 
  labs(x = "Number of countries selected", y = "Frequency") +
  theme_ingo()

Flows between countries selected

Code
continents_marked <- survey_orgs %>%
  select(home = Q2.2_iso3, target = Q2.5_iso3) %>%
  unnest(target) %>% 
  mutate(across(c(home, target), 
                list(continent = ~countrycode(., "iso3c", "continent",
                                              custom_match = c("XKX" = "Europe"))))) %>% 
  mutate(across(ends_with("_continent"),
                ~recode(., Oceania = "Asia &\nOceania", Asia = "Asia &\nOceania"))) %>% 
  group_by(home_continent, target_continent) %>%
  summarize(times_marked = n()) %>% 
  ungroup() %>% 
  mutate(id = 1:n()) %>% 
  pivot_longer(ends_with("continent"), names_to = "x", values_to = "continent")

ggplot(continents_marked,
       aes(x = x, y = times_marked, stratum = continent, alluvium = id, fill = continent)) +
  geom_flow() +
  geom_stratum(color = "grey30", alpha = 0.5, linewidth = 0.2) +
  geom_text(stat = "stratum", aes(label = continent, color = continent), lineheight = 0.9,
            family = "Fira Sans Semibold") +
  scale_x_discrete(labels = c("Home region", "Target region"),
                   expand = c(0, 0)) +
  scale_y_continuous(expand = c(0, 0)) +
  scale_fill_manual(values = c(clrs$Prism[6], clrs$Prism[8], clrs$Prism[11], clrs$Prism[2]),
                    guide = "none") +
  scale_color_manual(values = colorspace::darken(c(clrs$Prism[6], clrs$Prism[8], 
                                                   clrs$Prism[11], clrs$Prism[2]), 0.5),
                    guide = "none") +
  labs(x = NULL, y = NULL,
       title = "Flows between INGO home and target regions",
       subtitle = "European NGOs tend to work in Europe; American NGOs go all over the place") +
  theme_ingo() +
  theme(panel.grid.major.x = element_blank(),
        panel.grid.major.y = element_blank(),
        axis.text.x = element_text(family = "Fira Sans Semibold", size = rel(1.3)),
        axis.text.y = element_blank())

Regions worked in

Code
df_target_regions <- survey_orgs %>%
  select(target = Q2.5_iso3) %>%
  unnest(target) %>% 
  mutate(target = countrycode(target, "iso3c", "continent",
                              custom_match = c("XKX" = "Europe"))) %>% 
  mutate(target = recode(target, Oceania = "Asia &\nOceania", Asia = "Asia &\nOceania")) %>% 
  group_by(target) %>% 
  summarize(times_marked = n()) %>% 
  mutate(prop = times_marked / sum(times_marked)) %>% 
  arrange(times_marked) %>% 
  mutate(target = fct_inorder(target))

df_target_regions %>% 
  gt() %>% 
  cols_label(
    target = "Region",
    times_marked = "Times marked",
    prop = "Proportion"
  ) %>% 
  fmt_percent(columns = prop, decimals = 1) %>% 
  fmt_number(columns = times_marked, decimals = 0) %>% 
  cols_align(align = "left", columns = target) %>% 
  opts_theme() %>% 
  opts_int(use_pagination = FALSE, use_pagination_info = FALSE)
Code
ggplot(df_target_regions, aes(x = times_marked, y = target)) +
  geom_pointrange(aes(xmin = 0, xmax = times_marked)) +
  scale_x_continuous(labels = label_comma(),
                     sec.axis = sec_axis(~ . / sum(df_target_regions$times_marked),
                                         labels = label_percent())) +
  labs(x = "Times country in region selected", y = NULL,
       title = "Region of target countries",
       subtitle = "Q2.5: Where does your organization work? (summarized by region)") +
  theme_ingo() + 
  theme(panel.grid.major.y = element_blank())

What do these NGOs do?

Which issues do these NGOs work on?

Code
df_issues_most <- survey_orgs %>%
  group_by(Q3.2.clean, potential.contentiousness) %>%
  summarise(num = n()) %>%
  ungroup() %>%
  arrange(desc(num)) %>%
  mutate(issue = factor(Q3.2.clean, levels = rev(unique(Q3.2.clean)), ordered = TRUE)) %>%
  mutate(prop = num / sum(num), cum.prop = cumsum(prop))

ggplot(df_issues_most, aes(x = num, y = issue, color = potential.contentiousness)) +
  geom_pointrange(aes(xmin = 0, xmax = num)) +
  scale_x_continuous(sec.axis = sec_axis(~ . / sum(df_issues_most$num),
                                         labels = label_percent())) +
  scale_color_manual(values = c(clrs$Prism[2], clrs$Prism[8]), name = NULL) +
  labs(x = "Organizations", y = NULL) +
  theme_ingo() + 
  theme(legend.position = c(1, 0), legend.justification = c(1, 0),
        legend.direction = "horizontal", panel.grid.major.y = element_blank())

What kinds of activities do these NGOs engage in?

Code
ggplot(summary_activities$df_activities_collapsed, 
       aes(x = prop, y = fct_rev(response), color = potential.contentiousness)) +
  geom_pointrange(aes(xmin = 0, xmax = prop), position = position_dodge(width = 0.75),
                   size = 0.5) +
  labs(x = "Proportion selected", y = "How often INGO does work") +
  scale_color_manual(values = c(clrs$Prism[2], clrs$Prism[8]), name = NULL,
                     guide = guide_legend(reverse = TRUE)) +
  scale_x_continuous(labels = label_percent()) +
  facet_wrap(~ question, ncol = 2) +
  theme_ingo() + 
  theme(legend.position = c(1, 0), legend.justification = c(1, 0),
        legend.direction = "vertical", strip.text = element_text(size = rel(0.75)),
        panel.grid.major.y = element_blank())

Code
models_activities %>% 
  unnest(diffs_summary) %>% 
  mutate(nice_value = glue::glue(
    "{diff}<br>({p})",
    diff = label_number(style_negative = "minus", accuracy = 0.01)(y),
    p = label_number(accuracy = 0.01)(p_greater_0))
  ) %>% 
  ungroup() %>% 
  select(question, response, nice_value) %>% 
  pivot_wider(names_from = "response", values_from = "nice_value") %>% 
  gt() %>%
  fmt_markdown(columns = 2:4) %>%
  cols_align(align = "left", columns = question) %>%
  cols_label(question = "") %>% 
  tab_footnote(footnote = "Values show the differences in proportion of high and low contention INGOs providing each response (high contention proportion − low contention proportion)") %>% 
  opts_theme()

Median differences in proportion of high and low contention INGOs selecting frequency of type of work. Probability that the proportion is greater than 0 given in parentheses.

Almost always Sometimes Never
Engaging in advocacy

0.17
(1.00)

−0.11
(0.00)

−0.04
(0.05)

Engaging in research and public education

0.01
(0.58)

−0.01
(0.43)

0.01
(0.69)

Monitoring and assessing the effects of policies

0.13
(1.00)

−0.07
(0.05)

−0.05
(0.05)

Providing direct aid and services

−0.10
(0.01)

0.07
(0.93)

0.05
(0.94)

Mobilizing people

0.08
(0.96)

−0.03
(0.29)

−0.04
(0.15)

Values show the differences in proportion of high and low contention INGOs providing each response (high contention proportion − low contention proportion)

Challenges and obstacles to mission

Code
df_obstacles_raw <- survey_orgs %>% 
  filter(!is.na(Q3.12_obstacle)) %>%
  mutate(obstacle = str_split(Q3.12_obstacle, ", ")) %>%
  unnest(obstacle) %>%
  mutate(obstacle = recode(obstacle,
                           `access to country` = "bureaucratic, legal, and political",
                           `political context` = "bureaucratic, legal, and political",
                           `bureaucratic and legal` = "bureaucratic, legal, and political"
  )) %>%
  group_by(obstacle) %>%
  summarise(num = n()) %>%
  arrange(desc(num))

df_obstacles_denom <- survey_orgs %>%
  filter(!is.na(Q3.12_obstacle)) %>%
  nrow()

df_obstacles <- df_obstacles_raw %>% 
  mutate(prop = num / df_obstacles_denom) %>% 
  mutate(obstacle = str_to_sentence(obstacle)) %>% 
  filter(num > 20, obstacle != "Other") %>%
  mutate(obstacle = fct_inorder(obstacle))

ggplot(df_obstacles, aes(x = prop, y = fct_rev(obstacle))) +
  geom_pointrange(aes(xmin = 0, xmax = prop)) +
  scale_x_continuous(labels = label_percent()) +
  labs(x = "Proportion of respondents", y = NULL) +
  theme_ingo() + 
  theme(panel.grid.major.y = element_blank())