This notebook produces the descriptive statistics tables for Section 5.1 and the H1 concessionality regression for Section 5.2 of the thesis.
library(sandwich)
library(lmtest)
library(car)
library(stargazer)
library(ggplot2)
library(dplyr)
library(tidyr)
library(kableExtra)
df <- read.csv("~/Master Thesis/Analysis/r_dataset_3_regression_complete.csv",
stringsAsFactors = FALSE)
cat("Dataset loaded:", nrow(df), "observations\n")
## Dataset loaded: 6557 observations
This is the main descriptive table for Section 5.1. Reports N, Mean, SD, Min, Median, Max for all key variables.
desc_vars <- df[, c("total_mob", "ihs_mob_total", "guarantee_share",
"direct_inv_share", "synd_loan_share", "credit_line_share",
"concessional_mechanism_share", "first_loss_proxy",
"concessional_dominance",
"gdp_pc", "log_gdp_pc", "gdp_growth", "fdi_net",
"gov_quality_index", "va_est", "pv_est", "ge_est",
"rq_est", "rl_est", "cc_est")]
# Build descriptive table
desc_table <- data.frame(
Variable = names(desc_vars),
N = sapply(desc_vars, function(x) sum(!is.na(x))),
Mean = sapply(desc_vars, function(x) round(mean(x, na.rm = TRUE), 3)),
SD = sapply(desc_vars, function(x) round(sd(x, na.rm = TRUE), 3)),
Min = sapply(desc_vars, function(x) round(min(x, na.rm = TRUE), 3)),
Median = sapply(desc_vars, function(x) round(median(x, na.rm = TRUE), 3)),
Max = sapply(desc_vars, function(x) round(max(x, na.rm = TRUE), 3))
)
rownames(desc_table) <- NULL
kable(desc_table, caption = "Table 1: Full Sample Descriptive Statistics (N = 6,557)") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) %>%
pack_rows("Outcome & Instrument Variables", 1, 9) %>%
pack_rows("Macroeconomic Controls (WDI)", 10, 13) %>%
pack_rows("Governance Controls (WGI)", 14, 20)
| Variable | N | Mean | SD | Min | Median | Max |
|---|---|---|---|---|---|---|
| Outcome & Instrument Variables | ||||||
| total_mob | 6557 | 138.173 | 390.172 | 0.000 | 26.580 | 9700.000 |
| ihs_mob_total | 6557 | 3.912 | 1.987 | 0.000 | 3.974 | 9.873 |
| guarantee_share | 6557 | 0.294 | 0.419 | 0.000 | 0.000 | 1.000 |
| direct_inv_share | 6557 | 0.356 | 0.434 | 0.000 | 0.026 | 1.000 |
| synd_loan_share | 6557 | 0.167 | 0.329 | 0.000 | 0.000 | 1.000 |
| credit_line_share | 6557 | 0.116 | 0.288 | 0.000 | 0.000 | 1.000 |
| concessional_mechanism_share | 6557 | 0.410 | 0.454 | 0.000 | 0.073 | 1.000 |
| first_loss_proxy | 6557 | 0.413 | 0.492 | 0.000 | 0.000 | 1.000 |
| concessional_dominance | 6557 | 0.412 | 0.492 | 0.000 | 0.000 | 1.000 |
| Macroeconomic Controls (WDI) | ||||||
| gdp_pc | 6557 | 4406.870 | 3769.609 | 254.403 | 3378.435 | 33034.958 |
| log_gdp_pc | 6557 | 8.017 | 0.908 | 5.539 | 8.125 | 10.405 |
| gdp_growth | 6557 | 2.092 | 4.772 | -34.831 | 2.569 | 62.111 |
| fdi_net | 6549 | 3.215 | 4.470 | -37.173 | 2.299 | 34.990 |
| Governance Controls (WGI) | ||||||
| gov_quality_index | 6557 | -0.447 | 0.444 | -1.971 | -0.413 | 1.038 |
| va_est | 6557 | -0.443 | 0.619 | -2.029 | -0.409 | 1.241 |
| pv_est | 6557 | -0.569 | 0.675 | -2.614 | -0.522 | 1.230 |
| ge_est | 6557 | -0.283 | 0.510 | -2.060 | -0.235 | 1.059 |
| rq_est | 6557 | -0.318 | 0.465 | -1.978 | -0.267 | 1.276 |
| rl_est | 6557 | -0.535 | 0.465 | -2.305 | -0.535 | 1.159 |
| cc_est | 6557 | -0.534 | 0.491 | -1.806 | -0.524 | 1.385 |
This is the key comparison table. Look for: adaptation has lower mobilisation and higher concessionality.
table2 <- df %>%
group_by(climate_detail) %>%
summarise(
N = n(),
`% of Sample` = round(n() / nrow(df) * 100, 1),
`Mean Mob (USD M)` = round(mean(total_mob), 1),
`Median Mob (USD M)` = round(median(total_mob), 1),
`Mean IHS Mob` = round(mean(ihs_mob_total), 2),
`Guarantee Share` = round(mean(guarantee_share), 3),
`Direct Inv Share` = round(mean(direct_inv_share), 3),
`Synd Loan Share` = round(mean(synd_loan_share), 3),
`Credit Line Share` = round(mean(credit_line_share), 3),
`Concessional Mech Share` = round(mean(concessional_mechanism_share), 3),
`First-Loss Proxy` = round(mean(first_loss_proxy), 3),
`Conc Dominance` = round(mean(concessional_dominance), 3),
`Gov Quality Index` = round(mean(gov_quality_index), 3),
`GDP per capita` = round(mean(gdp_pc), 0),
.groups = "drop"
) %>%
arrange(match(climate_detail, c("adaptation", "mitigation", "climate-other", "non-climate")))
kable(table2, caption = "Table 2: Descriptive Statistics by Climate Category") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
| climate_detail | N | % of Sample | Mean Mob (USD M) | Median Mob (USD M) | Mean IHS Mob | Guarantee Share | Direct Inv Share | Synd Loan Share | Credit Line Share | Concessional Mech Share | First-Loss Proxy | Conc Dominance | Gov Quality Index | GDP per capita |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| adaptation | 254 | 3.9 | 78.0 | 24.5 | 3.65 | 0.139 | 0.443 | 0.214 | 0.112 | 0.250 | 0.205 | 0.264 | -0.394 | 4982 |
| mitigation | 980 | 14.9 | 144.4 | 38.1 | 4.19 | 0.192 | 0.428 | 0.234 | 0.087 | 0.279 | 0.287 | 0.287 | -0.399 | 4842 |
| climate-other | 2270 | 34.6 | 247.4 | 66.1 | 4.73 | 0.258 | 0.336 | 0.178 | 0.159 | 0.417 | 0.473 | 0.416 | -0.404 | 4829 |
| non-climate | 3053 | 46.6 | 60.0 | 12.0 | 3.23 | 0.366 | 0.339 | 0.134 | 0.094 | 0.459 | 0.426 | 0.462 | -0.499 | 3906 |
Interpretation checklist:
This table directly addresses Fix #2 from the thesis alignment.
table3 <- df %>%
group_by(climate_detail) %>%
summarise(
N = n(),
`First-Loss = 1 (%)` = round(mean(first_loss_proxy) * 100, 1),
`Conc Dominance = 1 (%)` = round(mean(concessional_dominance) * 100, 1),
`Mean Guarantee Share` = round(mean(guarantee_share), 3),
`Mean Concessional Share` = round(mean(concessional_mechanism_share), 3),
`Mean Credit Line Share` = round(mean(credit_line_share), 3),
.groups = "drop"
) %>%
arrange(match(climate_detail, c("adaptation", "mitigation", "climate-other", "non-climate")))
kable(table3, caption = "Table 3: Structural Proxy Variables by Climate Category") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
| climate_detail | N | First-Loss = 1 (%) | Conc Dominance = 1 (%) | Mean Guarantee Share | Mean Concessional Share | Mean Credit Line Share |
|---|---|---|---|---|---|---|
| adaptation | 254 | 20.5 | 26.4 | 0.139 | 0.250 | 0.112 |
| mitigation | 980 | 28.7 | 28.7 | 0.192 | 0.279 | 0.087 |
| climate-other | 2270 | 47.3 | 41.6 | 0.258 | 0.417 | 0.159 |
| non-climate | 3053 | 42.6 | 46.2 | 0.366 | 0.459 | 0.094 |
Key finding to highlight: If adaptation shows a LOWER first-loss proxy despite higher overall concessionality, this is the instrument mismatch; adaptation deploys concessional capital through lower-leverage channels (credit lines, direct investment) rather than guarantees.
Visual for the instrument mismatch argument.
instrument_data <- df %>%
group_by(climate_detail) %>%
summarise(
Guarantees = mean(guarantee_share),
`Direct Investment` = mean(direct_inv_share),
`Syndicated Loans` = mean(synd_loan_share),
`Shares in CIVs` = mean(civ_share_share),
`Credit Lines` = mean(credit_line_share),
.groups = "drop"
) %>%
pivot_longer(cols = -climate_detail, names_to = "Instrument", values_to = "Share") %>%
mutate(climate_detail = factor(climate_detail,
levels = c("adaptation", "mitigation", "climate-other", "non-climate")))
ggplot(instrument_data, aes(x = climate_detail, y = Instrument, fill = Share)) +
geom_tile(color = "white", linewidth = 1.2) +
geom_text(aes(label = scales::percent(Share, accuracy = 0.1)), size = 3.5) +
scale_fill_gradient(low = "#f7fbff", high = "#2171B5", labels = scales::percent) +
labs(title = "Instrument Composition by Climate Category",
subtitle = "Mean share of total mobilisation by leveraging mechanism",
x = "", y = "", fill = "Share") +
theme_minimal(base_size = 12) +
theme(panel.grid = element_blank(),
axis.text.x = element_text(angle = 0, hjust = 0.5))
H1: Climate adaptation portfolios exhibit a higher share of concessional mechanisms than mitigation and non-climate portfolios.
DV: concessional_mechanism_share
Key IV: adaptation_tag
Expected: β₁ > 0 (positive, significant)
h1b <- lm(concessional_mechanism_share ~ adaptation_tag + mitigation_tag + year_c +
log_gdp_pc + gdp_growth + fdi_net,
data = df)
h1c <- lm(concessional_mechanism_share ~ adaptation_tag + mitigation_tag + year_c +
log_gdp_pc + gdp_growth + fdi_net + gov_quality_index,
data = df)
All standard errors are clustered at the country level to account for within-country correlation.
# Clustered standard errors at country level
cl_h1a <- vcovCL(h1a, cluster = df$country_code)
cl_h1b <- vcovCL(h1b, cluster = df$country_code)
cl_h1c <- vcovCL(h1c, cluster = df$country_code)
# Results with clustered SEs
cat("=== H1a: Baseline ===\n")
## === H1a: Baseline ===
print(coeftest(h1a, vcov = cl_h1a))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.4497492 0.0227481 19.7708 < 2.2e-16 ***
## adaptation_tag -0.1758196 0.0288763 -6.0887 1.203e-09 ***
## mitigation_tag -0.1600531 0.0278405 -5.7489 9.385e-09 ***
## year_c -0.0136026 0.0036798 -3.6966 0.0002203 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
cat("\n=== H1b: + Macro Controls ===\n")
##
## === H1b: + Macro Controls ===
print(coeftest(h1b, vcov = cl_h1b))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.1267019 0.1399874 8.0486 9.870e-16 ***
## adaptation_tag -0.1679671 0.0285480 -5.8837 4.211e-09 ***
## mitigation_tag -0.1503090 0.0277659 -5.4134 6.402e-08 ***
## year_c -0.0116674 0.0036543 -3.1928 0.001416 **
## log_gdp_pc -0.0857018 0.0170536 -5.0254 5.156e-07 ***
## gdp_growth -0.0032008 0.0021754 -1.4713 0.141247
## fdi_net 0.0043092 0.0031579 1.3646 0.172428
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
cat("\n=== H1c: + Governance ===\n")
##
## === H1c: + Governance ===
print(coeftest(h1c, vcov = cl_h1c))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.2252351 0.1978443 6.1929 6.264e-10 ***
## adaptation_tag -0.1692735 0.0286073 -5.9171 3.441e-09 ***
## mitigation_tag -0.1513733 0.0280145 -5.4034 6.770e-08 ***
## year_c -0.0113589 0.0037018 -3.0685 0.00216 **
## log_gdp_pc -0.0956913 0.0221958 -4.3112 1.647e-05 ***
## gdp_growth -0.0034229 0.0021121 -1.6206 0.10515
## fdi_net 0.0039178 0.0031579 1.2406 0.21479
## gov_quality_index 0.0374483 0.0458747 0.8163 0.41435
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
stargazer(h1a, h1b, h1c,
type = "html",
title = "Table 4: H1: Concessionality Mechanism Share Regressions",
dep.var.labels = "Concessional Mechanism Share",
column.labels = c("Baseline", "+ Macro", "+ Governance"),
covariate.labels = c("Adaptation Tag", "Mitigation Tag", "Year (centered)",
"Log GDP per capita", "GDP Growth", "FDI Net Inflows",
"Governance Quality Index"),
se = list(sqrt(diag(cl_h1a)), sqrt(diag(cl_h1b)), sqrt(diag(cl_h1c))),
omit.stat = c("f"),
notes = "Standard errors clustered at the country level.",
notes.append = TRUE,
star.cutoffs = c(0.1, 0.05, 0.01),
header = FALSE)
| Dependent variable: | |||
| Concessional Mechanism Share | |||
| Baseline |
|
|
|
| (1) | (2) | (3) | |
| Adaptation Tag | -0.176*** | -0.168*** | -0.169*** |
| (0.029) | (0.029) | (0.029) | |
| Mitigation Tag | -0.160*** | -0.150*** | -0.151*** |
| (0.028) | (0.028) | (0.028) | |
| Year (centered) | -0.014*** | -0.012*** | -0.011*** |
| (0.004) | (0.004) | (0.004) | |
| Log GDP per capita | -0.086*** | -0.096*** | |
| (0.017) | (0.022) | ||
| GDP Growth | -0.003 | -0.003 | |
| (0.002) | (0.002) | ||
| FDI Net Inflows | 0.004 | 0.004 | |
| (0.003) | (0.003) | ||
| Governance Quality Index | 0.037 | ||
| (0.046) | |||
| Constant | 0.450*** | 1.127*** | 1.225*** |
| (0.023) | (0.140) | (0.198) | |
| Observations | 6,557 | 6,549 | 6,549 |
| R2 | 0.028 | 0.062 | 0.063 |
| Adjusted R2 | 0.028 | 0.061 | 0.062 |
| Residual Std. Error | 0.447 (df = 6553) | 0.439 (df = 6542) | 0.439 (df = 6541) |
| Note: | p<0.1; p<0.05; p<0.01 | ||
| Standard errors clustered at the country level. | |||
# VIF check for the fullest model
cat("=== VIF for H1c ===\n")
## === VIF for H1c ===
print(vif(h1c))
## adaptation_tag mitigation_tag year_c log_gdp_pc
## 1.013855 1.010363 1.017446 1.449632
## gdp_growth fdi_net gov_quality_index
## 1.034195 1.037501 1.460827
cat("\nInterpretation: VIF > 10 = problematic. VIF > 5 = watch carefully.\n")
##
## Interpretation: VIF > 10 = problematic. VIF > 5 = watch carefully.
# R-squared comparison
cat("\n=== Model Fit ===\n")
##
## === Model Fit ===
cat("H1a R²:", round(summary(h1a)$r.squared, 4), "\n")
## H1a R²: 0.0284
cat("H1b R²:", round(summary(h1b)$r.squared, 4), "\n")
## H1b R²: 0.0622
cat("H1c R²:", round(summary(h1c)$r.squared, 4), "\n")
## H1c R²: 0.0631
# Extract key coefficient
coef_adapt_h1c <- coeftest(h1c, vcov = cl_h1c)["adaptation_tag", ]
coef_mitig_h1c <- coeftest(h1c, vcov = cl_h1c)["mitigation_tag", ]
cat("=== H1 KEY FINDINGS ===\n\n")
## === H1 KEY FINDINGS ===
cat("Adaptation Tag coefficient:", round(coef_adapt_h1c[1], 4),
"\n SE:", round(coef_adapt_h1c[2], 4),
"\n p-value:", round(coef_adapt_h1c[4], 4),
"\n Significant:", ifelse(coef_adapt_h1c[4] < 0.1, "YES", "NO"), "\n\n")
## Adaptation Tag coefficient: -0.1693
## SE: 0.0286
## p-value: 0
## Significant: YES
cat("Mitigation Tag coefficient:", round(coef_mitig_h1c[1], 4),
"\n SE:", round(coef_mitig_h1c[2], 4),
"\n p-value:", round(coef_mitig_h1c[4], 4),
"\n Significant:", ifelse(coef_mitig_h1c[4] < 0.1, "YES", "NO"), "\n\n")
## Mitigation Tag coefficient: -0.1514
## SE: 0.028
## p-value: 0
## Significant: YES
cat("Interpretation:\n")
## Interpretation:
if (coef_adapt_h1c[1] > 0 & coef_adapt_h1c[4] < 0.1) {
cat(" H1 SUPPORTED: Adaptation portfolios use significantly MORE\n")
cat(" concessional mechanisms than non-climate portfolios.\n")
cat(" Adaptation portfolios have a", round(coef_adapt_h1c[1] * 100, 1),
"percentage point higher\n concessional mechanism share.\n")
} else if (coef_adapt_h1c[4] >= 0.1) {
cat(" ️ H1 NOT SUPPORTED: Coefficient is not statistically significant.\n")
cat(" The difference in concessionality is not distinguishable from zero.\n")
} else {
cat(" H1 REVERSED: Adaptation portfolios use LESS concessional mechanisms.\n")
}
## H1 REVERSED: Adaptation portfolios use LESS concessional mechanisms.
if (coef_adapt_h1c[1] > coef_mitig_h1c[1]) {
cat("\n Adaptation requires MORE concessionality than mitigation\n")
cat(" (adaptation β =", round(coef_adapt_h1c[1], 4),
"> mitigation β =", round(coef_mitig_h1c[1], 4), ")\n")
}