Pooled PCA for Development Indicators Across Time

When you track development over time, the ruler must stay fixed

72.4%variance captured by pooled PC1
+0.144net development shift, hidden by per-period PCA
16 / 153regions where the two methods disagree

Carlos Mendez

Nagoya University (GSID)

June 11, 2026

The Tension

Act I

If the ruler changes between measurements, you cannot tell whether the object grew or the ruler shrank

We want one composite Human Development Index for 153 South American regions, comparable across 2013 and 2019.

But running PCA separately each period lets the weights and the standardization drift. Did development change, or did the yardstick?

Education and health rose, but income fell — a mixed signal a naive method erases

2013 (steel) vs 2019 (orange). The teal arrow connects the period centroids: the cloud shifts right (education up) but down (income down).

Where we’re going

  • The data: a 153-region × 2-period panel of three HDI sub-indices
  • The problem: per-period PCA produces non-comparable scores
  • The fix: pooled standardization, weights, and bounds from stacked data
  • The payoff: a fixed yardstick that validates against the official SHDI

The Investigation

Act II

The lab: 153 regions × 2 periods = a 306-row panel of three sub-indices

  • Indicators — Education, Health, Income sub-indices (Global Data Lab SHDI)
  • Coverage — 153 sub-national regions, 12 South American countries, 2013 & 2019
  • Shape — reshaped wide → long into 306 rows (153 regions × 2 periods)

All three indicators run higher = better — no polarity flip needed.

Real development data is messier than simulation: PC1 will capture less

Pair Correlation
Education – Income 0.68
Health – Income 0.63
Education – Health 0.44

Far below the \(r > 0.93\) of simulated data — the three indicators carry more independent information.

Per-period PCA re-fits the weights every period — the index formula itself drifts

PC1 eigenvector weights shift between 2013 and 2019: education’s weight drops, health’s jumps.

Re-centring each period to zero erases the net signal — 43 regions appear to decline

Per-period PCA: the 10 largest rank shifts. Teal = improved, orange = declined.

Pooled standardization measures every region against one fixed ruler

\[Z_{ij,t}^{\text{pool}}=\frac{X_{ij,t}-\bar{X}_j^{\text{pool}}}{\sigma_j^{\text{pool}}}\]

The pooled mean \(\bar{X}_j^{\text{pool}}\) and SD \(\sigma_j^{\text{pool}}\) are computed from all 306 rows at once, then applied to every observation in every period.

Per-period shifts the baseline each year; pooled fixes it — so 0.926 → 0.946 is a genuine z-score rise.

One eigen-decomposition on the stacked data yields a single, fixed set of weights

\[\Sigma^{\text{pool}}\,\mathbf{v}_k=\lambda_k\,\mathbf{v}_k,\qquad PC1_{i,t}=\sum_{j} w_j\,Z_{ij,t}^{\text{pool}}\]

Pooled eigenvalues \([2.173,\,0.563,\,0.264]\); PC1 weights \([0.564,\,0.545,\,0.620]\)the same recipe for 2013 and 2019.

Because the weights come from the pooled data, the index formula no longer drifts between periods.

scikit-learn confirms it: fit_transform on stacked data is pooled PCA

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
scaler = StandardScaler()              # POOLED: fit on ALL 306 rows
Z = scaler.fit_transform(df[cols])
pca = PCA(n_components=1)              # one set of weights for both periods
df["pc1"] = pca.fit_transform(Z)[:, 0]

PC1 captures 72.4% of variance — strong, but real data is not one-dimensional

Variance explained: PC1 72.4%, PC2 18.8%, PC3 8.8%.

The fixed yardstick reveals a net development shift of +0.144 that per-period PCA zeroes out

+0.144

pooled PC1 mean: \(-0.072\) in 2013 \(\to\) \(+0.072\) in 2019 · net progress, invisible under per-period PCA

With a fixed scale, the 2019 HDI bars consistently extend past 2013

Pooled HDI, top and bottom 15 regions. Orange (2019) vs steel (2013); the dashed line splits the groups.

The Resolution

Act III

The two methods disagree on the direction of change for 16 of 153 regions

Pooled vs per-period HDI change. The dashed 45° line is perfect agreement; most points sit below it.

High rank agreement, but consequential re-orderings: Spearman ρ = 0.982

Improvement-rank comparison. Crossing lines mark where the two methods re-order who improved most.

Validated against the official SHDI, pooled PCA wins on both levels and dynamics

Comparison Pooled \(R^2\) Per-period \(R^2\)
Levels (vs SHDI) 0.9823 0.9750
Changes (vs SHDI) 0.9964 0.9913

The official SHDI uses a fixed geometric-mean formula across years — so it tracks the fixed-yardstick pooled index more tightly.

Pooled PCA reproduces the official benchmark across periods with almost no scatter

Validation against the official SHDI: pooled (left) clusters both periods tightly on the fit line; per-period (right) scatters wider.

Tracking dynamics is the real test — pooled change fits the SHDI change almost exactly

SHDI change validation: pooled (left, R² = 0.996) falls almost on the line; per-period (right, R² = 0.991) disperses more.

A fixed classification makes the map honest: 40 regions climbed, 25 fell

Pooled-PCA HDI choropleths, 2013 vs 2019, with Fisher-Jenks breaks fixed from 2013. Any colour change is a real threshold crossing.

Education converged, but income and health diverged — overall inequality rose

Gini index by indicator, 2013 vs 2019. Education’s Gini falls; health and income rise.

Does PCA make the index “true”? No — it makes it comparable

Objection. PC1 discards 28% of the variance and the weights are sample-specific — adding or removing regions changes every score.

Response. Correct, and worth stating plainly. Pooled PCA does not claim a true index — it guarantees a fixed yardstick across time. PC2 (19%) may hold real structure; the index is relative to this sample. What pooling buys is comparability, validated at \(R^2 = 0.98\) against the official SHDI.

Fit the standardization and the weights on the stacked data, and the yardstick stops moving.