Skip to content

Commit 2367fd1

Browse files
feat(plotnine): implement line-annotated-events (#3014)
## Implementation: `line-annotated-events` - plotnine Implements the **plotnine** version of `line-annotated-events`. **File:** `plots/line-annotated-events/implementations/plotnine.py` **Parent Issue:** #2997 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20617491526)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent dfafeca commit 2367fd1

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
""" pyplots.ai
2+
line-annotated-events: Annotated Line Plot with Event Markers
3+
Library: plotnine 0.15.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 plotnine import (
10+
aes,
11+
element_line,
12+
element_text,
13+
geom_line,
14+
geom_point,
15+
geom_text,
16+
geom_vline,
17+
ggplot,
18+
labs,
19+
scale_x_datetime,
20+
theme,
21+
theme_minimal,
22+
)
23+
24+
25+
# Data - Simulated website traffic with product release events
26+
np.random.seed(42)
27+
28+
# Generate daily data for one year
29+
dates = pd.date_range("2024-01-01", periods=365, freq="D")
30+
base = 50000 + np.cumsum(np.random.randn(365) * 500)
31+
seasonal = 5000 * np.sin(2 * np.pi * np.arange(365) / 365)
32+
weekly = 3000 * np.sin(2 * np.pi * np.arange(365) / 7)
33+
values = base + seasonal + weekly
34+
35+
df = pd.DataFrame({"date": dates, "value": values})
36+
37+
# Event markers - product releases and major updates
38+
events = pd.DataFrame(
39+
{
40+
"event_date": pd.to_datetime(["2024-02-15", "2024-05-01", "2024-07-20", "2024-10-10", "2024-12-01"]),
41+
"event_label": ["v2.0 Release", "Mobile App Launch", "API Update", "Enterprise Tier", "Holiday Campaign"],
42+
"y_offset": [0.92, 0.85, 0.92, 0.85, 0.92], # Alternating heights to avoid overlap
43+
}
44+
)
45+
46+
# Calculate y positions for labels (as fraction of y range)
47+
y_min, y_max = df["value"].min(), df["value"].max()
48+
y_range = y_max - y_min
49+
events["y_pos"] = y_min + events["y_offset"] * y_range
50+
51+
# Create the plot
52+
plot = (
53+
ggplot(df, aes(x="date", y="value"))
54+
+ geom_line(color="#306998", size=1.2, alpha=0.9)
55+
+ geom_vline(aes(xintercept="event_date"), data=events, color="#FFD43B", linetype="dashed", size=1.0, alpha=0.8)
56+
+ geom_point(aes(x="event_date", y="y_pos"), data=events, color="#FFD43B", size=4, shape="D")
57+
+ geom_text(
58+
aes(x="event_date", y="y_pos", label="event_label"),
59+
data=events,
60+
color="#333333",
61+
size=10,
62+
ha="center",
63+
va="bottom",
64+
nudge_y=1000,
65+
fontweight="bold",
66+
)
67+
+ labs(x="Date", y="Daily Visitors", title="line-annotated-events · plotnine · pyplots.ai")
68+
+ scale_x_datetime(date_breaks="2 months", date_labels="%b %Y")
69+
+ theme_minimal()
70+
+ theme(
71+
figure_size=(16, 9),
72+
text=element_text(size=14),
73+
axis_title=element_text(size=20),
74+
axis_text=element_text(size=16),
75+
axis_text_x=element_text(angle=45, ha="right"),
76+
plot_title=element_text(size=24, weight="bold"),
77+
panel_grid_major=element_line(color="#cccccc", size=0.3, alpha=0.3),
78+
panel_grid_minor=element_line(color="#eeeeee", size=0.2, alpha=0.2),
79+
)
80+
)
81+
82+
# Save
83+
plot.save("plot.png", dpi=300, verbose=False)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: plotnine
2+
specification_id: line-annotated-events
3+
created: '2025-12-31T10:55:04Z'
4+
updated: '2025-12-31T11:00:13Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20617491526
7+
issue: 2997
8+
python_version: 3.13.11
9+
library_version: 0.15.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-annotated-events/plotnine/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-annotated-events/plotnine/plot_thumb.png
12+
preview_html: null
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of alternating y-positions for event labels to avoid
17+
overlap
18+
- Clean grammar of graphics approach with well-layered geoms (line → vline → point
19+
→ text)
20+
- 'Appropriate use of the Python color scheme (#306998 blue, #FFD43B yellow)'
21+
- Good text sizing following the library guidelines (24pt title, 20pt axis labels,
22+
16pt ticks)
23+
- Realistic website traffic scenario with meaningful event annotations
24+
weaknesses:
25+
- Axis labels could include units (e.g., "Daily Visitors (count)")
26+
- Event markers at alternating heights work well but the fixed y_offset values (0.85,
27+
0.92) could be slightly higher to give more separation from the data line peaks

0 commit comments

Comments
 (0)