{"spec_id":"lift-curve","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nlift-curve: Model Lift Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\nimport sys\n\n\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nif _script_dir in sys.path:\n    sys.path.remove(_script_dir)\nif \"\" in sys.path:\n    sys.path.remove(\"\")\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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\"\n\n# Okabe-Ito palette\nBRAND = \"#009E73\"  # First series (lift curve)\nREFERENCE_COLOR = \"#999999\"  # Reference line (adaptive neutral)\n\n# Data - Simulate customer churn prediction model results\nnp.random.seed(42)\nn_samples = 1000\n\n# Create realistic churn prediction scenario\n# True positives have higher scores, some overlap for realism\ny_true = np.concatenate([np.ones(200), np.zeros(800)])  # 20% churn rate\ny_score = np.concatenate(\n    [\n        np.clip(np.random.beta(5, 2, 200), 0, 1),  # Churners: higher scores\n        np.clip(np.random.beta(2, 5, 800), 0, 1),  # Non-churners: lower scores\n    ]\n)\n\n# Calculate lift curve\nsorted_indices = np.argsort(y_score)[::-1]  # Sort by score descending\ny_true_sorted = y_true[sorted_indices]\n\n# Calculate cumulative lift at each percentage\npercentages = np.arange(1, 101)\nn_total = len(y_true)\nn_positives = y_true.sum()\nbaseline_rate = n_positives / n_total\n\nlift_values = []\nfor pct in percentages:\n    n_selected = int(np.ceil(n_total * pct / 100))\n    n_captured = y_true_sorted[:n_selected].sum()\n    model_rate = n_captured / n_selected\n    lift = model_rate / baseline_rate\n    lift_values.append(lift)\n\n# Create DataFrame for Altair\ndf = pd.DataFrame({\"Population (%)\": percentages, \"Cumulative Lift\": lift_values})\n\n# Reference line at y=1 (random selection)\ndf_reference = pd.DataFrame({\"Population (%)\": [0, 100], \"Reference\": [1.0, 1.0]})\n\n# Create lift curve chart\nlift_line = (\n    alt.Chart(df)\n    .mark_line(strokeWidth=4, color=BRAND)\n    .encode(\n        x=alt.X(\"Population (%):Q\", scale=alt.Scale(domain=[0, 100]), title=\"Population Targeted (%)\"),\n        y=alt.Y(\"Cumulative Lift:Q\", scale=alt.Scale(domain=[0, 5]), title=\"Cumulative Lift\"),\n        tooltip=[alt.Tooltip(\"Population (%):Q\", format=\".0f\"), alt.Tooltip(\"Cumulative Lift:Q\", format=\".2f\")],\n    )\n)\n\n# Reference line at lift = 1\nreference_line = (\n    alt.Chart(df_reference)\n    .mark_line(strokeWidth=2, strokeDash=[8, 4], color=REFERENCE_COLOR)\n    .encode(x=\"Population (%):Q\", y=\"Reference:Q\")\n)\n\n# Add decile markers\ndecile_df = df[df[\"Population (%)\"].isin([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])]\ndecile_points = (\n    alt.Chart(decile_df)\n    .mark_point(size=200, color=BRAND, filled=True)\n    .encode(\n        x=\"Population (%):Q\",\n        y=\"Cumulative Lift:Q\",\n        tooltip=[\n            alt.Tooltip(\"Population (%):Q\", format=\".0f\", title=\"Decile %\"),\n            alt.Tooltip(\"Cumulative Lift:Q\", format=\".2f\", title=\"Lift\"),\n        ],\n    )\n)\n\n# Combine all layers\nchart = (\n    alt.layer(reference_line, lift_line, decile_points)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(text=\"lift-curve · altair · anyplot.ai\", fontSize=28, anchor=\"middle\"),\n    )\n    .configure_axis(\n        labelFontSize=18,\n        titleFontSize=22,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.10,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_title(color=INK)\n)\n\n# Save as PNG and HTML\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}