Risk visualization and mapping for Python
riskplot renders charts and maps from ordinary pandas and numpy inputs. It is a plotting library — matplotlib by default, optional plotly for interactive output — and it stays out of the analysis itself.
Install
The core install covers the charts, the country resolver, and basic choropleths — no heavyweight geospatial stack required, because a simplified world geometry ships with the package. Optional features live behind extras:
pip install 'riskplot[geo]' # geopandas / shapely / pyproj
pip install 'riskplot[plotly]' # interactive backends
pip install 'riskplot[spatial]' # libpysal / esda (spatial autocorrelation)
pip install 'riskplot[ml]' # scikit-learn metrics
pip install 'riskplot[all]' # everything
Heavy dependencies are imported lazily and raise a clear error naming the extra to install. Nothing reaches out to the network at import or render time.
Country-code reconciliation
Country fields arrive as inconsistent free text or mixed code systems:
Ivory Coast vs Côte d'Ivoire,
Burma vs Myanmar, UK /
GB / United Kingdom. Aggregation and mapping
break silently when these do not reconcile.
CountryResolver resolves them to ISO 3166 codes and
records how it did it. Every result carries a confidence score and a
method, so you can audit each match instead of trusting it blindly.
from riskplot.geo import CountryResolver, to_iso3, normalize_countries
to_iso3("Ivory Coast") # 'CIV'
to_iso3("Burma") # 'MMR'
to_iso3("UK") # 'GBR'
resolver = CountryResolver()
audit = resolver.resolve_series(df["country"])
review = audit[audit["confidence"] < 1.0] # everything worth a second look
df = normalize_countries(df, "country") # append iso_a3, confidence, ...
Handles the awkward cases
- Aliases, exonyms, and abbreviations
- Historical names honoured for an
as_atdate, flagged when outdated - Genuinely ambiguous input (bare
Congo,Korea) surfaced with candidates rather than guessed - Fuzzy matching for typos, above a configurable threshold
Resolution order
- Normalize (casefold, strip diacritics/punctuation)
- Exact ISO code (alpha-2 / alpha-3 / numeric)
- Curated alias & historical-name table
- Official / common name via pycountry
- Fuzzy match, else left unmatched
Choropleth maps
Pass raw country names straight to choropleth — it resolves
them, bins the values, and draws from the bundled geometry. No geopandas
install needed for country-level maps.
import pandas as pd
from riskplot.geo import choropleth
data = pd.DataFrame({
"country": ["United States", "Brazil", "Germany", "Ivory Coast", "UK"],
"risk": [22, 55, 20, 70, 25],
})
ax = choropleth(data, location="country", value="risk", scheme="quantiles")
Charts
The chart plotters from earlier releases are still here, now under
riskplot.charts and re-exported at the top level:
import riskplot as rp
rp.ridge_plot(data, "category", "returns")
rp.correlation_heatmap(returns)
rp.risk_attribution_waterfall(data, "factor", "contribution")
rp.risk_matrix(data, "probability", "impact", "label")
Model diagnostics
riskplot.ml provides framework-agnostic model-evaluation
plots — inputs are arrays, never model objects. Metrics are computed in
numpy, so nothing beyond the core install is needed.
from riskplot import ml
ml.roc_curve(y_true, y_score)
ml.confusion_matrix(y_true, y_pred, normalize="true")
ml.calibration_plot(y_true, y_prob)
ml.pred_vs_actual(y_true, y_pred) # regression, with R²
ml.feature_importance(importances, names)
riskplot.geo.ml adds spatial diagnostics: scikit-learn
compatible spatial cross-validation splitters, residual maps, and
spatial autocorrelation (Moran's I, Getis-Ord, LISA) built on a bundled
country-adjacency table.
from riskplot.geo.ml import SpatialBlockCV, morans_i, plot_lisa
result = morans_i(residuals, geo) # global + local Moran's I
print(result.statistic, result.p_value)
plot_lisa(residuals, geo) # hotspot / coldspot clusters
Gallery
Docs & source
The value-scaled cartogram (riskplot.geo.cartogram) is the
remaining scaffolded piece; everything else on this page is implemented
and tested.