Model Lift Chart — Matplotlib

A lift curve visualizes how much better a predictive model performs compared to random selection, showing the cumulative lift ratio as you target increasing percentages of the population. It answers the question: "If I target the top X% of predictions, how many times more responders will I capture than random targeting?" This plot is essential for evaluating and comparing classification models in scenarios where targeting efficiency matters.

Model Lift Chart rendered with Matplotlib

Python source (Matplotlib)

""" anyplot.ai
lift-curve: Model Lift Chart
Library: matplotlib 3.10.9 | Python 3.13.13
Quality: 94/100 | Updated: 2026-05-10
"""

import os

import matplotlib.pyplot as plt
import numpy as np


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
BRAND = "#009E73"  # Okabe-Ito position 1
ACCENT = "#AE3030"  # Okabe-Ito position 5 for reference line

# Data - Simulate realistic customer response model predictions
np.random.seed(42)
n_samples = 1000
base_rate = 0.15  # 15% baseline response rate

# Generate true labels with base rate
y_true = np.random.binomial(1, base_rate, n_samples)

# Generate model scores - correlated with true outcomes for realistic model
# Good responders get higher scores, non-responders get lower scores
y_score = np.where(
    y_true == 1,
    np.clip(np.random.beta(5, 2, n_samples), 0, 1),  # Responders: higher scores
    np.clip(np.random.beta(2, 5, n_samples), 0, 1),  # Non-responders: lower scores
)

# Calculate lift curve
# Sort by predicted score (descending)
sorted_indices = np.argsort(y_score)[::-1]
y_true_sorted = y_true[sorted_indices]

# Calculate cumulative response rate and lift
n_total = len(y_true)
n_positive = y_true.sum()
baseline_rate = n_positive / n_total

# Calculate cumulative lift at each percentage
percentages = np.arange(1, 101)
lift_values = []

for pct in percentages:
    n_selected = int(np.ceil(n_total * pct / 100))
    n_responders = y_true_sorted[:n_selected].sum()
    response_rate = n_responders / n_selected
    lift = response_rate / baseline_rate
    lift_values.append(lift)

lift_values = np.array(lift_values)

# Plot
fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)
ax.set_facecolor(PAGE_BG)

# Plot lift curve
ax.plot(percentages, lift_values, color=BRAND, linewidth=3, label="Model Lift", zorder=3)

# Reference line at y=1 (random selection)
ax.axhline(y=1, color=ACCENT, linestyle="--", linewidth=2.5, label="Random (Lift = 1)", zorder=2)

# Fill area under curve for visual emphasis
ax.fill_between(percentages, 1, lift_values, where=(lift_values > 1), alpha=0.15, color=BRAND, zorder=1)

# Style
ax.set_xlabel("Population Targeted (%)", fontsize=20, color=INK)
ax.set_ylabel("Cumulative Lift", fontsize=20, color=INK)
ax.set_title("lift-curve · matplotlib · anyplot.ai", fontsize=24, fontweight="medium", color=INK)
ax.tick_params(axis="both", labelsize=16, colors=INK_SOFT)

# Set axis limits
ax.set_xlim(0, 100)
ax.set_ylim(0, max(lift_values) * 1.15)

# Grid
ax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)

# Spines
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
for s in ("left", "bottom"):
    ax.spines[s].set_color(INK_SOFT)

# Legend
leg = ax.legend(fontsize=16, loc="upper right")
if leg:
    leg.get_frame().set_facecolor(ELEVATED_BG)
    leg.get_frame().set_edgecolor(INK_SOFT)
    plt.setp(leg.get_texts(), color=INK_SOFT)

plt.tight_layout()
plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG)

Part of Model Lift Chart on anyplot.ai.

Other implementations