Skip to content

Commit 07dbba3

Browse files
feat(letsplot): implement line-annotated-events (#3046)
## Implementation: `line-annotated-events` - letsplot Implements the **letsplot** version of `line-annotated-events`. **File:** `plots/line-annotated-events/implementations/letsplot.py` **Parent Issue:** #2997 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20617493630)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 5568d9f commit 07dbba3

2 files changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
""" pyplots.ai
2+
line-annotated-events: Annotated Line Plot with Event Markers
3+
Library: letsplot 4.8.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import * # noqa: F403
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Data - Daily product metrics with feature launch events
15+
np.random.seed(42)
16+
dates = pd.date_range("2024-01-01", periods=365, freq="D")
17+
18+
# Create realistic user growth pattern with trend and seasonality
19+
trend = np.linspace(1000, 5000, 365)
20+
seasonality = 300 * np.sin(np.arange(365) * 2 * np.pi / 30)
21+
noise = np.random.normal(0, 150, 365)
22+
daily_users = trend + seasonality + noise
23+
24+
# Create jumps at event dates to show impact
25+
daily_users[45:] += 400 # After Feature A launch
26+
daily_users[120:] += 600 # After Feature B launch
27+
daily_users[200:] += 800 # After Mobile App launch
28+
daily_users[280:] += 500 # After API release
29+
daily_users[330:] += 300 # After Integration launch
30+
31+
df = pd.DataFrame({"date": dates, "users": daily_users})
32+
33+
# Convert date to numeric for plotting
34+
df["date_num"] = (df["date"] - df["date"].min()).dt.days
35+
36+
# Event data - Feature launches throughout the year
37+
events = pd.DataFrame(
38+
{
39+
"event_date": pd.to_datetime(["2024-02-15", "2024-05-01", "2024-07-20", "2024-10-07", "2024-11-20"]),
40+
"event_label": ["Feature A", "Feature B", "Mobile App", "API v2.0", "Partners"],
41+
"y_offset": [4800, 5200, 5600, 6000, 6400], # Alternating heights to avoid overlap
42+
}
43+
)
44+
events["event_num"] = (events["event_date"] - df["date"].min()).dt.days
45+
46+
# Create the plot
47+
plot = (
48+
ggplot()
49+
# Main line - daily active users
50+
+ geom_line(aes(x="date_num", y="users"), data=df, color="#306998", size=1.5, alpha=0.9)
51+
# Vertical lines for events
52+
+ geom_vline(aes(xintercept="event_num"), data=events, color="#DC2626", linetype="dashed", size=1.0, alpha=0.7)
53+
# Event markers at the line
54+
+ geom_point(aes(x="event_num", y="y_offset"), data=events, color="#DC2626", size=5, shape=18)
55+
# Event labels
56+
+ geom_text(
57+
aes(x="event_num", y="y_offset", label="event_label"),
58+
data=events,
59+
color="#333333",
60+
size=14,
61+
hjust=0,
62+
nudge_x=5,
63+
fontface="bold",
64+
)
65+
# Labels and title
66+
+ labs(x="Day of Year 2024", y="Daily Active Users", title="line-annotated-events · letsplot · pyplots.ai")
67+
# Styling
68+
+ theme_minimal()
69+
+ theme(
70+
plot_title=element_text(size=24, face="bold"),
71+
axis_title=element_text(size=20),
72+
axis_text=element_text(size=16),
73+
panel_grid_major=element_line(color="#CCCCCC", size=0.5),
74+
panel_grid_minor=element_blank(),
75+
)
76+
# Set axis limits to show all data and labels
77+
+ scale_x_continuous(
78+
breaks=[0, 60, 120, 180, 240, 300, 360], labels=["Jan", "Mar", "May", "Jul", "Sep", "Nov", "Jan"]
79+
)
80+
+ scale_y_continuous(limits=[0, 7500])
81+
# Figure size (scaled 3x on export = 4800 × 2700 px)
82+
+ ggsize(1600, 900)
83+
)
84+
85+
# Save as PNG and HTML
86+
ggsave(plot, "plot.png", scale=3)
87+
ggsave(plot, "plot.html")
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
library: letsplot
2+
specification_id: line-annotated-events
3+
created: '2025-12-31T11:00:44Z'
4+
updated: '2025-12-31T11:53:32Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20617493630
7+
issue: 2997
8+
python_version: 3.13.11
9+
library_version: 4.8.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-annotated-events/letsplot/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-annotated-events/letsplot/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/line-annotated-events/letsplot/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent use of alternating y_offset heights for event labels prevents overlap
17+
- Clean ggplot2 grammar implementation with proper layering of geoms
18+
- Good color contrast between data line (blue) and event markers (red)
19+
- Appropriate figure sizing with scale=3 for high-resolution output
20+
- Data shows realistic growth pattern with trend, seasonality, and event impacts
21+
weaknesses:
22+
- X-axis label says Day of Year 2024 but displays month names - should be consistent

0 commit comments

Comments
 (0)