Skip to content

Futuros Index — how it is computed

The Futuros Index is the 0–100 figure that headlines each country in the atlas at futuros.xyz. It is not an opinion or an editorial estimate: it is a documented function of cited indicators, recomputed from the raw data on every build of the platform. This page explains the full method, step by step, with the exact formulas and the audit chain that lets any score be decomposed down to its original source record.

One-line summary

The index is the equal-weights average of 10 axis scores; each axis is the average of its cited indicators, normalized to 0–100 against goalposts set at the 5th and 95th percentiles of the pooled regional distribution (HDI/SDG-style method).

Scope

DimensionValue
Geographies26 (25 countries + LATAM aggregate)
Axes10
Time window2010–2024, annual series
Indicators with signal (unique ids)798
Indicators excluded (neutral or constant)133
Contributing indicator×eje×country instances16,970
Scored axis×country cells260 of 260 (0 empty, 0 quarantined)

Figures from the latest bake (generated_at = 2026-08-25); they regenerate with every data update — the exact figures live in the stats block of scores.json, which is the authority over this table.

The 10 axes, with identical weight (0.10 each): health · education · work and economy · security · justice and rule of law · environment and climate · infrastructure · innovation and technological sovereignty · social cohesion and inclusion · democratic governance.

The pipeline, from the cache to the atlas

  1. Input. Each pillar × country cell of the parameter cache contains indicator instances with a time series, unit, desirable direction (good_direction) and citation_id. Quarantined cells (flagged by the integrity checks) are excluded entirely — see the exact semantics below.
  2. Bake. The script scripts/bake-composite-scores.ts applies the method described below and emits public/data/scores.json.
  3. Verification. A prebuild gate (scripts/check-scores.ts) blocks deployment with four checks: the file exists and parses; it covers every non-quarantined geography present in the cache; each geography has ≥1 scored pillar; and the composite, each pillar's latest and every point of every series are finite numbers in [0, 100]. It is a shape-and-coverage gate, not a freshness gate.
  4. Presentation. The interface reads scores.json; the pillar weights live in a single place in the client (PARAM_WEIGHTS in src/lib/composite-score.ts) and are renormalized to sum 1, so reweighting the index is a one-line, transparent, versioned change.

Quarantine semantics

Quarantine is not a score discount: it is total exclusion, with precise rules at every link.

RuleExact effect
OriginThe flag comes from the validation pipeline: each cell's envelope carries validator_status, red_team_status, quarantine and quarantine_reason (types in src/shared/index.ts).
BakeThe cell is discarded before pass 1: it receives no score and its observations do not participate in the goalpost setting of any indicator either.
Gatecheck-scores.ts builds the set of required geographies from non-quarantined cells only; a quarantined cell does not break the build as "missing".
InterfaceThe parameter page shows a quarantine banner both for status: "quarantined" envelopes and for ready envelopes with quarantine: true.
Accountingscores.json publishes stats.cells_quarantined; today it is 0, but the mechanism is always active.
MonitoringQuarantine excludes from the index, not from monitoring: the data-health layer scores every ready cell, quarantined or not, so the cell keeps its confidence score and its anomalies visible.

The method, step by step

Step 1 — Goalposts per indicator

For each indicator (for example, life expectancy at birth), all of its observations are pooled across all countries and all years of the window. The goalposts are set at the 5th and 95th percentiles of that pooled distribution:

  • P5 = floor (score 0) · P95 = ceiling (score 100)
  • With fewer than 8 observations, min–max is used as a fallback.
  • If the goalpost span is still degenerate (P95 − P5 < 10⁻⁹) it falls back to min–max; if that span is also zero, the indicator is excluded — an indicator that does not separate values does not discriminate.
  • An id's good_direction is the first declared one found in the cache; it is applied consistently to all instances of the same indicator across all countries.

Using percentiles instead of the minimum and maximum prevents a single country's outlier from defining the scale for the whole region. And pooling all years into a single distribution makes the goalposts stable over time: if a country improves, its score rises; the bar does not move with it.

Step 2 — 0–100 normalization, direction-aware

Each observation is transformed into a score:

n(i, c, t) = clamp[0,100]( (x(i, c, t) − P5ᵢ) / (P95ᵢ − P5ᵢ) × 100 )

if good_direction(i) = "down":   n ← 100 − n

where x(i, c, t) is the value of indicator i in country c in year t. The directional inversion guarantees that a higher score always means better: for homicides per 100,000 or emissions per capita, a low value produces a high score.

Exclusions (by design, not by convenience):

  • Indicators with good_direction: neutral (total population, surface area…) are descriptive, not normative → they do not score.
  • Constant or single-value indicators do not discriminate between countries → they do not score.
  • In the latest bake: 133 ids excluded by these rules, 798 with signal.

Step 3 — Filling the 2010–2024 series

Many sources publish with a lag or in alternating years. For each indicator instance, the annual series is completed by carrying the nearest observation: for each year without data, the most recent observation prior to or equal to that year is used (forward-fill); only the years before the first observation take the value of that first observation (back-fill). Each annual cell is marked as observed or filled.

The published coverage per pillar is exactly:

coverage(p, c) = observed indicator×year cells / (n_indicators × 15 years),  3 decimals

For example, health in Costa Rica: 141 indicators, coverage 0.749 — 74.9% of the 2,115 annual cells (141 × 15 years) are real observations, the rest is carry-forward. The fill never invents new levels — it only holds the last known value.

Step 4 — Axis score

S(p, c, t) = average of n(i, c, t) over the indicators i that contribute to axis p

A simple average, with no internal weights: within an axis, every indicator with signal counts the same.

Step 5 — The Futuros Index

Index(c, t) = Σₚ wₚ · S(p, c, t),   with wₚ = 0.10 for the 10 axes

The weights are deliberately equal: the index is a transparent average of ten lenses, not a model with opaque weightings. Any future reweighting will be an explicit, auditable change in PARAM_WEIGHTS (the weights are renormalized to sum 1 automatically).

The 5-year change (Δ5a) that accompanies the index is simply Index(c, 2024) − Index(c, 2019) over the same series.

Audit chain: from the figure to the source

Every score is decomposable end to end:

Index (71, Costa Rica)
  └─ health eje (75) — 141 indicators, coverage 0.749
       └─ contributing[]: indicator id + citation_id
            └─ citations.json: source, series, year, deep URL
                 └─ original record (World Bank, ECLAC, PAHO, national source…)

The exact shape of scores.json:

LevelFields
Rootgenerated_at · method (one-line description of the method) · year_min/year_max · pillar_weight (0.1) · stats · geographies{}
statsgeos · params · cells_scored · cells_quarantined · indicator_ids_scored · indicator_ids_excluded_neutral_or_constant · empty_pillar_cells
Per geographycomposite (latest year) · composite_series (2010–2024) · delta_5y · pillars{}
Per pillarseries · latest · n_indicators · coverage (3 decimals) · contributing[] with {id, label, citation_id, good_direction} for each indicator

There is no number in the index that cannot be traced to a cited source record.

How to read the number

  • It is relative to the region, not absolute. A 100 means "at the 95th percentile of the pooled Latin America and Caribbean 2010–2024 distribution", not "problem solved". It is not comparable with the HDI or other indices in levels.
  • The ordering matters more than the decimal. Scores are rounded to integers at bake time (the pillar score is round(average) and the composite is round(average of rounded pillars)); differences of 1–2 points between countries are within the coverage noise.
  • The index's behavior is verifiable at the extremes. In the latest bake: Uruguay 78, Chile 77, Costa Rica 71, Argentina 70 and Brazil 65 at the top; Nicaragua 43, Honduras 42 and Haiti 24 at the bottom; the LATAM aggregate at 58. Fragile states score low, the most solid democracies lead in governance and justice — the method's direction holds with no manual adjustments.

Declared limitations

Read this before citing the index

The four limitations below are part of the methodology, not fine print.

  1. Temporal fill. Years without an observation carry the last known value; the published coverage per pillar quantifies how much of the series is observed vs. held.
  2. Simple average within the pillar. A pillar with many indicators on the same theme tilts the score toward that theme; the contributing[] list makes that composition visible.
  3. Region-pooled goalposts. The index measures position within Latin America and the Caribbean; it does not capture the distance to extra-regional benchmarks.
  4. 2010–2024 window. Structural changes prior to 2010 do not affect the goalposts.

Reproducibility

The full index regenerates from the cited data with one command:

bash
bun run scripts/bake-composite-scores.ts          # regenerates public/data/scores.json
bun run scripts/check-scores.ts                   # gate: coverage + every score ∈ [0,100]

The method is implemented in a single readable file (scripts/bake-composite-scores.ts, ~360 lines) and this document corresponds line by line to that code. If the code and this document diverge, the code rules and the document is corrected — never the other way around.

The promise that "reweighting is a one-line change" also has a practical face on the platform: /indice is the build-your-own-index constructor (choose components, weights and min-max or z normalization; the 25 countries re-rank live), and /indices is the transparency hub that lists every published composite (ICF, ISC, IAC, ICR) with its weight tree read from the actual baked files.

Methodological references

  • UNDP, Human Development Report — Technical Notes (the HDI's goalpost normalization).
  • OECD/JRC, Handbook on Constructing Composite Indicators: Methodology and User Guide, 2008.

Every figure with its source — traceability is the contract.