{"spec_id":"lift-curve","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' lift-curve: Model Lift Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 92/100 | Created: 2026-09-05\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nGRID_LINE   <- if (THEME == \"light\") \"#D8D7D0\" else \"#3A3A36\"\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                     \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\")\n\n# --- Data: fraud detection model scores -------------------------------------\nn_transactions <- 4000\nfraud_rate <- 0.06\n\nis_fraud <- rbinom(n_transactions, 1, fraud_rate)\nfraud_score <- rbeta(n_transactions, 6, 2)\nlegit_score <- rbeta(n_transactions, 2, 6)\nmodel_score <- ifelse(is_fraud == 1, fraud_score, legit_score)\n\ntransactions <- tibble::tibble(is_fraud = is_fraud, model_score = model_score) %>%\n  arrange(desc(model_score)) %>%\n  mutate(\n    rank = row_number(),\n    pct_targeted = rank / n() * 100,\n    cum_capture_rate = cumsum(is_fraud) / rank,\n    lift = cum_capture_rate / mean(is_fraud)\n  )\n\ndecile_marks <- transactions %>%\n  filter(rank %in% round(n() * seq(0.1, 1.0, by = 0.1)))\n\ncallouts <- decile_marks %>%\n  filter(rank %in% round(n_transactions * c(0.1, 0.5))) %>%\n  mutate(label = sprintf(\"%.1fx @ %d%%\", lift, round(pct_targeted)))\n\n# --- Plot ---------------------------------------------------------------\np <- ggplot(transactions, aes(x = pct_targeted, y = lift)) +\n  geom_hline(aes(yintercept = 1, color = \"Random baseline\"),\n             linetype = \"dashed\", linewidth = 0.8) +\n  geom_line(aes(color = \"Model\"), linewidth = 1.2) +\n  geom_point(data = decile_marks, color = IMPRINT_PALETTE[1], size = 3) +\n  annotate(\n    \"text\",\n    x = callouts$pct_targeted, y = callouts$lift + 1, label = callouts$label,\n    color = INK, size = 3.2, fontface = \"bold\", hjust = 0\n  ) +\n  scale_color_manual(\n    name   = NULL,\n    values = c(\"Model\" = IMPRINT_PALETTE[1], \"Random baseline\" = INK_SOFT)\n  ) +\n  scale_x_continuous(labels = scales::label_percent(scale = 1)) +\n  labs(\n    title = \"Fraud Detection Model · lift-curve · r · ggplot2 · anyplot.ai\",\n    x = \"Population Targeted\",\n    y = \"Cumulative Lift\"\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background   = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background  = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major.y = element_line(color = GRID_LINE, linewidth = 0.3),\n    panel.grid.major.x = element_blank(),\n    panel.grid.minor  = element_blank(),\n    axis.title        = element_text(color = INK,      size = 10),\n    axis.text         = element_text(color = INK_SOFT, size = 8),\n    axis.ticks        = element_blank(),\n    plot.title        = element_text(color = INK,      size = 12),\n    legend.position   = \"inside\",\n    legend.position.inside = c(0.98, 0.94),\n    legend.justification = c(1, 1),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3),\n    legend.margin     = margin(t = 8, r = 8, b = 8, l = 8),\n    legend.key        = element_blank(),\n    legend.text       = element_text(color = INK_SOFT, size = 8)\n  )\n\n# --- Save ---------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}