Skip to content

Commit 37b5481

Browse files
feat(plotnine): implement facet-grid (#6520)
## Implementation: `facet-grid` - python/plotnine Implements the **python/plotnine** version of `facet-grid`. **File:** `plots/facet-grid/implementations/python/plotnine.py` **Parent Issue:** #2696 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25776601759)* --------- 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 323bc52 commit 37b5481

2 files changed

Lines changed: 241 additions & 188 deletions

File tree

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,104 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
facet-grid: Faceted Grid Plot
3-
Library: plotnine 0.15.2 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-30
3+
Library: plotnine 0.15.4 | Python 3.13.13
4+
Quality: 95/100 | Updated: 2026-05-13
55
"""
66

7-
import numpy as np
8-
import pandas as pd
9-
from plotnine import (
10-
aes,
11-
element_line,
12-
element_rect,
13-
element_text,
14-
facet_grid,
15-
geom_point,
16-
ggplot,
17-
labs,
18-
scale_color_brewer,
19-
theme,
20-
theme_minimal,
21-
)
7+
import os
8+
import sys
9+
10+
11+
# Handle import conflicts: remove the script directory from sys.path to prevent
12+
# shadowing of installed modules (matplotlib, plotnine, etc.)
13+
_script_dir = os.path.dirname(os.path.abspath(__file__))
14+
_original_path = sys.path.copy()
15+
sys.path = [p for p in sys.path if os.path.abspath(p) != _script_dir]
16+
17+
# Also remove any __pycache__ or .pyc files that might exist
18+
_mod_cache = {k: v for k, v in sys.modules.items() if k not in ("matplotlib", "plotnine")}
2219

20+
try:
21+
import numpy as np
22+
import pandas as pd
23+
from plotnine import (
24+
aes,
25+
element_line,
26+
element_rect,
27+
element_text,
28+
facet_grid,
29+
geom_point,
30+
ggplot,
31+
labs,
32+
scale_color_manual,
33+
theme,
34+
theme_minimal,
35+
)
36+
finally:
37+
pass
38+
39+
# Theme tokens
40+
THEME = os.getenv("ANYPLOT_THEME", "light")
41+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
42+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
43+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
44+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
2345

24-
# Data - Plant growth study with soil type and light conditions
46+
# Okabe-Ito palette
47+
OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"]
48+
49+
# Data - Student exam scores across teaching methods and subjects
2550
np.random.seed(42)
2651

27-
# Create faceting variables
28-
soil_types = ["Sandy", "Loamy", "Clay"]
29-
light_levels = ["Low", "Medium", "High"]
30-
species = ["Basil", "Fern", "Tomato"] # Alphabetical order for consistent legend
52+
methods = ["In-Person", "Online", "Hybrid"]
53+
subjects = ["Math", "Science", "Reading", "Writing"]
3154

32-
# Generate realistic data with more pronounced differences between conditions
3355
data = []
34-
for soil in soil_types:
35-
for light in light_levels:
36-
# Amplified effects to show clearer differences between facets
37-
soil_effect = {"Sandy": -12, "Loamy": 0, "Clay": 8}[soil]
38-
light_effect = {"Low": -15, "Medium": 0, "High": 18}[light]
39-
40-
for sp in species:
41-
species_effect = {"Basil": 0, "Tomato": 8, "Fern": -5}[sp]
42-
n_points = 12 # Slightly fewer points per panel for clarity
56+
for subject in subjects:
57+
for method in methods:
58+
subject_effect = {"Math": 5, "Science": 3, "Reading": -2, "Writing": 0}[subject]
59+
method_effect = {"In-Person": 3, "Online": -2, "Hybrid": 1}[method]
4360

44-
# Water amount (x-axis)
45-
water = np.random.uniform(25, 65, n_points)
61+
n_points = 15
62+
hours = np.random.uniform(2, 10, n_points)
4663

47-
# Growth depends on water, soil, light, and species
48-
base_growth = 25 + 0.9 * water + soil_effect + light_effect + species_effect
49-
growth = base_growth + np.random.randn(n_points) * 4
64+
base_score = 60 + 6 * hours + subject_effect + method_effect
65+
score = base_score + np.random.randn(n_points) * 5
66+
score = np.clip(score, 30, 100)
5067

51-
for i in range(n_points):
52-
data.append(
53-
{"water": water[i], "growth": growth[i], "soil_type": soil, "light_level": light, "species": sp}
54-
)
68+
for i in range(n_points):
69+
data.append({"hours": hours[i], "score": score[i], "subject": subject, "method": method})
5570

5671
df = pd.DataFrame(data)
5772

58-
# Ensure correct ordering of categorical variables for facets
59-
df["soil_type"] = pd.Categorical(df["soil_type"], categories=soil_types, ordered=True)
60-
df["light_level"] = pd.Categorical(df["light_level"], categories=light_levels, ordered=True)
61-
# Match legend order with species order
62-
df["species"] = pd.Categorical(df["species"], categories=species, ordered=True)
73+
df["subject"] = pd.Categorical(df["subject"], categories=subjects, ordered=True)
74+
df["method"] = pd.Categorical(df["method"], categories=methods, ordered=True)
6375

64-
# Plot - Faceted grid showing growth patterns across soil and light conditions
6576
plot = (
66-
ggplot(df, aes(x="water", y="growth", color="species"))
67-
+ geom_point(size=3.5, alpha=0.75)
68-
+ facet_grid("soil_type ~ light_level", labeller="label_both")
69-
+ scale_color_brewer(type="qual", palette="Set2")
70-
+ labs(title="facet-grid · plotnine · pyplots.ai", x="Water (mL/day)", y="Growth (cm)", color="Species")
77+
ggplot(df, aes(x="hours", y="score", color="method"))
78+
+ geom_point(size=4, alpha=0.75)
79+
+ facet_grid("subject ~ method", labeller="label_both")
80+
+ scale_color_manual(values=OKABE_ITO[:3])
81+
+ labs(title="facet-grid · plotnine · anyplot.ai", x="Study Hours", y="Exam Score", color="Method")
7182
+ theme_minimal()
7283
+ theme(
7384
figure_size=(16, 9),
74-
plot_title=element_text(size=24, face="bold", ha="center"),
75-
axis_title=element_text(size=18),
76-
axis_text=element_text(size=14),
77-
strip_text_x=element_text(size=14, face="bold"),
78-
strip_text_y=element_text(size=14, face="bold", rotation=0),
79-
strip_background=element_rect(fill="#f0f0f0", color="#cccccc"),
80-
legend_title=element_text(size=16, face="bold"),
81-
legend_text=element_text(size=14),
85+
plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
86+
panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
87+
plot_title=element_text(size=24, color=INK, ha="center"),
88+
axis_title=element_text(size=20, color=INK),
89+
axis_text=element_text(size=16, color=INK_SOFT),
90+
strip_text_x=element_text(size=14, color=INK, face="bold"),
91+
strip_text_y=element_text(size=14, color=INK, face="bold", rotation=0),
92+
strip_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
93+
legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
94+
legend_title=element_text(size=16, color=INK),
95+
legend_text=element_text(size=14, color=INK_SOFT),
8296
legend_position="right",
83-
panel_grid_major=element_line(color="#cccccc", alpha=0.4),
84-
panel_grid_minor=element_line(color="#eeeeee", alpha=0.2),
97+
panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),
98+
panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),
8599
panel_spacing_x=0.06,
86100
panel_spacing_y=0.06,
87101
)
88102
)
89103

90-
# Save
91-
plot.save("plot.png", dpi=300, verbose=False)
104+
plot.save(f"plot-{THEME}.png", dpi=300, verbose=False)

0 commit comments

Comments
 (0)