Python
Lift Chart
Gains Chart
Data Visualization
Machine Learning

How to build a lift chart a.k.a gains chart in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A lift chart, also called a gains chart, shows how much better a model performs than random selection when ranked by predicted probability. It is especially useful for binary classification tasks such as churn prediction, fraud detection, or campaign targeting. This guide walks through the full Python workflow from predictions to chart interpretation.

Prepare Model Scores and Labels

Lift analysis starts with two arrays: true binary labels and model probabilities for the positive class.

python
1import numpy as np
2import pandas as pd
3from sklearn.datasets import make_classification
4from sklearn.linear_model import LogisticRegression
5from sklearn.model_selection import train_test_split
6
7X, y = make_classification(
8    n_samples=4000,
9    n_features=12,
10    n_informative=6,
11    n_redundant=2,
12    weights=[0.75, 0.25],
13    random_state=42,
14)
15
16X_train, X_test, y_train, y_test = train_test_split(
17    X, y, test_size=0.3, stratify=y, random_state=42
18)
19
20model = LogisticRegression(max_iter=500)
21model.fit(X_train, y_train)
22
23proba = model.predict_proba(X_test)[:, 1]
24
25df = pd.DataFrame({"y_true": y_test, "score": proba})
26print(df.head())

Scores should represent likelihood of positive outcome and must be sorted from highest to lowest.

Build Deciles and Cumulative Gains

Create quantile buckets, then compute cumulative positives and cumulative sample share.

python
1import pandas as pd
2
3
4def gains_table(y_true, score, n_bins=10):
5    d = pd.DataFrame({"y_true": y_true, "score": score}).copy()
6    d = d.sort_values("score", ascending=False).reset_index(drop=True)
7
8    d["bucket"] = pd.qcut(
9        d.index + 1,
10        q=n_bins,
11        labels=False,
12        duplicates="drop"
13    ) + 1
14
15    grouped = d.groupby("bucket", as_index=False).agg(
16        samples=("y_true", "count"),
17        positives=("y_true", "sum")
18    )
19
20    grouped["cum_samples"] = grouped["samples"].cumsum()
21    grouped["cum_positives"] = grouped["positives"].cumsum()
22
23    total_samples = grouped["samples"].sum()
24    total_positives = grouped["positives"].sum()
25
26    grouped["cum_sample_pct"] = grouped["cum_samples"] / total_samples
27    grouped["cum_gain"] = grouped["cum_positives"] / total_positives
28    grouped["lift"] = grouped["cum_gain"] / grouped["cum_sample_pct"]
29
30    return grouped
31
32
33table = gains_table(df["y_true"], df["score"], n_bins=10)
34print(table)

cum_gain measures captured positives up to each bucket. lift compares that gain to random targeting.

Plot Gains and Lift Curves

Plot both curves to understand ranking quality and top-segment efficiency.

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4x = table["cum_sample_pct"].values
5gain = table["cum_gain"].values
6lift = table["lift"].values
7
8fig, axes = plt.subplots(1, 2, figsize=(12, 4))
9
10axes[0].plot(x, gain, marker="o", label="Model gains")
11axes[0].plot([0, 1], [0, 1], linestyle="--", label="Random baseline")
12axes[0].set_title("Cumulative Gains Chart")
13axes[0].set_xlabel("Cumulative sample share")
14axes[0].set_ylabel("Cumulative positives captured")
15axes[0].legend()
16
17axes[1].plot(x, lift, marker="o", color="darkgreen")
18axes[1].axhline(1.0, linestyle="--", color="gray")
19axes[1].set_title("Lift Curve")
20axes[1].set_xlabel("Cumulative sample share")
21axes[1].set_ylabel("Lift")
22
23plt.tight_layout()
24plt.show()

A strong model climbs quickly in the gains chart and shows lift greater than one in early buckets.

Interpret Results for Business Decisions

If the top ten percent of ranked users captures forty percent of positives, the model has strong prioritization value. Teams can target only top buckets when outreach budget is limited.

Compare lift curves across model versions, not just AUC scores. Two models can have similar AUC but different top-decile lift, which matters for campaign efficiency.

Also check calibration separately. Lift chart quality reflects ranking ability, not probability calibration.

Compare Lift Across Candidate Models

Lift charts are most useful when comparing competing models on the same holdout set. Build one gains table per model and overlay curves in a single figure. Focus on early buckets, since business programs often target only a small population segment. If Model A has slightly lower overall AUC but much stronger top-decile lift, it may still deliver better campaign outcomes. Always report sample counts per bucket so stakeholders understand statistical stability before making deployment decisions.

Common Pitfalls

A common mistake is using predicted class labels instead of probabilities. Lift charts require ranking signal, so class labels lose information.

Another issue is computing deciles before sorting by score. That breaks cumulative interpretation and yields misleading curves.

Teams also forget to evaluate on holdout data. Lift measured on training data is overly optimistic.

Finally, highly imbalanced datasets can produce unstable bucket metrics with small test sets. Use sufficient sample size and repeated validation splits.

Summary

  • Lift and gains charts evaluate ranking effectiveness for binary models.
  • Use true labels plus predicted probabilities, sorted by score descending.
  • Compute cumulative sample share, cumulative gain, and lift by buckets.
  • Plot both gains and lift curves to assess top-segment performance.
  • Evaluate on holdout data and align interpretation with business targeting goals.

Course illustration
Course illustration

All Rights Reserved.