{"spec_id":"learning-curve-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nlearning-curve-basic: Model Learning Curve\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\nimport sys\n\n\nsaved_path = sys.path[:]\nsys.path = [p for p in sys.path if p not in (\"\", \".\", os.getcwd())]\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\nsys.path = saved_path\n\n# Theme setup\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\"  # Training (first series)\nACCENT = \"#C475FD\"  # Validation (second series)\n\n# Data - simulate learning curve for a classification model\nnp.random.seed(42)\n\n# Training set sizes (10 points from 100 to 1000 samples)\ntrain_sizes = np.linspace(100, 1000, 10).astype(int)\n\n# Simulate 5-fold cross-validation scores\nn_folds = 5\nn_sizes = len(train_sizes)\n\n# Training scores: start high, remain high (slight decrease variance with more data)\ntrain_scores = np.zeros((n_folds, n_sizes))\nfor i, size in enumerate(train_sizes):\n    base_score = 0.98 - 0.02 * (size / 1000)\n    noise = np.random.normal(0, 0.01, n_folds)\n    train_scores[:, i] = np.clip(base_score + noise, 0.85, 1.0)\n\n# Validation scores: start lower, improve with more data (learning curve shape)\nval_scores = np.zeros((n_folds, n_sizes))\nfor i, size in enumerate(train_sizes):\n    base_score = 0.70 + 0.18 * (1 - np.exp(-size / 400))\n    noise = np.random.normal(0, 0.025 * np.exp(-size / 500), n_folds)\n    val_scores[:, i] = np.clip(base_score + noise, 0.6, 0.95)\n\n# Compute means and standard deviations\ntrain_mean = train_scores.mean(axis=0)\ntrain_std = train_scores.std(axis=0)\nval_mean = val_scores.mean(axis=0)\nval_std = val_scores.std(axis=0)\n\n# Create DataFrame for Altair with enhanced tooltips\ndata = []\nfor i, size in enumerate(train_sizes):\n    data.append(\n        {\n            \"Training Set Size\": size,\n            \"Score\": train_mean[i],\n            \"Score_lower\": train_mean[i] - train_std[i],\n            \"Score_upper\": train_mean[i] + train_std[i],\n            \"Type\": \"Training\",\n            \"Score_display\": f\"{train_mean[i]:.3f}\",\n            \"Std\": f\"±{train_std[i]:.3f}\",\n        }\n    )\n    data.append(\n        {\n            \"Training Set Size\": size,\n            \"Score\": val_mean[i],\n            \"Score_lower\": val_mean[i] - val_std[i],\n            \"Score_upper\": val_mean[i] + val_std[i],\n            \"Type\": \"Validation\",\n            \"Score_display\": f\"{val_mean[i]:.3f}\",\n            \"Std\": f\"±{val_std[i]:.3f}\",\n        }\n    )\n\ndf = pd.DataFrame(data)\n\n# Define color scale using Okabe-Ito palette\ncolor_scale = alt.Scale(domain=[\"Training\", \"Validation\"], range=[BRAND, ACCENT])\n\n# Tighter Y-axis scale showing only relevant data range\ny_scale = alt.Scale(domain=[0.65, 1.0])\n\n# Create the confidence bands\nband = (\n    alt.Chart(df)\n    .mark_area(opacity=0.2, interpolate=\"monotone\")\n    .encode(\n        x=alt.X(\"Training Set Size:Q\", title=\"Training Set Size (samples)\"),\n        y=alt.Y(\"Score_lower:Q\", scale=y_scale),\n        y2=\"Score_upper:Q\",\n        color=alt.Color(\"Type:N\", scale=color_scale, legend=None),\n    )\n)\n\n# Create the lines with enhanced tooltips\nline = (\n    alt.Chart(df)\n    .mark_line(size=3, point=True, tension=0.3)\n    .encode(\n        x=alt.X(\"Training Set Size:Q\", title=\"Training Set Size (samples)\"),\n        y=alt.Y(\"Score:Q\", title=\"Accuracy Score\", scale=y_scale),\n        color=alt.Color(\"Type:N\", scale=color_scale),\n        tooltip=[\n            alt.Tooltip(\"Training Set Size:Q\", format=\",\"),\n            alt.Tooltip(\"Score:Q\", format=\".3f\", title=\"Score\"),\n            alt.Tooltip(\"Std\", title=\"Std Dev\"),\n            alt.Tooltip(\"Type:N\", title=\"Curve Type\"),\n        ],\n    )\n)\n\n# Points for better interactivity\npoints = (\n    alt.Chart(df)\n    .mark_point(size=100)\n    .encode(\n        x=alt.X(\"Training Set Size:Q\"),\n        y=alt.Y(\"Score:Q\", scale=y_scale),\n        color=alt.Color(\"Type:N\", scale=color_scale, legend=None),\n        tooltip=[\n            alt.Tooltip(\"Training Set Size:Q\", format=\",\", title=\"Size\"),\n            alt.Tooltip(\"Score:Q\", format=\".3f\", title=\"Score\"),\n            alt.Tooltip(\"Std\", title=\"Std Dev\"),\n            alt.Tooltip(\"Type:N\", title=\"Type\"),\n        ],\n    )\n)\n\n# Combine layers\nchart = (\n    alt.layer(band, line, points)\n    .properties(\n        width=1600,\n        height=900,\n        title=alt.Title(\"learning-curve-basic · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n        background=PAGE_BG,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, continuousWidth=1600, continuousHeight=900)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.1,\n        labelColor=INK_SOFT,\n        labelFontSize=18,\n        titleColor=INK,\n        titleFontSize=22,\n    )\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        labelFontSize=16,\n        titleColor=INK,\n        titleFontSize=18,\n        padding=10,\n        cornerRadius=5,\n    )\n    .interactive()\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}