{"spec_id":"line-annotated-events","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nline-annotated-events: Annotated Line Plot with Event Markers\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 89/100 | Updated: 2026-05-16\n\"\"\"\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Data - Daily product metrics with feature launch events\nnp.random.seed(42)\ndates = pd.date_range(\"2024-01-01\", periods=365, freq=\"D\")\n\n# Create realistic user growth pattern with trend and seasonality\ntrend = np.linspace(1000, 5000, 365)\nseasonality = 300 * np.sin(np.arange(365) * 2 * np.pi / 30)\nnoise = np.random.normal(0, 150, 365)\ndaily_users = trend + seasonality + noise\n\n# Create jumps at event dates to show impact\ndaily_users[45:] += 400  # After Feature A launch\ndaily_users[120:] += 600  # After Feature B launch\ndaily_users[200:] += 800  # After Mobile App launch\ndaily_users[280:] += 500  # After API release\ndaily_users[330:] += 300  # After Integration launch\n\ndf = pd.DataFrame({\"date\": dates, \"users\": daily_users})\n\n# Convert date to numeric for plotting\ndf[\"date_num\"] = (df[\"date\"] - df[\"date\"].min()).dt.days\n\n# Event data - Feature launches throughout the year\nevents = pd.DataFrame(\n    {\n        \"event_date\": pd.to_datetime([\"2024-02-15\", \"2024-05-01\", \"2024-07-20\", \"2024-10-07\", \"2024-11-20\"]),\n        \"event_label\": [\"Feature A\", \"Feature B\", \"Mobile App\", \"API v2.0\", \"Partners\"],\n        \"y_offset\": [4800, 5200, 5600, 6000, 6400],  # Alternating heights to avoid overlap\n    }\n)\nevents[\"event_num\"] = (events[\"event_date\"] - df[\"date\"].min()).dt.days\n\n# Create the plot\nplot = (\n    ggplot()\n    # Main line - daily active users\n    + geom_line(aes(x=\"date_num\", y=\"users\"), data=df, color=\"#306998\", size=1.5, alpha=0.9)\n    # Vertical lines for events\n    + geom_vline(aes(xintercept=\"event_num\"), data=events, color=\"#DC2626\", linetype=\"dashed\", size=1.0, alpha=0.7)\n    # Event markers at the line\n    + geom_point(aes(x=\"event_num\", y=\"y_offset\"), data=events, color=\"#DC2626\", size=5, shape=18)\n    # Event labels\n    + geom_text(\n        aes(x=\"event_num\", y=\"y_offset\", label=\"event_label\"),\n        data=events,\n        color=\"#333333\",\n        size=14,\n        hjust=0,\n        nudge_x=5,\n        fontface=\"bold\",\n    )\n    # Labels and title\n    + labs(x=\"Day of Year 2024\", y=\"Daily Active Users\", title=\"line-annotated-events · letsplot · pyplots.ai\")\n    # Styling\n    + theme_minimal()\n    + theme(\n        plot_title=element_text(size=24, face=\"bold\"),\n        axis_title=element_text(size=20),\n        axis_text=element_text(size=16),\n        panel_grid_major=element_line(color=\"#CCCCCC\", size=0.5),\n        panel_grid_minor=element_blank(),\n    )\n    # Set axis limits to show all data and labels\n    + scale_x_continuous(\n        breaks=[0, 60, 120, 180, 240, 300, 360], labels=[\"Jan\", \"Mar\", \"May\", \"Jul\", \"Sep\", \"Nov\", \"Jan\"]\n    )\n    + scale_y_continuous(limits=[0, 7500])\n    # Figure size (scaled 3x on export = 4800 × 2700 px)\n    + ggsize(1600, 900)\n)\n\n# Save as PNG and HTML\nggsave(plot, \"plot.png\", scale=3)\nggsave(plot, \"plot.html\")\n"}