Skip to content

Commit a26cad7

Browse files
feat(plotnine): implement errorbar-basic (#5387)
## Implementation: `errorbar-basic` - python/plotnine Implements the **python/plotnine** version of `errorbar-basic`. **File:** `plots/errorbar-basic/implementations/python/plotnine.py` **Parent Issue:** #973 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24927208728)* --------- 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 ac22f68 commit a26cad7

2 files changed

Lines changed: 268 additions & 144 deletions

File tree

Lines changed: 101 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,122 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
errorbar-basic: Basic Error Bar Plot
3-
Library: plotnine 0.15.2 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: plotnine 0.15.3 | Python 3.14.4
4+
Quality: 85/100 | Updated: 2026-04-25
55
"""
66

7+
import os
8+
79
import pandas as pd
8-
from plotnine import aes, element_text, geom_errorbar, geom_point, ggplot, labs, theme, theme_minimal
10+
from plotnine import (
11+
aes,
12+
element_blank,
13+
element_line,
14+
element_rect,
15+
element_text,
16+
geom_errorbar,
17+
geom_point,
18+
ggplot,
19+
labs,
20+
position_dodge,
21+
scale_color_manual,
22+
theme,
23+
theme_minimal,
24+
)
25+
926

27+
THEME = os.getenv("ANYPLOT_THEME", "light")
28+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
29+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
30+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
31+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
1032

11-
# Data - Lab measurements with measurement uncertainty
33+
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"]
34+
35+
# Data — three lab methods measured across six samples
1236
data = pd.DataFrame(
1337
{
14-
"experiment": ["Sample A", "Sample B", "Sample C", "Sample D", "Sample E", "Sample F"],
15-
"measurement": [42.5, 38.2, 55.1, 47.8, 33.6, 51.3],
16-
"error": [3.2, 4.1, 2.8, 5.5, 3.8, 4.2],
38+
"sample": [
39+
"Sample A",
40+
"Sample B",
41+
"Sample C",
42+
"Sample D",
43+
"Sample E",
44+
"Sample F",
45+
"Sample A",
46+
"Sample B",
47+
"Sample C",
48+
"Sample D",
49+
"Sample E",
50+
"Sample F",
51+
"Sample A",
52+
"Sample B",
53+
"Sample C",
54+
"Sample D",
55+
"Sample E",
56+
"Sample F",
57+
],
58+
"method": (["Method A"] * 6 + ["Method B"] * 6 + ["Method C"] * 6),
59+
"measurement": [
60+
42.5,
61+
38.2,
62+
55.1,
63+
47.8,
64+
33.6,
65+
51.3,
66+
44.8,
67+
40.1,
68+
53.6,
69+
49.2,
70+
35.9,
71+
52.7,
72+
41.2,
73+
39.5,
74+
56.3,
75+
46.4,
76+
34.8,
77+
50.1,
78+
],
79+
"error": [3.2, 4.1, 2.8, 5.5, 3.8, 4.2, 2.6, 3.4, 3.1, 4.2, 3.0, 3.6, 3.9, 4.5, 2.5, 5.1, 4.3, 4.0],
1780
}
1881
)
1982

20-
# Calculate error bar positions
2183
data["ymin"] = data["measurement"] - data["error"]
2284
data["ymax"] = data["measurement"] + data["error"]
2385

24-
# Plot
86+
# Order samples by mean measurement to create visual hierarchy
87+
sample_order = data.groupby("sample")["measurement"].mean().sort_values().index.tolist()
88+
data["sample"] = pd.Categorical(data["sample"], categories=sample_order, ordered=True)
89+
90+
dodge = position_dodge(width=0.55)
91+
2592
plot = (
26-
ggplot(data, aes(x="experiment", y="measurement"))
27-
+ geom_errorbar(aes(ymin="ymin", ymax="ymax"), width=0.3, size=1.5, color="#306998")
28-
+ geom_point(size=6, color="#306998")
29-
+ labs(x="Experiment", y="Measurement Value", title="errorbar-basic · plotnine · pyplots.ai")
93+
ggplot(data, aes(x="sample", y="measurement", color="method", group="method"))
94+
+ geom_errorbar(aes(ymin="ymin", ymax="ymax"), width=0.35, size=1.4, position=dodge)
95+
+ geom_point(size=5, position=dodge)
96+
+ scale_color_manual(values=OKABE_ITO, name="Method")
97+
+ labs(x="Experimental Sample", y="Measurement Value (mg/L)", title="errorbar-basic · plotnine · anyplot.ai")
3098
+ theme_minimal()
3199
+ theme(
32100
figure_size=(16, 9),
33-
text=element_text(size=14),
34-
axis_title=element_text(size=20),
35-
axis_text=element_text(size=16),
36-
plot_title=element_text(size=24),
37-
axis_text_x=element_text(size=16),
101+
text=element_text(size=14, color=INK_SOFT),
102+
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
103+
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
104+
panel_border=element_blank(),
105+
panel_grid_major_x=element_blank(),
106+
panel_grid_minor=element_blank(),
107+
panel_grid_major_y=element_line(color=INK, size=0.3, alpha=0.10),
108+
axis_line_x=element_line(color=INK_SOFT, size=0.6),
109+
axis_line_y=element_blank(),
110+
axis_ticks=element_blank(),
111+
axis_title=element_text(size=20, color=INK),
112+
axis_text=element_text(size=16, color=INK_SOFT),
113+
plot_title=element_text(size=24, color=INK, weight="bold"),
114+
legend_background=element_rect(fill=ELEVATED_BG, color=ELEVATED_BG),
115+
legend_key=element_rect(fill=PAGE_BG, color=PAGE_BG),
116+
legend_text=element_text(size=16, color=INK_SOFT),
117+
legend_title=element_text(size=16, color=INK),
118+
legend_position="right",
38119
)
39120
)
40121

41-
# Save
42-
plot.save("plot.png", dpi=300)
122+
plot.save(f"plot-{THEME}.png", dpi=300, width=16, height=9, verbose=False)

0 commit comments

Comments
 (0)