riskplotv0.5

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.

PyPI version Tests License: MIT Python 3.10-3.13

Install

$ pip install riskplot

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_at date, 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")
World choropleth of country risk scores
Country risk scores rendered with the matplotlib backend. Input used plain country names; the resolver mapped them to ISO codes before drawing.

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)
ROC curve and confusion matrix
Classification diagnostics: ROC curve with AUC, and a row-normalized confusion matrix.

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
LISA cluster map of residuals
LISA cluster map: where residuals cluster into significant hotspots and coldspots.

Docs & source

The value-scaled cartogram (riskplot.geo.cartogram) is the remaining scaffolded piece; everything else on this page is implemented and tested.