Plotlyml library: ML Monitoring Visualization Primitives for Plotly β€” Distribution Drift, Model Disagreement, Quantile Evolution Resources

Hi Plotly community!

Just wanted to highlight this cool plotlyml project :smiling_face_with_sunglasses:


This library provides three new high-level functions for plotly that address a real gap in the ML observability and data science space. These are general-purpose visualization primitives β€” not domain-specific β€” that fill gaps common in production ML workflows.


1. distribution_drift(reference, current, ...)

What it does: Compares two distributions (e.g. training vs. live inference data) with overlapping normalized histograms and a scalar KL-divergence annotation. When drift exceeds a configurable threshold, the current distribution is highlighted in a warning color.

fig = distribution_drift(
    reference,                # array-like: baseline samples
    current,                  # array-like: current samples
    bins=50,
    divergence_threshold=0.1,
    reference_name="Reference",
    current_name="Current",
    title=None,
    template=None,
)

Gap it fills: px.histogram with barmode="overlay" gets you close, but there’s no built-in divergence scoring, threshold annotation, or drift-aware color logic. This is a one-liner for a very common ML monitoring pattern.


2. model_disagreement(x, y, predictions, ...)

What it does: Scatter plot of samples in a 2-D reduced feature space (UMAP/t-SNE/PCA), colored by ensemble variance. Samples above a disagreement threshold are fully opaque; others are dimmed. Includes a marginal histogram of variance scores.

fig = model_disagreement(
    x,                        # dim 1 of reduced space (per sample)
    y,                        # dim 2 of reduced space
    predictions,              # shape (n_samples, n_models) β€” ensemble preds
    threshold=0.05,
    colorscale="Viridis",
    title=None,
    template=None,
)

Gap it fills: px.scatter with color= handles the spatial encoding, but computing ensemble variance, dimming low-uncertainty samples, and combining with a marginal histogram requires significant boilerplate. This surfaces a critical active-learning and model-audit pattern as a single call.


3. quantile_evolution(timestamps, p50, *, p10, p90, p25, p75, ...)

What it does: Layered ribbon/band chart tracking P10–P90 and P25–P75 bands with P50 as a bold center line, over time or cohorts. Automatically annotates timestamps where the spread exceeds a volatility threshold.

fig = .quantile_evolution(
    timestamps,               # x-axis: ISO strings, labels, or numbers
    p50=median_values,        # required: median (center line)
    p10=p10_values,           # optional: outer lower band
    p90=p90_values,           # optional: outer upper band
    p25=p25_values,           # optional: IQR lower bound
    p75=p75_values,           # optional: IQR upper bound
    mean=mean_values,
    show_mean=False,
    volatility_multiplier=1.5,
    title=None,
    template=None,
)

Gap it fills: Building fill-between ribbon plots requires chaining 5+ go.Scatter traces with careful fill="tonexty" sequencing. This is error-prone and undiscoverable. A single px call makes this pattern accessible to the full data science audience.

2 Likes