{"spec_id":"lift-curve","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nlift-curve: Model Lift Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\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\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito colors\nBRAND = \"#009E73\"  # bluish green - first series\nSECONDARY = \"#C475FD\"  # vermillion\n\n# Set seaborn theme with theme-adaptive colors\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data - simulate customer response prediction for marketing campaign\nnp.random.seed(42)\nn_samples = 1000\nbase_response_rate = 0.10  # 10% overall response rate\n\n# Create a model with good predictive power\nlatent_propensity = np.random.normal(0, 1, n_samples)\n\n# Model score approximates the latent propensity with some noise\ny_score = latent_propensity + np.random.normal(0, 0.3, n_samples)\ny_score = (y_score - y_score.min()) / (y_score.max() - y_score.min())\n\n# Actual responses based on latent propensity (strong correlation)\nresponse_threshold = np.percentile(latent_propensity, 100 * (1 - base_response_rate))\ny_true = (latent_propensity >= response_threshold).astype(int)\n\n# Calculate lift curve data\nsorted_indices = np.argsort(y_score)[::-1]\ny_true_sorted = y_true[sorted_indices]\n\nn_positives = y_true.sum()\ncumulative_positives = np.cumsum(y_true_sorted)\npopulation_percentages = np.arange(1, n_samples + 1) / n_samples * 100\n\ncumulative_positive_rate = cumulative_positives / np.arange(1, n_samples + 1)\nbaseline_rate = n_positives / n_samples\nlift = cumulative_positive_rate / baseline_rate\n\n# Create dataframe for seaborn\ndf = pd.DataFrame({\"Population Targeted (%)\": population_percentages, \"Cumulative Lift\": lift})\n\n# Create plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\n\n# Plot lift curve using seaborn lineplot\nsns.lineplot(\n    data=df, x=\"Population Targeted (%)\", y=\"Cumulative Lift\", ax=ax, color=BRAND, linewidth=3, label=\"Model Lift\"\n)\n\n# Add baseline reference line (random selection = lift of 1)\nax.axhline(y=1, color=INK_SOFT, linestyle=\"--\", linewidth=2.5, label=\"Random (No Lift)\", zorder=3)\n\n# Add decile markers with improved spacing\ndecile_percentages = [10, 20, 30, 40, 50]\nfor pct in decile_percentages:\n    idx = int(n_samples * pct / 100) - 1\n    pop_pct = population_percentages[idx]\n    lift_val = lift[idx]\n    ax.plot(pop_pct, lift_val, \"o\", color=BRAND, markersize=12, zorder=5)\n\n    # Alternate annotation positions to avoid cramping\n    offset_y = 25 if pct % 20 == 10 else 10\n    ax.annotate(\n        f\"{lift_val:.2f}x\",\n        (pop_pct, lift_val),\n        textcoords=\"offset points\",\n        xytext=(0, offset_y),\n        ha=\"center\",\n        fontsize=14,\n        fontweight=\"bold\",\n        color=INK,\n    )\n\n# Styling\nax.set_xlabel(\"Population Targeted (%)\", fontsize=20, color=INK)\nax.set_ylabel(\"Cumulative Lift Ratio\", fontsize=20, color=INK)\nax.set_title(\"lift-curve · seaborn · anyplot.ai\", fontsize=24, fontweight=\"bold\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Remove top and right 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# Subtle grid\nax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\n\n# Set axis limits\nax.set_xlim(0, 100)\nax.set_ylim(0, max(lift) * 1.1)\n\n# Legend styling\nlegend = ax.legend(fontsize=16, loc=\"upper right\", framealpha=0.95)\nlegend.get_frame().set_facecolor(ELEVATED_BG)\nlegend.get_frame().set_edgecolor(INK_SOFT)\n\n# Tight layout\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}