Model Lift Chart — Apache ECharts

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 Apache ECharts

Renders

JavaScript source (Apache ECharts)

// anyplot.ai
// lift-curve: Model Lift Chart
// Library: echarts 6.1.0 | JavaScript 22.23.2
// Quality: 95/100 | Created: 2026-09-05

const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic fraud-detection scenario) --------------
// Tiny fixed-seed LCG so the ranking of predicted scores is reproducible.
let seed = 42;
function rand() {
  seed = (seed * 1103515245 + 12345) & 0x7fffffff;
  return seed / 0x7fffffff;
}

const nTransactions = 2000;
const fraudRate = 0.06;

// Model score correlates with true fraud label but with realistic noise
// (wide overlap between the two distributions), so the resulting lift curve
// starts well below the theoretical maximum and decays toward 1 - the shape
// of a genuinely good, but imperfect, classifier.
const records = [];
for (let i = 0; i < nTransactions; i++) {
  const isFraud = rand() < fraudRate ? 1 : 0;
  const score = isFraud
    ? Math.min(1, 0.35 + rand() * 0.55)
    : Math.max(0, rand() * 0.7);
  records.push({ isFraud, score });
}
records.sort((a, b) => b.score - a.score);

const totalFraud = records.reduce((sum, r) => sum + r.isFraud, 0);
const baselineRate = totalFraud / nTransactions;

// Cumulative lift at each decile (10%, 20%, ..., 100%) of targeted population.
const steps = 20;
const pctTargeted = [];
const liftValues = [];
for (let s = 1; s <= steps; s++) {
  const cutoff = Math.round((s / steps) * nTransactions);
  const capturedFraud = records
    .slice(0, cutoff)
    .reduce((sum, r) => sum + r.isFraud, 0);
  const targetedRate = capturedFraud / cutoff;
  pctTargeted.push(Math.round((s / steps) * 100));
  liftValues.push(Number((targetedRate / baselineRate).toFixed(2)));
}

// Decile markers (every other step = every 10%) for emphasis.
const decileIndices = pctTargeted
  .map((pct, idx) => (pct % 10 === 0 ? idx : -1))
  .filter((idx) => idx >= 0);

// --- Init --------------------------------------------------------------------
const chart = echarts.init(document.getElementById("container"));

// --- Option --------------------------------------------------------------------
chart.setOption({
  animation: false,
  color: t.palette,
  backgroundColor: "transparent",
  title: {
    text: "Fraud Detection Model · lift-curve · javascript · echarts · anyplot.ai",
    left: "center",
    top: 20,
    textStyle: { color: t.ink, fontSize: 19, fontWeight: 500 },
  },
  legend: {
    data: ["Model"],
    top: 70,
    textStyle: { color: t.inkSoft, fontSize: 15 },
  },
  grid: { left: 90, right: 70, top: 130, bottom: 90 },
  xAxis: {
    type: "value",
    name: "Population Targeted (%)",
    nameLocation: "middle",
    nameGap: 45,
    nameTextStyle: { color: t.ink, fontSize: 16 },
    min: 0,
    max: 100,
    axisLabel: {
      color: t.inkSoft,
      fontSize: 14,
      formatter: "{value}%",
    },
    axisLine: { lineStyle: { color: t.inkSoft } },
    splitLine: { show: false },
  },
  yAxis: {
    type: "value",
    name: "Cumulative Lift",
    nameLocation: "middle",
    nameGap: 60,
    nameTextStyle: { color: t.ink, fontSize: 16 },
    axisLabel: { color: t.inkSoft, fontSize: 14 },
    axisLine: { lineStyle: { color: t.inkSoft } },
    splitLine: { lineStyle: { color: t.grid } },
  },
  series: [
    {
      name: "Model",
      type: "line",
      data: pctTargeted.map((pct, idx) => [pct, liftValues[idx]]),
      smooth: false,
      symbol: "circle",
      symbolSize: (val, params) =>
        decileIndices.includes(params.dataIndex) ? 12 : 0,
      lineStyle: { color: t.palette[0], width: 4 },
      itemStyle: { color: t.palette[0] },
      areaStyle: {
        color: {
          type: "linear",
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            { offset: 0, color: `${t.palette[0]}33` },
            { offset: 1, color: `${t.palette[0]}00` },
          ],
        },
      },
      // Reference line for random selection (y=1) - a markLine keeps the
      // "no lift" baseline attached to the Model series instead of a second
      // full series, avoiding legend/series boilerplate for a constant line.
      markLine: {
        silent: true,
        symbol: "none",
        lineStyle: { color: t.inkSoft, width: 2, type: "dashed" },
        label: {
          show: true,
          formatter: "Random selection (no lift)",
          position: "insideMiddleTop",
          color: t.inkSoft,
          fontSize: 13,
        },
        data: [{ yAxis: 1 }],
      },
      // Callout annotating the headline lift value at the first decile.
      markPoint: {
        symbol: "pin",
        symbolSize: 56,
        itemStyle: { color: t.palette[0] },
        label: {
          formatter: `${liftValues[1].toFixed(1)}x`,
          color: t.pageBg,
          fontSize: 13,
          fontWeight: 600,
        },
        data: [
          {
            name: "Top decile lift",
            coord: [pctTargeted[1], liftValues[1]],
          },
        ],
      },
    },
  ],
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/lift-curve/echarts/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "lift-curve",
  "language": "javascript",
  "library": "echarts",
  "page": "https://anyplot.ai/lift-curve/javascript/echarts",
  "hub": "https://anyplot.ai/lift-curve",
  "code_json": "https://api.anyplot.ai/specs/lift-curve/echarts/code",
  "spec_json": "https://api.anyplot.ai/specs/lift-curve",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/lift-curve/javascript/echarts/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/lift-curve/javascript/echarts/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/lift-curve/javascript/echarts/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/lift-curve/javascript/echarts/plot-dark.html",
  "quality_score": 95.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Model Lift Chart on anyplot.ai.

Other implementations