TLDR: Prima postare a stabilit că măsurile privind statul de drept WGI și WJP sunt de acord cu privire la modul în care clasifică țările. Această urmărire testează întrebarea mai strictă dacă WGI poate prezice WJP-urile nivelurimai degrabă decât doar clasarea relativă/poziția relativă a țărilor. Poate: erorile de prognoză par a fi minime și nu variază semnificativ în timp.
Acesta este al doilea post dintr-o serie care examinează relațiile dintre statul de drept și rezultatele economice și sociale.
În postarea anterioară a seriei, am testat dacă indicele de stat de drept (RoL) al World Governance Indicator (WGI) a fost un substitut demn pentru indicele produs de World Justice Project atunci când trebuia să profite de acoperirea mai largă și mai lungă a WGI.
Când i-am cerut feedback-ului lui Claude, mi-a oferit-o cu nepoliticos, argumentând că analiza mea a fost în regulă pentru confirmare acord general în ceea ce privește modul în care cele două măsuri au clasificat țările, dar a fost necesară o analiză suplimentară pentru a testa dacă WGI ar putea fi utilizat în mod fiabil pentru a prezice nivelurile RoL.
Primul meu răspuns a fost să-i spun asta lui Claude nimeni ar trebui să folosească indici compoziți în acest fel în primul rând, deoarece implică un nivel de precizie pe care nu îl au, dar am decis că ar fi un addendum util la primul post pe care l-aș putea posta în vacanța mea în Teritoriul de Nord.
Configurarea proiectului și date
Încă o dată, datele folosite în această postare pot fi descărcate aici pentru WGI și aici pentru indexul RoL al WJP. Acestea erau actuale din iulie 2025.
#load the packages we'll probably need
library(tidyverse)
library(readxl)
library(janitor)
library(countrycode)
#import WGI data
dta_wgi_2025<-read_excel("./Data/wgidataset_with_sourcedata-2025.xlsx",
sheet="rl") |>
clean_names()
#import World Justice Project RoL data
dta_wjp_rol<-read_excel('./Data/2025_wjp_rule_of_law_index_HISTORICAL_DATA_FILE.xlsx', sheet="Historical Data")|>
clean_names()
Curățarea datelor
Curățarea datelor este identică cu ultima postare și se bazează pe asigurarea faptului că numele țărilor și regiunilor sunt aplicate în mod consecvent pe cei doi indici.
#standardize column names
dta_wgi_2025<-dta_wgi_2025 |>
rename(country=economy_name,
iso3c=economy_code,
wgi_rol=governance_estimate_approx_2_5_to_2_5)
dta_wjp_rol<-dta_wjp_rol |>
rename(iso3c=country_code) |>
rename_with(~ str_replace(., "^x", "factor_"), starts_with("x"))
#change wjp's year variable to YYYY format and convert to numeric
#(adopts the first 4 digit year when in YYYY-YYYY format)
dta_wjp_rol <- dta_wjp_rol |>
mutate(year = as.numeric(str_sub(year, 1, 4)))
#cold-heartedly drop columns I'm not interested in
dta_wgi_2025<-dta_wgi_2025 |>
select(iso3c, income_classification, year,wgi_rol)
#drop country and region name labels so these can be standardized
dta_wjp_rol<-dta_wjp_rol |>
select(-country_year,-country,-region) |>
rename(wjp_rol=wjp_rule_of_law_index_overall_score)
#merge dataframes
dta_rol_unified<-left_join(dta_wgi_2025,
dta_wjp_rol,
by = join_by(year, iso3c),
keep=FALSE)
#add standardized and region names country names
#define country code assignments for legacy / ambigious codes
#(Note: matches devised by Claude)
ref_iso3c_custom_names <- c(ADO = "Andorra",
ANT = "Netherlands Antilles",
PRI = "Puerto Rico",
REU = "Réunion",
XKX = "Kosovo")
ref_iso3c_custom_regions <- c(ADO = "Europe & Central Asia",
ANT = "Latin America & Caribbean",
PRI = "Latin America & Caribbean",
REU = "Sub-Saharan Africa",
XKX = "Europe & Central Asia")
#assign country names and regions:
dta_rol_unified<-dta_rol_unified |>
mutate(country_name=countrycode(iso3c,
origin='iso3c',
destination = 'country.name.en',
custom_match = ref_iso3c_custom_names),
region=countrycode(iso3c,
origin='iso3c',
destination = 'region',
custom_match =ref_iso3c_custom_regions))
Testarea înlocuirii cardinale
Codul de mai jos întreabă practic dacă este posibil să se estimeze nivel a indicelui RoL al WJP folosind măsura RoL a WGI folosind un model liniar simplu. La început, potrivirea modelului este destul de mare, la 97 la sută, dar am fi putut ghici acest lucru din dispersarea pe care am produs-o în ultimul nostru post.
# Cardinal substitution: does a WGI->WJP mapping reproduce WJP levels? -----
sum_rol_country <- dta_rol_unified |>
filter(!is.na(wgi_rol), !is.na(wjp_rol)) |>
summarise(wgi_rol_mean = mean(wgi_rol),
wjp_rol_mean = mean(wjp_rol),
.by = country_name)
mod_rol_calib <- lm(wjp_rol_mean ~ wgi_rol_mean, data = sum_rol_country)
#output the R squared
summary(mod_rol_calib)$r.squared |> round(2)
A doua bucată calculează eroarea pătrată medie a rădăcinii de validare încrucișată Leave-One-Out (LOO RMSE), care estimează eroarea de predicție pentru fiecare țară atunci când este ținută în afara eșantionului: un LOO RMSE mai mic implică erori de prognoză mai mici atunci când se prezică în afara eșantionului. Aceasta este apoi scalată de abaterea standard a mijloacelor WJP la nivel de țară pentru a face mai ușor de interpretat.
Rezultatele sunt încurajatoare: LOO RMSE indică faptul că utilizarea WGI pentru a estima nivelurile WJP implică o eroare tipică de mai puțin de trei procente, ceea ce reprezintă aproximativ 19% din spread-ul între țări în scorurile WJP. Sugerând WGI poate oferiți un proxy rezonabil atunci când nivelurile sunt și ele importante.
# Leave-One-Out Cross-Validation Root Mean Squared Error (LOO RMSE)
rlt_rol_loo_rmse <- sqrt(mean((residuals(mod_rol_calib) /
(1 - hatvalues(mod_rol_calib)))^2))
#divide by standard deviation of WJP index means to put the figure in the context fo the data
rlt_rol_rmse_ratio <- rlt_rol_loo_rmse / sd(sum_rol_country$wjp_rol_mean)
Prejudecată sistematică
Desigur, deoarece măsurile LOO RMSE sunt calculate pentru întregul set de date, ar putea ascunde părtinirea sistematică, cum ar fi dacă acuratețea prognozei variază în funcție de scorul WGI.
Acest lucru este testat în graficul de mai jos, comparând eroarea de predicție (reziduală) cu valoarea prezisă pentru indicele WJP (folosind măsura WGI RoL). Dacă nu există o părtinire sistematică, reziduurile ar trebui să rămână relativ stabile între valorile WJP prezise.
Din păcate, acest lucru nu pare să fie cazul, modelul neprevăzând indicele RoL al WJP la ambele capete. Deși această părtinire este relativ ușoară la 0,05 (în unități de indice WJP), sugerează că WGI este probabil un substitut mai slab pentru indicele WJP pentru țările de la fiecare capăt al spectrului RoL.

dta_plt_rol_calib <- sum_rol_country |>
mutate(wjp_pred = fitted(mod_rol_calib),
resid = wjp_rol_mean - wjp_pred)
plt_rol_calib <- ggplot(dta_plt_rol_calib, aes(wjp_pred, resid)) +
geom_hline(yintercept = 0, linewidth = 0.3, col = "grey70") +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE, col = "grey40", linewidth = 0.5) +
labs(x = "Predicted WJP (from WGI)", y = "Residual (WJP units)") +
theme_classic()
plt_rol_calib
Cu toate acestea, a fi economist aplicat înseamnă că știu un truc simplu: adăugați termeni neliniari la toate și vedeți dacă zâmbetul dispare.
Și da, zâmbetul este acum o grimasă: așa cum se arată în diagrama de mai jos, adăugarea unui termen neliniar pare să aibă grijă de părtinire la fiecare capăt. Observați că erorile medii nu mai cresc la niciun capăt, făcând predicția relativ constantă (și mică) pe tot spectrul RoL. Sugerând WGI poate fi de încredere pentru a prezice indicele WJP la fiecare capăt al spectrului, cu condiția să luăm în considerare neliniaritatea.


#reestimate the model with a non-linear WGI term
mod_rol_calib_non_linear <- lm(wjp_rol_mean ~ wgi_rol_mean + I(wgi_rol_mean^2),
data = sum_rol_country)
dta_plt_rol_calib_nl <- sum_rol_country |>
mutate(wjp_pred = fitted(mod_rol_calib_non_linear),
resid = wjp_rol_mean - wjp_pred)
plt_rol_calib_nl <- ggplot(dta_plt_rol_calib_nl, aes(wjp_pred, resid)) +
geom_hline(yintercept = 0, linewidth = 0.3, col = "grey70") +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE, col = "grey40", linewidth = 0.5) +
labs(x = "Predicted WJP (from WGI, quadratic)", y = "Residual (WJP units)") +
theme_classic()
plt_rol_calib_nl
Dar derivă?
Ultima întrebare pe care Claude a sugerat-o să o pun a fost dacă capacitatea WGI de a prezice indicele WJP se schimbă în timp. Codul de mai jos testează acest lucru comparând un model de predicție liniară simplu cu unul în care influența WGI se modifică în timp.
Funcția anova() compară apoi rezultatele ambelor modele pentru a testa dacă permiterea influenței WGI să se schimbe în timp îmbunătățește acuratețea prognozei modelului. Dacă o face (indicată printr-o valoare p semnificativă), capacitatea WGI de a prezice indicele WJP ar putea să nu fie stabilă în timp.
Rezultatul: valoarea ap de 9%, ceea ce indică faptul că modelul invariant în timp este suficient de bun pentru a prezice indicele WJP.
#estimate a simple linear model with WGI's influence kept constant over time mod_rol_calib_flat <- lm(wjp_rol ~ wgi_rol, data = dta_rol_unified) #estimate a model where WGI's influence varies over time mod_rol_calib_yr <- lm(wjp_rol ~ wgi_rol * factor(year), data = dta_rol_unified) #compare the prediction capacity of both models anova(mod_rol_calib_flat, mod_rol_calib_yr)
Uite ce m-a facut Claude sa fac…
Iată-l: WGI poate servi ca un substitut util pentru indexul WJP privind statul de drept, indiferent dacă sunteți interesat de clasamentele RoL sau niveluri, cu condiția să luați în considerare neliniaritatea la fiecare capăt al scalei RoL.
Cum a fost folosit AI pentru această postare: Deoarece această analiză suplimentară a fost sugestia lui Claude şi Eram la câteva ore departe de o vacanță, m-am sprijinit foarte mult pe Claude pentru prima schiță a codului. O mare parte din text este al meu, dar am cerut AI sugestii pentru a mă asigura că am explicat analiza corect.
Postul La nivel cu mine: poate WGI să prezică scorurile WJP pentru statul de drept? a apărut prima dată pe Giles.
