Skip to content

Commit b5248b1

Browse files
feat(plotnine): implement ecdf-basic (#9489)
## Implementation: `ecdf-basic` - python/plotnine Implements the **python/plotnine** version of `ecdf-basic`. **File:** `plots/ecdf-basic/implementations/python/plotnine.py` **Parent Issue:** #976 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/28160686084)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent c729120 commit b5248b1

2 files changed

Lines changed: 145 additions & 99 deletions

File tree

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,101 @@
11
""" anyplot.ai
22
ecdf-basic: Basic ECDF Plot
3-
Library: plotnine 0.15.3 | Python 3.14.4
4-
Quality: 91/100 | Updated: 2026-04-24
3+
Library: plotnine 0.15.7 | Python 3.13.14
4+
Quality: 89/100 | Updated: 2026-06-25
55
"""
66

77
import os
8+
import sys
89

910
import numpy as np
1011
import pandas as pd
11-
from plotnine import (
12+
13+
14+
# Avoid shadowing the plotnine library when this file is run directly
15+
_cwd = os.getcwd()
16+
sys.path = [p for p in sys.path if os.path.abspath(p) != _cwd]
17+
18+
from plotnine import ( # noqa: E402
1219
aes,
20+
annotate,
1321
element_blank,
1422
element_line,
1523
element_rect,
1624
element_text,
1725
geom_hline,
18-
geom_step,
1926
geom_vline,
2027
ggplot,
2128
ggsave,
2229
labs,
2330
scale_x_continuous,
2431
scale_y_continuous,
32+
stat_ecdf,
2533
theme,
2634
theme_minimal,
2735
)
2836

2937

30-
# Theme
38+
# Theme tokens (Imprint palette)
3139
THEME = os.getenv("ANYPLOT_THEME", "light")
3240
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
3341
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
3442
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
3543
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
36-
BRAND = "#009E73"
44+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
45+
BRAND = "#009E73" # Imprint palette position 1
3746

38-
# Data: test scores — normal distribution centered at 50
47+
# Data: test scores — normal distribution
3948
np.random.seed(42)
4049
scores = np.random.randn(200) * 15 + 50
50+
q1 = float(np.percentile(scores, 25))
51+
median_score = float(np.median(scores))
52+
q3 = float(np.percentile(scores, 75))
4153

42-
# Compute ECDF
43-
sorted_scores = np.sort(scores)
44-
ecdf_y = np.arange(1, len(sorted_scores) + 1) / len(sorted_scores)
45-
median_score = np.median(sorted_scores)
54+
df = pd.DataFrame({"score": scores})
4655

47-
df = pd.DataFrame({"score": sorted_scores, "ecdf": ecdf_y})
56+
title = "ecdf-basic · python · plotnine · anyplot.ai"
4857

49-
# Plot
58+
# Plot — stat_ecdf computes ECDF internally via plotnine's stat layer
5059
plot = (
51-
ggplot(df, aes(x="score", y="ecdf"))
52-
+ geom_hline(yintercept=0.5, color=INK_SOFT, size=0.6, linetype="dotted", alpha=0.7)
53-
+ geom_vline(xintercept=median_score, color=INK_SOFT, size=0.6, linetype="dotted", alpha=0.7)
54-
+ geom_step(color=BRAND, size=2)
55-
+ labs(x="Test Score (points)", y="Cumulative Proportion", title="ecdf-basic · plotnine · anyplot.ai")
60+
ggplot(df, aes(x="score"))
61+
# IQR shaded band — highlights the interquartile range (middle 50%)
62+
+ annotate("rect", xmin=q1, xmax=q3, ymin=0.0, ymax=1.0, fill=BRAND, alpha=0.06)
63+
# Quartile reference crosshairs (Q1, median, Q3)
64+
+ geom_hline(yintercept=0.25, color=INK_SOFT, size=0.5, linetype="dotted", alpha=0.55)
65+
+ geom_hline(yintercept=0.50, color=INK_SOFT, size=0.6, linetype="dotted", alpha=0.70)
66+
+ geom_hline(yintercept=0.75, color=INK_SOFT, size=0.5, linetype="dotted", alpha=0.55)
67+
+ geom_vline(xintercept=q1, color=INK_SOFT, size=0.5, linetype="dotted", alpha=0.55)
68+
+ geom_vline(xintercept=median_score, color=INK_SOFT, size=0.6, linetype="dotted", alpha=0.70)
69+
+ geom_vline(xintercept=q3, color=INK_SOFT, size=0.5, linetype="dotted", alpha=0.55)
70+
# ECDF step line — rendered on top of reference elements
71+
+ stat_ecdf(geom="step", color=BRAND, size=1.1)
72+
# Quartile annotations — teach readers how to extract percentile information
73+
+ annotate("text", x=q1 + 1.5, y=0.16, label=f"Q1: {q1:.1f}", color=INK_SOFT, size=3.5, ha="left")
74+
+ annotate(
75+
"text", x=median_score + 1.5, y=0.08, label=f"Median: {median_score:.1f}", color=INK_SOFT, size=4.0, ha="left"
76+
)
77+
+ annotate("text", x=q3 + 1.5, y=0.80, label=f"Q3: {q3:.1f}", color=INK_SOFT, size=3.5, ha="left")
78+
+ labs(x="Test Score (points)", y="Cumulative Proportion", title=title)
5679
+ scale_x_continuous(expand=(0.01, 0))
5780
+ scale_y_continuous(limits=(0, 1), breaks=np.arange(0, 1.1, 0.1), expand=(0.01, 0))
5881
+ theme_minimal()
5982
+ theme(
60-
figure_size=(16, 9),
83+
figure_size=(8, 4.5),
6184
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
6285
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
6386
panel_border=element_blank(),
64-
panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),
87+
panel_grid_major=element_line(color=INK, size=0.3, alpha=0.15),
6588
panel_grid_minor=element_blank(),
6689
axis_line=element_line(color=INK_SOFT, size=0.6),
6790
axis_ticks=element_line(color=INK_SOFT, size=0.5),
68-
text=element_text(color=INK, size=14),
69-
plot_title=element_text(color=INK, size=24, weight="medium", ha="left"),
70-
axis_title=element_text(color=INK, size=20),
71-
axis_text=element_text(color=INK_SOFT, size=16),
91+
text=element_text(color=INK, size=7),
92+
plot_title=element_text(color=INK, size=12, weight="medium", ha="left"),
93+
axis_title=element_text(color=INK, size=10),
94+
axis_text=element_text(color=INK_SOFT, size=8),
7295
legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
73-
legend_text=element_text(color=INK_SOFT, size=16),
74-
legend_title=element_text(color=INK, size=16),
96+
legend_text=element_text(color=INK_SOFT, size=8),
97+
legend_title=element_text(color=INK, size=8),
7598
)
7699
)
77100

78-
ggsave(plot, filename=f"plot-{THEME}.png", dpi=300, width=16, height=9)
101+
ggsave(plot, filename=f"plot-{THEME}.png", dpi=400, width=8, height=4.5)

0 commit comments

Comments
 (0)