---
title: "The World's Largest “Investor Countries” Are Often Financial Hubs"
subtitle: "Five of the ten largest sources of foreign direct investment in 2024 were recognized offshore financial centers — including Luxembourg, Hong Kong, and the British Virgin Islands."
description: "A redesign of Visual Capitalist's 'World's Largest Investor Countries,' revealing that five of the ten largest sources of 2024 foreign direct investment are offshore financial centers rather than major economies. A ranked bar chart flags conduit and sink jurisdictions using a published OFC classification. Built in R with ggplot2."
date: "2026-06-16"
author:
- name: "Steven Ponce"
url: "https://stevenponce.netlify.app"
citation:
url: "https://stevenponce.netlify.app/data_visualizations/MakeoverMonday/2026/mm_2026_24.html"
categories: ["MakeoverMonday", "2026"]
tags: [
"makeover-monday",
"data-visualization",
"ggplot2",
"bar-chart",
"ranked-bar-chart",
"foreign-direct-investment",
"fdi",
"offshore-financial-centers",
"global-finance",
"annotation",
"ggtext",
"2026"
]
image: "thumbnails/mm_2026_24.png"
format:
html:
toc: true
toc-depth: 5
code-link: true
code-fold: true
code-tools: true
code-summary: "Show code"
editor_options:
chunk_output_type: inline
execute:
freeze: true
cache: true
error: false
message: false
warning: false
eval: true
---
```{r}
#| label: setup-links
#| include: false
# CENTRALIZED LINK MANAGEMENT
## Project-specific info
current_year <- 2026
current_week <- 24
project_file <- "mm_2026_24.qmd"
project_image <- "mm_2026_24.png"
## Data Sources
data_main <- "https://data.world/makeovermonday/2026w23-global-top-foreign-investments"
data_secondary <- "https://data.world/makeovermonday/2026w23-global-top-foreign-investments"
## Repository Links
repo_main <- "https://github.com/poncest/personal-website/"
repo_file <- paste0("https://github.com/poncest/personal-website/blob/master/data_visualizations/MakeoverMonday/", current_year, "/", project_file)
## External Resources/Images
chart_original <- "https://raw.githubusercontent.com/poncest/MakeoverMonday/refs/heads/master/2026/Week_24/original_chart.png"
## Organization/Platform Links
org_primary <- "https://www.visualcapitalist.com/the-worlds-largest-investor-countries/"
org_secondary <- "https://www.visualcapitalist.com/the-worlds-largest-investor-countries/"
# Helper function to create markdown links
create_link <- function(text, url) {
paste0("[", text, "](", url, ")")
}
# Helper function for citation-style links
create_citation_link <- function(text, url, title = NULL) {
if (is.null(title)) {
paste0("[", text, "](", url, ")")
} else {
paste0("[", text, "](", url, ' "', title, '")')
}
}
```
### Original
The original visualization comes from `r create_link("Global Top Foreign Investments", data_secondary)`

### Makeover
{#fig-1}
### [**Steps to Create this Graphic**]{.mark}
#### [1. Load Packages & Setup]{.smallcaps}
```{r}
#| label: load
#| warning: false
#| message: false
#| results: "hide"
## 1. LOAD PACKAGES & SETUP ----
suppressPackageStartupMessages({
if (!require("pacman")) install.packages("pacman")
pacman::p_load(
tidyverse, ggtext, showtext, scales, glue, janitor
)
})
### |- figure size ----
camcorder::gg_record(
dir = here::here("temp_plots"),
device = "png",
width = 8,
height = 9,
units = "in",
dpi = 320
)
# Source utility functions
suppressMessages(source(here::here("R/utils/fonts.R")))
source(here::here("R/utils/social_icons.R"))
source(here::here("R/utils/image_utils.R"))
source(here::here("R/themes/base_theme.R"))
```
#### [2. Read in the Data]{.smallcaps}
```{r}
#| label: read
#| include: true
#| eval: true
#| warning: false
#|
df_raw <- readxl::read_excel(
here::here("data/MakeoverMonday/2026/MM2026-WK24-LargestFDI.xlsx")) |>
clean_names()
```
#### [3. Examine the Data]{.smallcaps}
```{r}
#| label: examine
#| include: true
#| eval: true
#| results: 'hide'
#| warning: false
glimpse(df_raw)
skimr::skim_without_charts(df_raw)
```
#### [4. Tidy Data]{.smallcaps}
```{r}
#| label: tidy
#| warning: false
### |- OFC classification (Garcia-Bernardo et al. 2017) ----
conduit_ofc <- c(
"Netherlands", "United Kingdom", "Switzerland",
"Singapore", "Ireland"
)
sink_ofc <- c(
"British Virgin Islands", "Cayman Islands", "Bermuda",
"Luxembourg", "Hong Kong", "Jersey", "Mauritius"
)
norm <- function(x) str_squish(str_to_lower(x))
df <- df_raw |>
rename(fdi_m = fdi_outflows_in_2024_m) |>
mutate(country = if_else(country == "enmark", "Denmark", country)) |>
arrange(desc(fdi_m)) |>
mutate(
rank = row_number(),
is_ofc = norm(country) %in% norm(c(conduit_ofc, sink_ofc)),
category = if_else(is_ofc,
"Offshore financial center",
"Productive economy"
)
)
global_total <- sum(df$fdi_m)
top6_share <- sum(df$fdi_m[df$rank <= 6]) / global_total
### |- top 20 for the chart ----
df_plot <- df |>
filter(rank <= 20) |>
mutate(
country = fct_reorder(country, fdi_m),
label_b = paste0("$", comma(round(fdi_m / 1000)), "B")
)
```
#### [5. Visualization Parameters]{.smallcaps}
```{r}
#| label: params
#| include: true
#| warning: false
### |- plot aesthetics ----
clrs <- get_theme_colors(
palette = list(
ofc = "#8E3B46",
productive = "#B7B4AC",
text_dark = "#2B2B2B",
text_muted = "#6E6B66"
)
)
col_ofc <- clrs$palette$ofc
col_prod <- clrs$palette$productive
col_dark <- clrs$palette$text_dark
col_muted <- clrs$palette$text_muted
### |- titles and caption ----
title_text <- str_glue(
"The World's Largest \u201cInvestor Countries\u201d ",
"Are Often Financial Hubs"
)
subtitle_text <- str_glue(
"Five of the ten largest sources of foreign direct investment in 2024 were ",
"recognized <b style='color:{col_ofc}'>offshore financial centers</b> \u2014 ",
"including Luxembourg, Hong Kong, and the British Virgin Islands."
)
caption_text <- create_mm_caption(
mm_year = current_year,
mm_week = current_week,
source_text = paste0(
"Visual Capitalist \u00b7 FDI outflows 2024 \u00b7 top 20 of 50 shown ",
"(data.world)<br>",
"OFC classification: Garcia-Bernardo et al. (2017) \u2014 academic, ",
"not an official designation"
)
)
### |- annotation text ----
ann_group <- "Offshore financial\ncenters"
ann_mech <- str_glue(
"Luxembourg and the British Virgin Islands each report\n",
"larger outflows than Germany or India."
)
### |- annotation coordinates ----
grp_x <- 128000; grp_y <- 17.3
mech_x <- 128000; mech_y <- 14.2
### |- fonts ----
setup_fonts()
fonts <- get_font_families()
### |- plot theme ----
base_theme <- create_base_theme(clrs)
weekly_theme <- extend_weekly_theme(
base_theme,
theme(
plot.title = element_markdown(
face = "bold", size = rel(1.48),
margin = margin(b = 4), family = fonts$title_1
),
plot.subtitle = element_textbox_simple(
size = rel(0.85), color = col_muted, lineheight = 1.1,
width = unit(1, "npc"), margin = margin(b = 16), family = fonts$subtitle
),
plot.caption = element_markdown(
size = rel(0.5), color = col_muted,
hjust = 0, margin = margin(t = 14), family = fonts$caption
),
axis.text.y = element_text(size = rel(0.95), color = col_dark),
axis.text.x = element_text(color = col_muted),
axis.title = element_blank(),
panel.grid.major.x = element_line(color = "gray90", linewidth = 0.3),
panel.grid.major.y = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks = element_blank(),
legend.position = "none"
)
)
theme_set(weekly_theme)
```
#### [6. Plot]{.smallcaps}
```{r}
#| label: plot
#| warning: false
p <- ggplot(df_plot, aes(x = fdi_m, y = country, fill = category)) +
# Geoms
geom_col(width = 0.74) +
geom_text(aes(label = label_b),
hjust = -0.15, size = 3.2,
color = col_dark
) +
# Annotate
annotate("text",
x = grp_x, y = grp_y, label = ann_group,
hjust = 0, vjust = 1, color = col_ofc, fontface = "bold",
size = 4.0, lineheight = 0.95
) +
annotate("text",
x = mech_x, y = mech_y, label = ann_mech,
hjust = 0, vjust = 1, color = col_muted, size = 3.1,
lineheight = 0.95
) +
# Scales
scale_fill_manual(values = c(
"Offshore financial center" = col_ofc,
"Productive economy" = col_prod
)) +
scale_x_continuous(
labels = label_number(
scale = 1e-3, prefix = "$", suffix = "B",
accuracy = 1
),
expand = expansion(mult = c(0, 0.12))
) +
# Labs
labs(
title = title_text,
subtitle = subtitle_text,
caption = caption_text
) +
coord_cartesian(clip = "off")
```
#### [7. Save]{.smallcaps}
```{r}
#| label: save
#| warning: false
### |- plot image ----
save_plot(
plot = p ,
type = "makeovermonday",
year = current_year,
week = current_week,
width = 8,
height = 9
)
```
#### [8. Session Info]{.smallcaps}
::: {.callout-tip collapse="true"}
##### Expand for Session Info
```{r, echo = FALSE}
#| eval: true
#| warning: false
sessionInfo()
```
:::
#### [9. GitHub Repository]{.smallcaps}
::: {.callout-tip collapse="true"}
##### Expand for GitHub Repo
The complete code for this analysis is available in `r create_link(project_file, repo_file)`.
For the full repository, `r create_link("click here", repo_main)`.
:::
#### [10. References]{.smallcaps}
::: {.callout-tip collapse="true"}
##### Expand for References
**Primary Data (Makeover Monday):**
1. Makeover Monday `r current_year` Week `r current_week`: `r create_link("Global Top Foreign Investments", "https://data.world/makeovermonday/2026w23-global-top-foreign-investments")`
- CSV: 50 rows × 3 columns (`country`, `abbr`, `fdi_outflows_in_2024_m`); FDI outflows in 2024, USD millions; the top 50 source countries/jurisdictions
2. Original Chart: `r create_link("Visual Capitalist — Ranked: The World's Biggest Sources of Foreign Investment", "https://www.visualcapitalist.com/the-worlds-largest-investor-countries/")`
- Author: Gabriel Cohen; Design: Amy Kuo; published 2026-06-08
- Format: circular Voronoi treemap, FDI outflows by country, continents color-coded
**Source Data:**
3. `r create_link("UNCTAD — World Investment Report 2025", "https://unctad.org/publication/world-investment-report-2025")`
- Ultimate source for the FDI outflow figures (UN Trade and Development); both the Visual Capitalist graphic and the MakeoverMonday dataset derive from this report
- Coverage: 152 source countries/jurisdictions; the MakeoverMonday dataset is the top 50 by outflow
- Unit: FDI outflows in 2024, current USD millions
**Classification (analytical layer added in the makeover):**
4. `r create_link("Garcia-Bernardo, Fichtner, Takes & Heemskerk (2017) — Uncovering Offshore Financial Centers", "https://www.nature.com/articles/s41598-017-06322-9")`
- *Scientific Reports* 7, 6246
- Used to flag conduit-OFCs (Netherlands, Singapore, United Kingdom, Ireland) and sink-OFCs (Luxembourg, Hong Kong, British Virgin Islands, Cayman Islands) within the ranking
- Academic classification, not an official government designation
**Note:** FDI outflow figures originate from UNCTAD's *World Investment Report 2025*; more than $1.7 trillion was invested abroad in 2024, and six sources (United States, Japan, China, Luxembourg, Hong Kong, Canada) accounted for roughly $915 billion — 54.3% of the 50-country total of $1,684,328M used here. The MakeoverMonday dataset is the top 50 of 152 jurisdictions; this chart shows the top 20 of those 50. Offshore financial centers are classified per Garcia-Bernardo et al. (2017) — an academic, not official, designation; 8 of the 50 countries (5 of the top 10) are OFCs, and they are scattered through the ranking rather than clustered, which is why color encodes membership independent of rank. Hong Kong and Macau are reported separately from China (per UNCTAD) and are not summed into it. The makeover encodes a distinction the original chart omitted but its own article acknowledged: Visual Capitalist's text notes that Luxembourg, Hong Kong, the British Virgin Islands, the Cayman Islands, and the Netherlands "often act as conduits" for global investment, routed for tax, regulatory, or corporate-structuring reasons. No causal relationship is asserted: the chart describes the composition of the ranking — where capital is routed versus where it originates — not why, and "financial hubs" refers to jurisdictions classified as offshore financial centers. Annotation claims verified against the data: Luxembourg ($108,598M) exceeds Germany ($38,525M) and France ($40,950M); the British Virgin Islands ($59,451M) exceeds India ($23,782M).
:::
#### [11. Custom Functions Documentation]{.smallcaps}
::: {.callout-note collapse="true"}
##### 📦 Custom Helper Functions
This analysis uses custom functions from my personal module library for efficiency and consistency across projects.
**Functions Used:**
- **`fonts.R`**: `setup_fonts()`, `get_font_families()` - Font management with showtext
- **`social_icons.R`**: `create_social_caption()` - Generates formatted social media captions
- **`image_utils.R`**: `save_plot()` - Consistent plot saving with naming conventions
- **`base_theme.R`**: `create_base_theme()`, `extend_weekly_theme()`, `get_theme_colors()` - Custom ggplot2 themes
**Why custom functions?**\
These utilities standardize theming, fonts, and output across all my data visualizations. The core analysis (data tidying and visualization logic) uses only standard tidyverse packages.
**Source Code:**\
View all custom functions → [GitHub: R/utils](https://github.com/poncest/personal-website/tree/master/R)
:::