{"spec_id":"sparkline-basic","library":"altair","language":"python","code":"\"\"\"anyplot.ai\nsparkline-basic: Basic Sparkline\nLibrary: altair 6.0.0 | Python 3.13.11\nQuality: 78/100 | Updated: 2026-05-03\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\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\",\n             \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data - 75 consecutive business days: a product's launch-to-maturity user trend\n# with viral spike, long-tail stabilization, and recovery — a classic adoption curve\nnp.random.seed(42)\nn_points = 75\nday = np.arange(n_points)\n\n# S-shaped adoption curve: slow start → acceleration → saturation\nadoption = 40 + 60 * (1 - np.exp(-day / 20)) ** 2\n# Viral launch-week spike\nspike = 18 * np.exp(-((day - 25) ** 2) / 8)\n# Weekly engagement pattern\nweekly = 3 * np.sin(2 * np.pi * day / 5)\nnoise = np.random.randn(n_points) * 1.5\nusers = adoption + spike + weekly + noise\n\ndf = pd.DataFrame({\"day\": day, \"users\": users})\n\n# Min/max highlights\nmin_idx = df[\"users\"].idxmin()\nmax_idx = df[\"users\"].idxmax()\nhighlights = df.iloc[[min_idx, max_idx]].copy()\nhighlights[\"type\"] = [\"min\", \"max\"]\n\n# Semi-transparent area fill — Altair's layered chart pattern\nfill = (\n    alt.Chart(df)\n    .mark_area(color=IMPRINT[0], opacity=0.15)\n    .encode(\n        x=alt.X(\"day:Q\", axis=None),\n        y=alt.Y(\"users:Q\", axis=None, scale=alt.Scale(zero=False)),\n    )\n)\n\n# Main line\nline = (\n    alt.Chart(df)\n    .mark_line(color=IMPRINT[0], strokeWidth=3)\n    .encode(\n        x=alt.X(\"day:Q\", axis=None),\n        y=alt.Y(\"users:Q\", axis=None, scale=alt.Scale(zero=False)),\n    )\n)\n\n# Highlight min (orange) and max (violet)\npoints = (\n    alt.Chart(highlights)\n    .mark_circle(size=150)\n    .encode(\n        x=alt.X(\"day:Q\", axis=None),\n        y=alt.Y(\"users:Q\", axis=None, scale=alt.Scale(zero=False)),\n        color=alt.Color(\n            \"type:N\",\n            scale=alt.Scale(domain=[\"min\", \"max\"],\n                            range=[IMPRINT[1], IMPRINT[3]]),\n            legend=None,\n        ),\n    )\n)\n\n# Combine layers\nchart = (\n    (fill + line + points)\n    .properties(\n        width=1600,\n        height=320,  # ~5:1 aspect ratio\n        background=PAGE_BG,\n        title=alt.Title(\"sparkline-basic · altair · anyplot.ai\",\n                        fontSize=28, color=INK),\n    )\n    .configure_axis(domainColor=INK_SOFT, tickColor=INK_SOFT,\n                    labelColor=INK_SOFT, titleColor=INK)\n    .configure_view(strokeWidth=0)\n)\n\n# Save as PNG and HTML\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}