Climate impact results come at impact region level, but most analyses need them at country, state, or district level. This package does that aggregation step. Published weight files describe how impact regions overlap administrative units; the library downloads them, applies them to your data, and computes statistics. It can also generate new weight files between any two sets of geometries when the published ones do not cover your case.
import cil_regionalization as cilreg
weights = cilreg.fetch_weights("gadm20-adm1-per-source")
result = cilreg.apply_weights(
weights,
my_draws,
kind="extensive",
weight="pop",
data_version="world-combo-201710",
# drop this line if the draws cover every impact region
restrict_to_sources={(h,) for h in my_draws["hierid"].unique()},
)
stats = cilreg.summarize_samples(
result.frame,
sample_dims=["batch", "gcm"],
time_col="year",
window=(2080, 2099),
quantiles=[0.05, 0.5, 0.95],
)my_draws is a long table: one row per impact region and year (plus
batch, model, and whatever other columns your sample has), with the
region id in a hierid column and the numbers in a value column
(another name works via value_col=). restrict_to_sources limits
the aggregation to the regions the draws cover; leave it out only when
they cover all 24,378. data_version names the impact region set the
draws are keyed to; draws built on the published shapefile record
below use world-combo-201710, and a mismatch with the weight file
raises before any numbers are produced. fetch_weights downloads the
file from its Zenodo record, verifies its checksum, and caches it
under ~/.cache/cil_regionalization.
The same works from the command line: cilreg fetch --list shows
every published weight file name, cilreg fetch <name>
downloads and prints the cached path, cilreg cache list and
cilreg cache clear manage the cache, and cilreg pipeline <config.toml> runs a whole Monte Carlo tree from one config file.
The top lane happens once per pair of geometries: intersect the administrative boundaries, the impact regions, and a population raster, and record what share of each intersection carries. The result is a weights parquet plus a manifest that says exactly what produced it, published on Zenodo. The bottom lane is what you do day to day: fetch the weight file, apply it to your Monte Carlo draws, compute statistics at the end.
Both aggregation operations are the same multiply and add; what differs is which way the weights are normalized. For a total (dollars, deaths), each region's value is split between the units it touches, and the pieces must add back up:
value(u) = sum over r of w(r, u) * value(r),
where the w(r, u) for one region r sum to 1
For an average (a death rate, a temperature), each unit's value is the population weighted mean over the regions inside it:
value(u) = sum over r of w(r, u) * value(r),
where the w(r, u) for one unit u sum to 1
Quantiles do not pass through sums: the 95th percentile of a sum is not the sum of 95th percentiles, because that would need every region to hit its worst draw together. The pipeline aggregates each Monte Carlo draw separately, takes quantiles at the end, and rejects input that already contains quantiles.
Ask one question: does adding your variable across regions produce a meaningful total?
| Variable | Weight file | kind | Why |
|---|---|---|---|
| Dollars, deaths, counts | ...-per-source |
extensive |
A total is split between the units it touches |
| Rates, temperatures, shares | ...-per-destination |
intensive |
An average is taken over the regions in each unit |
| A ratio needed at the target level, like percent of GDP | ...-per-source |
ratio |
Sum the numerator and denominator separately, divide after |
A file built one way cannot do the other's job. The library checks
the kind you declare against the direction the file records and
raises on a mismatch.
In the mortality, labor, and energy trees, the effect of climate
change is a file's rebased variable minus the same variable of its
-histclim sibling, whatever the file stem; rebased on its own is
not it. No adaptation is the exception and stands alone. The Climate
Impact Lab memo "The art of rebasing and histclim" has the reasoning.
Energy adds two traps of its own. The physical values are GJ per
capita, although the files say kWh/pc. The dollar -price...-levels
files understate by exactly 0.0036, because a GJ quantity was
multiplied by a per-kWh price; divide by 0.0036 to repair. Energy
also has no Monte Carlo draws: its uncertainty lives in delta-method
trees, where rebased holds the variance and rebased_bcde the
gradient per regression coefficient. Aggregate the gradients with the
weights and take one quadratic form against the embedded covariance
matrix (aggregate_gradient and delta_method); summing
independently drawn region values understates the aggregate spread.
Climate Impact Lab projections weight climate models by the SMME
weights. summarize_samples and pooled_statistics take
weight_col, a column with one weight per sample member. The default
is unweighted; use the weights when the numbers will sit next to
results from these projections.
The weight files are published as their own Zenodo record
(doi:10.5281/zenodo.22003542), one per RCP (rcp45_SMME_weights.tsv,
rcp85_SMME_weights.tsv; columns quantile, model, weight). To
apply them, normalize the model names and merge the weight onto every
draw of each model, so the weight repeats across batches:
w = pd.read_csv(
"https://zenodo.org/records/22003542/files/rcp85_SMME_weights.tsv?download=1",
sep="\t",
)[["model", "weight"]]
w["key"] = w["model"].str.replace("*", "", regex=False).str.lower()
draws["key"] = (
draws["gcm"].str.replace("surrogate_", "", regex=False).str.lower()
)
draws = draws.merge(w[["key", "weight"]], on="key").drop(columns="key")
stats = cilreg.summarize_samples(
draws,
sample_dims=["batch", "gcm"],
time_col="year",
window=(2080, 2099),
quantiles=[0.05, 0.5, 0.95],
weight_col="weight",
)The naming rule: weight file names are lowercase, a trailing * is
bookkeeping and drops out, and an underscore suffix (gfdl-cm3_94)
marks a surrogate, which the projection trees spell with a prefix
(surrogate_GFDL-CM3_94). Strip the prefix and lowercase, and every
model in the trees matches exactly one weight row.
Weighted quantiles follow the historical extraction tool: the left
step inverse of the weighted empirical distribution, not the
interpolated quantiles of the unweighted path. Pooling a sample
dimension named gcm without weights warns once; filter
UnweightedModelWeightsWarning to silence it. On the Mexico sample
(examples/rates) the weights barely move the median and pull the
95th percentile down by about a fifth.
examples/aggregation/ aggregates one batch of Monte Carlo mortality
projections, all 33 climate models, for Colombia. Its notebook maps a
single draw next to a pooled quantile, shows percent of GDP with the
ratio kind, and plots the spread of draws; run_example.py adds
municipalities and rates from the command line. examples/rates/
aggregates the effect of climate change on Mexican mortality rates to
municipalities on both GADM versions. examples/labor/ aggregates
the effect of climate change on labor supply to Indian states,
physical in minutes per worker per day and valued in 2005 PPP USD.
examples/energy/ aggregates the effect of climate change on energy
use to United States states with delta-method uncertainty: gradient
aggregation, the quadratic form, and the spread that independent
draws would lose. Notebook outputs are committed, so they read
without running.
Straight from GitHub:
pip install "git+https://github.com/ClimateImpactLab/cil_regionalization.git"
pip install "cil_regionalization[netcdf] @ git+https://github.com/ClimateImpactLab/cil_regionalization.git"
or from a clone:
pip install . # library and CLI
pip install '.[netcdf]' # also read NetCDF Monte Carlo trees
uv pip install . works too. The geospatial stack (geopandas,
shapely, rasterio, exactextract) installs with it and ships wheels on
Linux and macOS. [netcdf] reads the NetCDF Monte Carlo trees,
[bigquery] adds the BigQuery generation backend, [dev] adds
pytest. Python 3.10 or newer.
The envs/ directory and examples/montecarlo/runs/ exist to
reproduce the runs behind the published results on the University of
Chicago RCC cluster; they are not part of installing or using the
package.
A TOML config names the target geometry, the source geometry or grid,
the weighting rasters, and the normalization direction. cilreg run <config.toml> writes the weights parquet plus a manifest that
records where everything came from, with checksums. cilreg validate <config.toml> checks a config without computing anything.
Worked configurations, including the GADM target preparation and the
full generation runs, live under examples/.
GADM 2.0 and GADM 4.1 have different unit universes and different keys, so results built on one cannot be compared with the other. Each geometry version is its own record, and every manifest states which version it was built against.
Four records are published on Zenodo:
- the impact region shapefile, world-combo-201710 (doi:10.5281/zenodo.21934131)
- the SMME climate model weights (doi:10.5281/zenodo.22003542):
rcp45_SMME_weights.tsvandrcp85_SMME_weights.tsv, used by the statistics stage rather than the spatial aggregation - GADM 2.0 weights (doi:10.5281/zenodo.21934155):
gadm20-adm1-per-source,gadm20-adm1-per-destination,gadm20-adm2-per-source,gadm20-adm2-per-destination - GADM 4.1 weights (doi:10.5281/zenodo.21935431), keyed by GADM's GID
codes:
gadm41-adm1-per-source,gadm41-adm1-per-destination,gadm41-adm2-per-source,gadm41-adm2-per-destination. GADM 4.1 draws coastlines differently from the impact region geometry, so 1,178 coastal regions are partially covered; the manifests record this, andapply_weightsneedsallow_partial_coverage=Trueto proceed
The weights were built against two GADM versions, recorded in every
manifest by their version labels: GADM 2.0 (label
gadm-2.0-impactmap-copy-2025, a processed copy of the combined GADM
2.0 shapefile) and GADM 4.1 (label gadm-4.10-impactmap-copy-2022,
the gadm_410.gpkg GeoPackage as distributed by GADM). You can obtain
the same versions from GADM directly. The dissolved target layers are
large (840 MB for GADM 2.0, about 2 GB for GADM 4.1) and are not
distributed, since GADM restricts redistribution; regenerate them with
the dissolve scripts under examples/gadm20/ and examples/gadm41/.
The targets manifest records the source file's checksum, so you can
confirm a regenerated set started from the same input.
fetch_weights, apply_weights, summarize_samples,
window_means, pooled_statistics, read_gradient_leaf,
aggregate_gradient, delta_method, WeightsArtifact, and
load_config are the supported API, re-exported at the package root.
window_means and pooled_statistics are the two halves of
summarize_samples, for pipelines that reduce time per leaf before
pooling. Everything else is internal and may change.