{"spec_id":"curve-bias-variance-tradeoff","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\ncurve-bias-variance-tradeoff: Bias-Variance Tradeoff Curve\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-28\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\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# Data — formula distinct from seaborn sibling (change request)\nnp.random.seed(42)\ncomplexity = np.linspace(0.5, 10.0, 100)\nbias_squared = 3.0 / (1 + 0.7 * complexity)\nvariance = 0.08 * complexity**1.3\nirreducible_error = np.full_like(complexity, 0.25)\ntotal_error = bias_squared + variance + irreducible_error\n\noptimal_idx = int(np.argmin(total_error))\noptimal_complexity = float(complexity[optimal_idx])\noptimal_error = float(total_error[optimal_idx])\n\ncomponent_order = [\"Bias²\", \"Variance\", \"Irreducible Error\", \"Total Error\"]\ndf = pd.DataFrame(\n    {\n        \"complexity\": np.tile(complexity, 4),\n        \"error\": np.concatenate([bias_squared, variance, irreducible_error, total_error]),\n        \"component\": pd.Categorical(\n            [\"Bias²\"] * 100 + [\"Variance\"] * 100 + [\"Irreducible Error\"] * 100 + [\"Total Error\"] * 100,\n            categories=component_order,\n        ),\n    }\n)\n\n# Curve values at x=10 for right-side direct labels\nx_lbl = 10.0\nbias_at_lbl = float(3.0 / (1 + 0.7 * x_lbl))\nvar_at_lbl = float(0.08 * x_lbl**1.3)\nirred_at_lbl = 0.25\ntotal_at_lbl = bias_at_lbl + var_at_lbl + irred_at_lbl\n\nlabel_df = pd.DataFrame(\n    {\n        \"complexity\": [x_lbl + 0.3] * 4,\n        \"error\": [bias_at_lbl + 0.15, var_at_lbl + 0.05, irred_at_lbl - 0.22, total_at_lbl + 0.04],\n        \"label\": [\"Bias²\", \"Variance\", \"Irred.\\nError\", \"Total\\nError\"],\n        \"component\": pd.Categorical(component_order, categories=component_order),\n    }\n)\n\n# Shaded underfitting / overfitting zones\nunder_df = pd.DataFrame({\"xmin\": [0.4], \"xmax\": [optimal_complexity], \"ymin\": [-0.15], \"ymax\": [3.38]})\nover_df = pd.DataFrame({\"xmin\": [optimal_complexity], \"xmax\": [10.5], \"ymin\": [-0.15], \"ymax\": [3.38]})\n\n# Zone labels\nzone_df = pd.DataFrame(\n    {\n        \"x\": [optimal_complexity * 0.42, (optimal_complexity + 10.5) * 0.5],\n        \"y\": [3.12, 3.12],\n        \"label\": [\"← Underfitting\\n(High Bias)\", \"Overfitting →\\n(High Variance)\"],\n    }\n)\n\n# Optimal complexity annotation\nopt_df = pd.DataFrame({\"x\": [optimal_complexity + 0.25], \"y\": [optimal_error + 0.40], \"label\": [\"Optimal\\nComplexity\"]})\n\n# Formula annotation\nformula_df = pd.DataFrame({\"x\": [5.25], \"y\": [2.80], \"label\": [\"Total Error = Bias² + Variance + Irreducible Error\"]})\n\n# Imprint palette: Bias²=pos1 (green), Variance=pos2 (purple), Irred.=pos3 (blue), Total=red (semantic error)\ncolor_values = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#AE3030\"]\nlinetype_values = [\"dashed\", \"longdash\", \"dotted\", \"solid\"]\n\ntitle_str = \"curve-bias-variance-tradeoff · python · letsplot · anyplot.ai\"\ntitle_n = len(title_str)\ntitle_size = max(11, round(16 * 67 / title_n)) if title_n > 67 else 16\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_border=element_blank(),\n    panel_grid_major_x=element_blank(),\n    panel_grid_major_y=element_line(color=INK_SOFT, size=0.12),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(color=INK, size=12),\n    axis_text=element_text(color=INK_SOFT, size=10),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(color=INK, size=title_size),\n    legend_position=\"none\",\n)\n\nplot = (\n    ggplot(df, aes(x=\"complexity\", y=\"error\", color=\"component\"))\n    + geom_rect(\n        data=under_df,\n        mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=\"#009E73\",\n        color=PAGE_BG,\n        alpha=0.06,\n        inherit_aes=False,\n    )\n    + geom_rect(\n        data=over_df,\n        mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=\"#AE3030\",\n        color=PAGE_BG,\n        alpha=0.06,\n        inherit_aes=False,\n    )\n    + geom_line(aes(linetype=\"component\"), size=1.0, tooltips=layer_tooltips([\"component\", \"error\"]))\n    + geom_vline(xintercept=optimal_complexity, linetype=\"dotdash\", color=INK_SOFT, size=0.65)\n    + geom_text(\n        data=zone_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), inherit_aes=False, color=INK_SOFT, size=3.3, hjust=0.5\n    )\n    + geom_text(data=opt_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), inherit_aes=False, color=INK, size=3.2, hjust=0)\n    + geom_text(\n        data=formula_df,\n        mapping=aes(x=\"x\", y=\"y\", label=\"label\"),\n        inherit_aes=False,\n        color=INK_SOFT,\n        size=3.3,\n        hjust=0.5,\n    )\n    + geom_text(\n        data=label_df,\n        mapping=aes(x=\"complexity\", y=\"error\", label=\"label\", color=\"component\"),\n        inherit_aes=False,\n        size=3.2,\n        hjust=0,\n    )\n    + scale_color_manual(values=color_values)\n    + scale_linetype_manual(values=linetype_values)\n    + scale_x_continuous(limits=[0.4, 12.0], breaks=[2, 4, 6, 8, 10])\n    + scale_y_continuous(limits=[-0.15, 3.45])\n    + labs(x=\"Model Complexity\", y=\"Prediction Error\", title=title_str)\n    + ggsize(800, 450)\n    + theme_classic()\n    + anyplot_theme\n)\n\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}