Model Lift Chart — Pygal

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 Pygal

Python source (Pygal)

""" anyplot.ai
lift-curve: Model Lift Chart
Library: pygal 3.1.0 | Python 3.13.13
Quality: 84/100 | Updated: 2026-05-10
"""

import os

import numpy as np
import pygal
from pygal.style import Style


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"

IMPRINT = ("#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD", "#954477")

# Data - Simulated customer response prediction
np.random.seed(42)
n_samples = 1000

# Generate realistic model scores and true outcomes
y_score = np.random.beta(2, 5, n_samples)
response_prob = 0.1 + 0.6 * y_score
y_true = (np.random.random(n_samples) < response_prob).astype(int)

# Calculate lift curve data
sorted_indices = np.argsort(y_score)[::-1]
y_true_sorted = y_true[sorted_indices]

# Calculate cumulative metrics
n_total = len(y_true)
n_positive = y_true.sum()
baseline_rate = n_positive / n_total

# Calculate lift at decile intervals
deciles = list(range(10, 101, 10))
lift_values = []

for pct in deciles:
    n_targeted = int(n_total * pct / 100)
    positives_captured = y_true_sorted[:n_targeted].sum()
    model_rate = positives_captured / n_targeted
    lift = model_rate / baseline_rate if baseline_rate > 0 else 1
    lift_values.append(lift)

# Create custom style
custom_style = Style(
    background=PAGE_BG,
    plot_background=PAGE_BG,
    foreground=INK,
    foreground_strong=INK,
    foreground_subtle=INK_SOFT,
    colors=IMPRINT,
    title_font_size=28,
    label_font_size=22,
    major_label_font_size=18,
    legend_font_size=18,
    value_font_size=16,
    stroke_width=4,
)

# Create line chart
chart = pygal.Line(
    width=4800,
    height=2700,
    style=custom_style,
    title="lift-curve · pygal · anyplot.ai",
    x_title="Population Targeted (%)",
    y_title="Lift (Model Rate / Baseline Rate)",
    show_dots=True,
    dots_size=10,
    stroke_style={"width": 5},
    fill=False,
    show_x_guides=False,
    show_y_guides=True,
    legend_at_bottom=False,
    range=(0.9, max(lift_values) * 1.1),
    margin=100,
)

# X-axis labels at deciles
chart.x_labels = [f"{d}%" for d in deciles]

# Add lift curve with tooltip-friendly data
chart.add("Model Lift", [{"value": v, "label": f"{v:.2f}"} for v in lift_values])

# Add baseline reference line at y=1
baseline = [1.0] * len(deciles)
chart.add("Random (No Lift)", baseline)

# Save as PNG and HTML
chart.render_to_png(f"plot-{THEME}.png")
with open(f"plot-{THEME}.html", "wb") as f:
    f.write(chart.render())

Part of Model Lift Chart on anyplot.ai.

Other implementations