{"spec_id":"roc-curve","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nroc-curve: ROC Curve with AUC\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-09\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot import ggsave\n\n\nLetsPlot.setup_html()\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\"\n\n# Okabe-Ito palette\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Generate ROC curve data for multiple classifiers\nnp.random.seed(42)\n\n# Generate synthetic classification scores\nn_samples = 1000\ny_true = np.concatenate([np.zeros(500), np.ones(500)])\n\n# Model A - Good classifier (AUC ~0.92)\nscores_a = np.concatenate(\n    [\n        np.random.beta(2, 5, 500),  # Negative class\n        np.random.beta(5, 2, 500),  # Positive class\n    ]\n)\n\n# Model B - Moderate classifier (AUC ~0.78)\nscores_b = np.concatenate(\n    [\n        np.random.beta(2, 3, 500),  # Negative class\n        np.random.beta(3, 2, 500),  # Positive class\n    ]\n)\n\n\n# Calculate ROC curve points\ndef compute_roc(y_true, scores):\n    thresholds = np.linspace(0, 1, 200)\n    tpr_list = []\n    fpr_list = []\n    for thresh in thresholds:\n        predictions = (scores >= thresh).astype(int)\n        tp = np.sum((predictions == 1) & (y_true == 1))\n        fn = np.sum((predictions == 0) & (y_true == 1))\n        fp = np.sum((predictions == 1) & (y_true == 0))\n        tn = np.sum((predictions == 0) & (y_true == 0))\n        tpr = tp / (tp + fn) if (tp + fn) > 0 else 0\n        fpr = fp / (fp + tn) if (fp + tn) > 0 else 0\n        tpr_list.append(tpr)\n        fpr_list.append(fpr)\n    return np.array(fpr_list), np.array(tpr_list)\n\n\n# Compute ROC curves\nfpr_a, tpr_a = compute_roc(y_true, scores_a)\nfpr_b, tpr_b = compute_roc(y_true, scores_b)\n\n# Calculate AUC using trapezoidal rule\nauc_a = -np.trapezoid(tpr_a, fpr_a)\nauc_b = -np.trapezoid(tpr_b, fpr_b)\n\n# Create DataFrames for plotting\ndf_model_a = pd.DataFrame({\"fpr\": fpr_a, \"tpr\": tpr_a, \"model\": f\"Model A (AUC = {auc_a:.2f})\"})\n\ndf_model_b = pd.DataFrame({\"fpr\": fpr_b, \"tpr\": tpr_b, \"model\": f\"Model B (AUC = {auc_b:.2f})\"})\n\n# Random classifier reference line\ndf_random = pd.DataFrame({\"fpr\": [0, 1], \"tpr\": [0, 1], \"model\": \"Random (AUC = 0.50)\"})\n\n# Combine all data\ndf = pd.concat([df_model_a, df_model_b, df_random], ignore_index=True)\n\n# Theme-adaptive color for reference line\nref_color = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\ncolors = [IMPRINT[0], IMPRINT[1], ref_color]\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"fpr\", y=\"tpr\", color=\"model\"))\n    + geom_line(size=2)\n    + scale_color_manual(values=colors)\n    + scale_x_continuous(limits=[0, 1])\n    + scale_y_continuous(limits=[0, 1])\n    + coord_fixed(ratio=1)\n    + labs(\n        x=\"False Positive Rate\", y=\"True Positive Rate\", title=\"roc-curve · letsplot · anyplot.ai\", color=\"Classifier\"\n    )\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_title=element_text(size=24, color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_position=\"bottom\",\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale 3x = 4800 x 2700 px) and HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}