{"spec_id":"learning-curve-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nlearning-curve-basic: Model Learning Curve\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_ribbon,\n    ggplot,\n    ggsave,\n    labs,\n    scale_color_manual,\n    scale_fill_manual,\n    theme,\n    theme_minimal,\n)\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\"\n\n# Okabe-Ito palette\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data - Underfitting scenario with different seed and pattern\nnp.random.seed(123)\n\n# Training set sizes (12 points from 20 to 500 samples)\ntrain_sizes = np.linspace(20, 500, 12).astype(int)\n\n# Underfitting pattern: both curves start low and plateau together\n# This shows a model that cannot achieve high accuracy even with more data\ntrain_mean = 0.72 + 0.12 * np.tanh((train_sizes - 150) / 100)\ntrain_std = 0.04 * np.exp(-train_sizes / 300) + 0.02\n\n# Validation scores follow training more closely (underfitting signature)\n# Small gap between train and validation\nval_mean = 0.70 + 0.10 * np.tanh((train_sizes - 150) / 100)\nval_std = 0.05 * np.exp(-train_sizes / 250) + 0.025\n\n# Create DataFrame for plotting\ndf_train = pd.DataFrame(\n    {\n        \"Training Set Size\": train_sizes,\n        \"Score\": train_mean,\n        \"Score_low\": np.maximum(0, train_mean - train_std),\n        \"Score_high\": np.minimum(1, train_mean + train_std),\n        \"Type\": \"Training Score\",\n    }\n)\n\ndf_val = pd.DataFrame(\n    {\n        \"Training Set Size\": train_sizes,\n        \"Score\": val_mean,\n        \"Score_low\": np.maximum(0, val_mean - val_std),\n        \"Score_high\": np.minimum(1, val_mean + val_std),\n        \"Type\": \"Validation Score\",\n    }\n)\n\ndf = pd.concat([df_train, df_val], ignore_index=True)\n\n# Colors: Okabe-Ito brand green for training, second color for validation\ncolors = {\"Training Score\": IMPRINT[0], \"Validation Score\": IMPRINT[1]}\n\n# Custom theme\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_border=element_rect(color=INK_SOFT, fill=None),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=24),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK),\n    figure_size=(16, 9),\n    legend_position=(0.75, 0.25),\n)\n\n# Create plot\nplot = (\n    ggplot(df, aes(x=\"Training Set Size\", y=\"Score\", color=\"Type\", fill=\"Type\"))\n    + geom_ribbon(aes(ymin=\"Score_low\", ymax=\"Score_high\"), alpha=0.25, color=\"none\")\n    + geom_line(size=2)\n    + scale_color_manual(values=colors)\n    + scale_fill_manual(values=colors)\n    + labs(\n        x=\"Training Set Size\",\n        y=\"Accuracy Score\",\n        title=\"learning-curve-basic · plotnine · anyplot.ai\",\n        color=\"\",\n        fill=\"\",\n    )\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", dpi=300, width=16, height=9)\n"}