Skip to content

Research · M.Sc. thesis filed

A bound, not an absence.

The design rules out financial inclusion moving growth by more than 0.55 points per standard deviation of the index.

19 public sources, 1,123 municipalities, an index with its weights in plain sight and 160 specifications estimated. Built with Python, dbt and DuckDB; every figure on this page comes from the results file the repository recomputes and tests.

M.Sc. in Economics thesis · Pontificia Universidad Javeriana, 2026 · supervised by Gabriel Penagos Londoño

below the 2018 baseline · 2018 baseline · above the baseline
Financial-inclusion index by department, 2018 and 2025, on one scale: the whole country rose at once.

Atlas

The index on the map, by department and by municipality

Three views of the same data. Flat, with the metrics around it; raised, to lift one department out and look at it; and municipalities, to go down to all 1,123 units. Pinning a department lets you descend to its municipalities from the map itself, and choosing a region closes the frame in on that selection while the neighbours bleed off the edge.

View

Loading the map and its series…

  • A territory without color is not a zero: it is a territory no supervised institution reported that year.
  • Block height is constant and encodes nothing. A height that depended on the value would let the units in front hide the ones behind, and a unit's reading must not depend on where it falls on the map.
  • The San Andrés archipelago is a chip drawn off scale: it lies 700 km offshore and covers 26 km², so inside the frame it shrank the map by a fifth to paint a dot.
  • Two of the panel's 1,123 municipalities have no polygon in the 2024 national map: they appear in the ranking and the figures, but not on the map.

Abstract

  • Data you can trust. Every download is fingerprinted with sha256; zeros that are really missing values are treated as missing; the splice between the supervisor's two tables is measured (median gap 0.02%).
  • A method chosen by evidence, not habit. PCA was ruled out because sampling adequacy came in at 0.317 and 0.407; a denominator that manufactured correlation (−0.23) was found and fixed (−0.13).
  • A result that says how much it knows. β = +0.0038 (p = 0.54) with entity and time effects: it rules out effects above 0.55 pp per identifying standard deviation of the index (about 2.0 pp per raw standard deviation) and says plainly it cannot see smaller ones, not even ±0.50 pp.
Read the full abstract

Does financial inclusion predict the economic growth of Colombia's departments once you take out the national trends that move all of them at once? To answer it, the project downloads nineteen public sources with a verifiable manifest, resolves every series to the municipal code, models them as a star schema on dbt and DuckDB with each download's vintage in the manifest, and builds a financial-inclusion index by dimension with frozen, published weights. On top go two annual panels, department and municipality, an interactive atlas, and an econometric battery of two-way fixed effects, cross-sectional and spatial dependence diagnostics, designs that do not rely on the index being exogenous (CCE, initial exposure, an event study with pre-trends), and inference built for 33 clusters. The answer is published with its specification, its N, its tests and its power, whatever the sign, and it is a bound rather than an absence: with a cluster wild bootstrap, the design rules out effects above about 0.55 percentage points of annual growth per identifying standard deviation of the index, and cannot speak to anything smaller.

What this shows

Four capabilities, each with its proof on this page

A dimensional warehouse with data tests

A star schema in dbt on DuckDB, with tests that fail if the splice between the two tables or the department totals break.

Panel econometrics with honest inference

Few clusters, a wild bootstrap, a permutation placebo, power and equivalence: the result says how much it knows and how much it does not.

Interactive web cartography

The atlas on this page: d3 and TopoJSON, three views and all 1,123 municipalities, with missing data painted as missing.

Reproducible engineering

uv with a lock file, continuous integration that recomputes the published results, and 25 decisions written before the code.

Data

Nineteen public sources, each download fingerprinted

Acquisition with a manifest

A purpose-built downloader pages the open-data APIs, verifies the row count against the source, types every column from the portal's metadata and writes Parquet partitioned by year. Each pull records the source date, rows, bytes and sha256 in a versioned manifest.

Geographic keys resolved

The financial supervisor's tables never declare the municipal code, yet they carry it implicitly in the row column. Resolving it gives 100% coverage across the 34 quarterly cuts and avoids name matching, which breaks on accents and aliases.

Dimensional warehouse with dbt

A star schema with vintage-aware facts on DuckDB: department, municipality and period dimensions, plus the supervisor's 98 variables each with its annualisation rule; inclusion in long form by product block.

The schema splice, measured

The current table replaces the previous one in 2021Q1, and that quarter exists in both: a natural test of the splice. The median difference per department is 0.02% and the worst case reaches 1.8%; a dbt test fails if those thresholds break.

Annual frequency, two panels

Subnational GDP is annual, so no annual value is ever repeated across four quarters. The department panel covers 2018 to 2025 with 231 growth observations and none repeated; the municipal one covers 2018 to 2024 on the statistics office's municipal value added.

A zero is not a reading

Zeros from the supervisor and the education ministry are absences, not values: outside each row's product block they disappear by construction. On the map, a territory without color is one nobody reported, and that is not the same as zero.

The index

The assumption gets measured before the method is chosen

Eight variables, three dimensions

Access, use and depth, each with its variables normalized per adult or per product and standardized over the calibration window. Eighteen candidates were left out, each with its reason written down.

PCA was ruled out by measuring it

Sampling adequacy comes to 0.317 for use and 0.407 for depth, below the 0.5 a factor model needs; access has a single variable. Forcing it produces implicit weights with the wrong sign: a variable that should add enters subtracting.

The denominator, lagged

Amounts are normalized by the previous year's output, not the same year's: that GDP also sits in the dependent variable, and dividing by it manufactures correlation. A placebo index with its numerators frozen, which moves only through its denominator, "predicts" growth with β = −0.23 when the output is the same year's; with the lagged one the bias falls to −0.13 (p = 0.03) but does not vanish, and with a fixed 2018 denominator it is gone (+0.13, p = 0.44). That is why the fixed-denominator index is published alongside, as co-principal.

Equal weights, frozen and published

Within each dimension the weights are equal, fixed over the 2018–2019 window and never touched again. Implicit weights per variable are always published, and none may be negative.

Sensitivity in plain sight

PCA and Sarma's distance index are computed the same way and published as alternatives, with the rank correlation between the three versions. None is hidden.

Zero is an average, not an absence

The index is standardized against the average department over the calibration window. A value of 2 is two standard deviations above that average, not "twice the inclusion".

Results

With entity and time effects, the effect is bounded

The design rules out effects above 0.55 percentage points of annual growth per identifying standard deviation of the index (about 2.0 pp per raw standard deviation), and cannot speak to anything smaller.

β = +0.0038 · p = 0.54

Thirty-three departments, 2019 to 2025, 228 observations. With entity and time fixed effects the composite index does not move real GDP per capita growth in any way distinguishable from zero: the wild cluster bootstrap gives p = 0.55 and the permutation placebo p = 0.51. A null without its power cannot tell "there is no effect" from "this design would not see one", so the power is measured: the minimum detectable effect at 80% is 0.60 pp per identifying standard deviation, and equivalence testing rules out effects above ±0.50 pp (p = 0.07) but fails to rule out ±0.25 pp (p = 0.29). Without time effects the same coefficient is +0.027 with p < 0.001: that distance is what the national trend was worth, and two-way effects take away 92% of the index's variance.

SpecificationβSEpN
Entity and time effects (baseline)+0.00380.00620.54228
Baseline with Driscoll-Kraay errors+0.00380.00340.27228
Entity effects only+0.02720.0050< 0.001228
In changes of the index−0.00450.00430.30226
CCE, heterogeneous loadings by department+0.00170.00870.84228
SLX, spatial lag of the index+0.00740.00710.30221
Initial exposure: 2018 index × national adoption+0.01660.00590.005223
minimum detectable effect at 80%
0.60 pp
per identifying standard deviation of the index; with the bootstrap, equivalence rules out ±1.0 pp and not ±0.50 pp (p = 0.07)
wild cluster bootstrap p
0.55
999 Rademacher draws with the null imposed over 33 departments, studentised with the cluster-robust error
permutation placebo p
0.51
499 reassignments of each department's whole trajectory; shuffling within the year would narrow the cloud artificially
Pesaran CD on the residuals
2.40
weak but present cross-sectional dependence (p = 0.016); hence Driscoll-Kraay alongside the cluster
  • 1The three dimensions on their own, the specification in changes, CCE, SLX and the two alternative indices agree: none is distinguishable from zero. Leaving out one department at a time, the coefficient stays between +0.000 and +0.008 and is never significant.
  • 2The specification curve estimates 160 variants: without time effects, 50 of 80 come out significant; with time effects, 5 of 80. What separates the two halves is the national trend, not inclusion.
  • 3The only design with a signal is the initial-exposure design (+0.017, p = 0.005), which survives its own bootstrap and its own placebo. It does not survive the contrast that matters: initial urbanisation produces an equally significant slope on its own (+0.050, p = 0.008), and with both exposures in the same equation the index falls to p = 0.10. Nor does it survive a Holm correction over the twelve contrasts published. It is a differential slope for the more urban departments, not an effect of the index, and it is published as such.
  • 4The event study around 2020 cannot test pre-trends — the dependent variable starts in 2019 and the shock is 2020 — and it is published with that limitation written down.
  • 5The 2026–2028 forecast layer is published as a scenario, not as a superior forecast: in the backtest it cuts the naive forecast's mean error by 38.6%, but it wins in 3 of 8 years, without 2021 the gain is +8.3%, and grouped by origin year the difference is not significant (p = 0.29).

Decisions

Every decision of substance is written before the code

The decisions that change a result — frequency, data model, index method, econometric design — go into a decision record with their alternatives, their cost and how to reverse them. Constants live in one place and no published figure exists without the test that holds it up.

Annual frequency, two panels

Subnational output is annual and no annual value is spread across four quarters. Temporal disaggregation stays an annex, with its caveats.

Star schema with provenance

Natural keys, each download's sha256 fingerprint in the manifest, and a schema splice measured in the quarter that exists in both sources.

Index by dimension with its assumption measured

Sampling adequacy before factoring; equal weights, frozen and published; alternatives in plain sight.

Econometric design

Two-way fixed effects, diagnostics measured rather than assumed, designs against endogeneity with their pre-trends, a wild bootstrap studentised with the cluster-robust error because 33 clusters are not enough for the asymptotics, a Holm correction, and the power published beside the null.

Derived data under the source's license

What comes out of the supervisor, the ICT ministry and the education ministry is published CC BY-SA 4.0, with attribution.

Reproduce

Five commands reproduce the project

  1. uv sync

    Exact dependencies from the lock file. Nothing is installed outside it.

  2. uv run iif acquire all

    Downloads the nineteen sources, verifies the count against each API and writes the manifest with sha256.

  3. make check

    Ruff, 242 Python tests, the dbt models and tests on DuckDB, and the site render. It exits zero or there is no commit.

  4. uv run iif econ

    Runs the full econometric battery in under half a minute and writes the file every figure on this page comes from.

  5. uv run iif atlas

    Generates the geometry and series this map consumes, inside the 3 MB budget the contract sets.

The local and CI engine is DuckDB. BigQuery is kept as an alternative target with a per-query cost cap, and Snowflake as a later demonstration; nothing in the project depends on either staying alive.

What is there and what is coming

The project ships in phases

  1. 1Published: the nineteen sources with a manifest, the full dbt warehouse, the index by dimension with its weights, the two annual panels, the atlas, the econometric battery with its power and its specification curve, and the 2026–2028 forecast as a scenario, also on this page's map.
  2. 2Next: the temporal-disaggregation annex (Chow-Lin, Denton and Fernández on the statistics office's quarterly indicator) and the manuscript.
  3. 3All the code, the derived data and the decisions are in the repository, versioned.

Depending on who is reading

Stop choosing between the person who understands the business and the person who builds the data.

If your team needs someone who reads the P&L and builds the pipeline that feeds it, let's talk this week. I reply within one business day.

A 20-minute video call: you pick the slot in your time zone and the link arrives by email.