|
1 | 1 | """ anyplot.ai |
2 | 2 | errorbar-basic: Basic Error Bar Plot |
3 | | -Library: plotnine 0.15.3 | Python 3.14.4 |
4 | | -Quality: 85/100 | Updated: 2026-04-25 |
| 3 | +Library: plotnine 0.15.7 | Python 3.13.14 |
| 4 | +Quality: 84/100 | Updated: 2026-06-30 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import os |
|
13 | 13 | element_line, |
14 | 14 | element_rect, |
15 | 15 | element_text, |
16 | | - geom_errorbar, |
17 | | - geom_point, |
| 16 | + geom_crossbar, |
18 | 17 | ggplot, |
19 | 18 | labs, |
20 | 19 | position_dodge, |
21 | 20 | scale_color_manual, |
| 21 | + scale_fill_manual, |
| 22 | + scale_x_discrete, |
22 | 23 | theme, |
23 | 24 | theme_minimal, |
24 | 25 | ) |
|
29 | 30 | ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
30 | 31 | INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
31 | 32 | INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 33 | +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" |
32 | 34 |
|
33 | 35 | IMPRINT = ["#009E73", "#C475FD", "#4467A3"] |
34 | 36 |
|
35 | | -# Data — three lab methods measured across six samples |
| 37 | +# Data — three analytical methods measuring nitrate concentration across six sampling sites |
36 | 38 | data = pd.DataFrame( |
37 | 39 | { |
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, |
| 40 | + "site": ["Site A", "Site B", "Site C", "Site D", "Site E", "Site F"] * 3, |
| 41 | + "method": ["Standard"] * 6 + ["Enhanced"] * 6 + ["Automated"] * 6, |
| 42 | + "concentration": [ |
| 43 | + # Standard: baseline sensitivity, tight precision |
| 44 | + 36.2, |
| 45 | + 39.8, |
| 46 | + 42.1, |
| 47 | + 37.5, |
| 48 | + 43.6, |
| 49 | + 40.4, |
| 50 | + # Enhanced: ~35% higher detection |
| 51 | + 48.5, |
| 52 | + 52.3, |
| 53 | + 55.7, |
69 | 54 | 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, |
| 55 | + 56.1, |
| 56 | + 51.8, |
| 57 | + # Automated: highest values, greatest variability |
| 58 | + 61.4, |
| 59 | + 65.8, |
| 60 | + 69.2, |
| 61 | + 58.6, |
| 62 | + 67.3, |
| 63 | + 63.5, |
| 64 | + ], |
| 65 | + "se": [ |
| 66 | + 1.8, |
| 67 | + 2.1, |
| 68 | + 1.6, |
| 69 | + 2.3, |
| 70 | + 1.9, |
| 71 | + 2.0, # Standard: SE ±1.6–2.3 |
| 72 | + 3.2, |
| 73 | + 2.8, |
| 74 | + 3.5, |
| 75 | + 3.0, |
| 76 | + 2.6, |
| 77 | + 3.4, # Enhanced: SE ±2.6–3.5 |
| 78 | + 5.1, |
| 79 | + 6.3, |
| 80 | + 4.8, |
| 81 | + 7.2, |
| 82 | + 5.5, |
| 83 | + 6.0, # Automated: SE ±4.8–7.2 |
78 | 84 | ], |
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], |
80 | 85 | } |
81 | 86 | ) |
82 | 87 |
|
83 | | -data["ymin"] = data["measurement"] - data["error"] |
84 | | -data["ymax"] = data["measurement"] + data["error"] |
| 88 | +data["ymin"] = data["concentration"] - data["se"] |
| 89 | +data["ymax"] = data["concentration"] + data["se"] |
| 90 | + |
| 91 | +# Sort sites by Standard-method concentration so all three methods trend upward left→right |
| 92 | +site_order = data[data["method"] == "Standard"].set_index("site")["concentration"].sort_values().index.tolist() |
85 | 93 |
|
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) |
| 94 | +dodge = position_dodge(width=0.6) |
89 | 95 |
|
90 | | -dodge = position_dodge(width=0.55) |
| 96 | +title = "errorbar-basic · python · plotnine · anyplot.ai" |
| 97 | +subtitle = "Automated sampling shows 3–4× wider uncertainty than Standard across all sites" |
91 | 98 |
|
92 | 99 | plot = ( |
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) |
| 100 | + ggplot(data, aes(x="site", y="concentration", color="method", fill="method", group="method")) |
| 101 | + + geom_crossbar(aes(ymin="ymin", ymax="ymax"), width=0.18, fatten=3, size=0.9, alpha=0.2, position=dodge) |
96 | 102 | + scale_color_manual(values=IMPRINT, name="Method") |
97 | | - + labs(x="Experimental Sample", y="Measurement Value (mg/L)", title="errorbar-basic · plotnine · anyplot.ai") |
| 103 | + + scale_fill_manual(values=IMPRINT, name="Method") |
| 104 | + + scale_x_discrete(limits=site_order) |
| 105 | + + labs(x="Sampling Site", y="Nitrate Concentration (mg/L)", title=title, subtitle=subtitle) |
98 | 106 | + theme_minimal() |
99 | 107 | + theme( |
100 | | - figure_size=(16, 9), |
101 | | - text=element_text(size=14, color=INK_SOFT), |
| 108 | + figure_size=(8, 4.5), |
| 109 | + text=element_text(size=7, color=INK_SOFT), |
102 | 110 | plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
103 | 111 | panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
104 | 112 | panel_border=element_blank(), |
|
108 | 116 | axis_line_x=element_line(color=INK_SOFT, size=0.6), |
109 | 117 | axis_line_y=element_blank(), |
110 | 118 | 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), |
| 119 | + axis_title=element_text(size=10, color=INK), |
| 120 | + axis_text=element_text(size=8, color=INK_SOFT), |
| 121 | + plot_title=element_text(size=12, color=INK, weight="bold"), |
| 122 | + plot_subtitle=element_text(size=9, color=INK_MUTED), |
| 123 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT, size=0.4), |
115 | 124 | 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), |
| 125 | + legend_text=element_text(size=8, color=INK_SOFT), |
| 126 | + legend_title=element_text(size=8, color=INK), |
118 | 127 | legend_position="right", |
119 | 128 | ) |
120 | 129 | ) |
121 | 130 |
|
122 | | -plot.save(f"plot-{THEME}.png", dpi=300, width=16, height=9, verbose=False) |
| 131 | +plot.save(f"plot-{THEME}.png", dpi=400, width=8, height=4.5, units="in", verbose=False) |
0 commit comments