{"spec_id":"lift-curve","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nlift-curve: Model Lift Chart\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"  # Okabe-Ito position 1\nACCENT = \"#AE3030\"  # Okabe-Ito position 5 for reference line\n\n# Data - Simulate realistic customer response model predictions\nnp.random.seed(42)\nn_samples = 1000\nbase_rate = 0.15  # 15% baseline response rate\n\n# Generate true labels with base rate\ny_true = np.random.binomial(1, base_rate, n_samples)\n\n# Generate model scores - correlated with true outcomes for realistic model\n# Good responders get higher scores, non-responders get lower scores\ny_score = np.where(\n    y_true == 1,\n    np.clip(np.random.beta(5, 2, n_samples), 0, 1),  # Responders: higher scores\n    np.clip(np.random.beta(2, 5, n_samples), 0, 1),  # Non-responders: lower scores\n)\n\n# Calculate lift curve\n# Sort by predicted score (descending)\nsorted_indices = np.argsort(y_score)[::-1]\ny_true_sorted = y_true[sorted_indices]\n\n# Calculate cumulative response rate and lift\nn_total = len(y_true)\nn_positive = y_true.sum()\nbaseline_rate = n_positive / n_total\n\n# Calculate cumulative lift at each percentage\npercentages = np.arange(1, 101)\nlift_values = []\n\nfor pct in percentages:\n    n_selected = int(np.ceil(n_total * pct / 100))\n    n_responders = y_true_sorted[:n_selected].sum()\n    response_rate = n_responders / n_selected\n    lift = response_rate / baseline_rate\n    lift_values.append(lift)\n\nlift_values = np.array(lift_values)\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Plot lift curve\nax.plot(percentages, lift_values, color=BRAND, linewidth=3, label=\"Model Lift\", zorder=3)\n\n# Reference line at y=1 (random selection)\nax.axhline(y=1, color=ACCENT, linestyle=\"--\", linewidth=2.5, label=\"Random (Lift = 1)\", zorder=2)\n\n# Fill area under curve for visual emphasis\nax.fill_between(percentages, 1, lift_values, where=(lift_values > 1), alpha=0.15, color=BRAND, zorder=1)\n\n# Style\nax.set_xlabel(\"Population Targeted (%)\", fontsize=20, color=INK)\nax.set_ylabel(\"Cumulative Lift\", fontsize=20, color=INK)\nax.set_title(\"lift-curve · matplotlib · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Set axis limits\nax.set_xlim(0, 100)\nax.set_ylim(0, max(lift_values) * 1.15)\n\n# Grid\nax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\n\n# Spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\n# Legend\nleg = ax.legend(fontsize=16, loc=\"upper right\")\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}