{"spec_id":"line-annotated-events","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nline-annotated-events: Annotated Line Plot with Event Markers\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-16\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_point,\n    geom_text,\n    geom_vline,\n    ggplot,\n    labs,\n    scale_x_datetime,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme-adaptive colors\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 - Simulated website traffic with product release events\nnp.random.seed(42)\n\n# Generate daily data for one year\ndates = pd.date_range(\"2024-01-01\", periods=365, freq=\"D\")\nbase = 50000 + np.cumsum(np.random.randn(365) * 500)\nseasonal = 5000 * np.sin(2 * np.pi * np.arange(365) / 365)\nweekly = 3000 * np.sin(2 * np.pi * np.arange(365) / 7)\nvalues = base + seasonal + weekly\n\ndf = pd.DataFrame({\"date\": dates, \"value\": values})\n\n# Event markers - product releases and major updates\nevents = pd.DataFrame(\n    {\n        \"event_date\": pd.to_datetime([\"2024-02-15\", \"2024-05-01\", \"2024-07-20\", \"2024-10-10\", \"2024-12-01\"]),\n        \"event_label\": [\"v2.0 Release\", \"Mobile App Launch\", \"API Update\", \"Enterprise Tier\", \"Holiday Campaign\"],\n        \"y_offset\": [0.92, 0.85, 0.92, 0.85, 0.92],  # Alternating heights to avoid overlap\n    }\n)\n\n# Calculate y positions for labels (as fraction of y range)\ny_min, y_max = df[\"value\"].min(), df[\"value\"].max()\ny_range = y_max - y_min\nevents[\"y_pos\"] = y_min + events[\"y_offset\"] * y_range\n\n# Create the plot\nplot = (\n    ggplot(df, aes(x=\"date\", y=\"value\"))\n    + geom_line(color=\"#009E73\", size=1.2, alpha=0.9)\n    + geom_vline(aes(xintercept=\"event_date\"), data=events, color=\"#C475FD\", linetype=\"dashed\", size=1.0, alpha=0.8)\n    + geom_point(aes(x=\"event_date\", y=\"y_pos\"), data=events, color=\"#C475FD\", size=4, shape=\"D\")\n    + geom_text(\n        aes(x=\"event_date\", y=\"y_pos\", label=\"event_label\"),\n        data=events,\n        color=INK,\n        size=10,\n        ha=\"center\",\n        va=\"bottom\",\n        nudge_y=1000,\n        fontweight=\"bold\",\n    )\n    + labs(x=\"Date\", y=\"Daily Visitors (count)\", title=\"line-annotated-events · plotnine · anyplot.ai\")\n    + scale_x_datetime(date_breaks=\"2 months\", date_labels=\"%b %Y\")\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\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        text=element_text(size=14, color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_text_x=element_text(angle=45, ha=\"right\"),\n        axis_line=element_line(color=INK_SOFT),\n        plot_title=element_text(size=24, weight=\"bold\", color=INK),\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}