{"spec_id":"precision-recall","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nprecision-recall: Precision-Recall Curve\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\nimport sys\n\n\nif __name__ == \"__main__\":\n    sys.path = [p for p in sys.path if \"precision-recall\" not in p]\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\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\nnp.random.seed(42)\n\nn_points = 100\nrecall_vals = np.linspace(1, 0, n_points)\n\nlr_precision = 0.3 + 0.65 * (1 - recall_vals) + np.random.normal(0, 0.02, n_points)\nlr_precision = np.clip(lr_precision, 0, 1)\nlr_precision = np.maximum.accumulate(lr_precision)\nlr_ap = np.trapezoid(lr_precision, recall_vals[::-1])\n\nrf_precision = 0.4 + 0.58 * (1 - recall_vals) ** 0.7 + np.random.normal(0, 0.015, n_points)\nrf_precision = np.clip(rf_precision, 0, 1)\nrf_precision = np.maximum.accumulate(rf_precision)\nrf_ap = np.trapezoid(rf_precision, recall_vals[::-1])\n\nbaseline = 0.30\n\nlr_df = pd.DataFrame(\n    {\"Recall\": recall_vals, \"Precision\": lr_precision, \"Model\": f\"Logistic Regression (AP = {lr_ap:.3f})\"}\n)\n\nrf_df = pd.DataFrame({\"Recall\": recall_vals, \"Precision\": rf_precision, \"Model\": f\"Random Forest (AP = {rf_ap:.3f})\"})\n\ncurve_df = pd.concat([lr_df, rf_df], ignore_index=True)\n\nbaseline_df = pd.DataFrame(\n    {\"Recall\": [0.0, 1.0], \"Precision\": [baseline, baseline], \"Model\": f\"Random Classifier (baseline = {baseline:.2f})\"}\n)\n\npr_curves = (\n    alt.Chart(curve_df)\n    .mark_line(strokeWidth=4, interpolate=\"step-after\")\n    .encode(\n        x=alt.X(\"Recall:Q\", title=\"Recall (True Positive Rate)\", scale=alt.Scale(domain=[0, 1])),\n        y=alt.Y(\"Precision:Q\", title=\"Precision (Positive Predictive Value)\", scale=alt.Scale(domain=[0, 1])),\n        color=alt.Color(\n            \"Model:N\",\n            scale=alt.Scale(\n                domain=[f\"Logistic Regression (AP = {lr_ap:.3f})\", f\"Random Forest (AP = {rf_ap:.3f})\"],\n                range=[IMPRINT[0], IMPRINT[1]],\n            ),\n            legend=alt.Legend(\n                title=\"Model\",\n                titleFontSize=20,\n                labelFontSize=16,\n                labelLimit=400,\n                orient=\"bottom-right\",\n                direction=\"vertical\",\n                offset=10,\n                symbolStrokeWidth=4,\n                symbolSize=300,\n                fillColor=ELEVATED_BG,\n                strokeColor=INK_SOFT,\n                labelColor=INK_SOFT,\n                titleColor=INK,\n            ),\n        ),\n        strokeDash=alt.StrokeDash(\n            \"Model:N\",\n            scale=alt.Scale(\n                domain=[f\"Logistic Regression (AP = {lr_ap:.3f})\", f\"Random Forest (AP = {rf_ap:.3f})\"],\n                range=[[0], [0]],\n            ),\n            legend=None,\n        ),\n    )\n)\n\nbaseline_line = (\n    alt.Chart(baseline_df)\n    .mark_line(strokeWidth=3, strokeDash=[8, 4])\n    .encode(x=alt.X(\"Recall:Q\"), y=alt.Y(\"Precision:Q\"), color=alt.ColorValue(IMPRINT[2]))\n)\n\nchart = (\n    alt.layer(pr_curves, baseline_line)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\"precision-recall · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_axis(\n        labelFontSize=18,\n        titleFontSize=22,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        gridColor=INK,\n        gridOpacity=0.10,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}