|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | box-grouped: Grouped Box Plot |
3 | | -Library: letsplot 4.8.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-25 |
| 3 | +Library: letsplot 4.9.0 | Python 3.13.13 |
| 4 | +Quality: 91/100 | Updated: 2026-05-08 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | +import shutil |
| 9 | + |
7 | 10 | import numpy as np |
8 | 11 | import pandas as pd |
9 | 12 | from lets_plot import ( |
10 | 13 | LetsPlot, |
11 | 14 | aes, |
| 15 | + element_line, |
| 16 | + element_rect, |
12 | 17 | element_text, |
13 | 18 | geom_boxplot, |
14 | 19 | ggplot, |
|
23 | 28 |
|
24 | 29 | LetsPlot.setup_html() |
25 | 30 |
|
| 31 | +# Theme tokens |
| 32 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 33 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 34 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 35 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 36 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 37 | + |
| 38 | +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] |
| 39 | + |
26 | 40 | # Data: Employee performance scores by department and experience level |
27 | 41 | np.random.seed(42) |
28 | 42 |
|
|
33 | 47 | for dept in departments: |
34 | 48 | for exp in experience_levels: |
35 | 49 | n = 40 |
36 | | - # Create realistic distributions with variation |
37 | 50 | if exp == "Junior": |
38 | | - base = 60 + np.random.choice([0, 5, 10]) |
| 51 | + base = 50 + np.random.choice([0, 5, 10]) |
39 | 52 | spread = 12 |
40 | 53 | elif exp == "Mid-Level": |
41 | | - base = 72 + np.random.choice([0, 3, 6]) |
| 54 | + base = 60 + np.random.choice([0, 3, 6]) |
42 | 55 | spread = 10 |
43 | | - else: # Senior |
44 | | - base = 82 + np.random.choice([0, 2, 4]) |
| 56 | + else: |
| 57 | + base = 72 + np.random.choice([0, 2, 4]) |
45 | 58 | spread = 8 |
46 | 59 |
|
47 | | - # Add department-specific variation |
48 | 60 | dept_offset = {"Engineering": 3, "Marketing": 0, "Sales": -2, "Operations": 1}[dept] |
49 | 61 | values = np.random.normal(base + dept_offset, spread, n) |
50 | 62 |
|
51 | | - # Add some outliers for boxplot visualization |
52 | 63 | if np.random.random() > 0.5: |
53 | 64 | outlier_low = base - 3 * spread + np.random.randn(2) * 2 |
54 | 65 | outlier_high = base + 3 * spread + np.random.randn(2) * 2 |
|
58 | 69 | data.append({"Department": dept, "Experience": exp, "Performance Score": v}) |
59 | 70 |
|
60 | 71 | df = pd.DataFrame(data) |
61 | | - |
62 | | -# Ensure proper ordering |
63 | 72 | df["Experience"] = pd.Categorical(df["Experience"], categories=experience_levels, ordered=True) |
64 | 73 | df["Department"] = pd.Categorical(df["Department"], categories=departments, ordered=True) |
65 | 74 |
|
66 | 75 | # Plot |
67 | 76 | plot = ( |
68 | 77 | ggplot(df, aes(x="Department", y="Performance Score", fill="Experience")) |
69 | 78 | + geom_boxplot(alpha=0.85, outlier_size=3, outlier_alpha=0.7, width=0.7) |
70 | | - + scale_fill_manual(values=["#306998", "#FFD43B", "#4CAF50"]) |
71 | | - + labs( |
72 | | - title="box-grouped · letsplot · pyplots.ai", x="Department", y="Performance Score (%)", fill="Experience Level" |
73 | | - ) |
| 79 | + + scale_fill_manual(values=OKABE_ITO) |
| 80 | + + labs(title="box-grouped · letsplot · anyplot.ai", x="Department", y="Performance Score", fill="Experience Level") |
74 | 81 | + theme_minimal() |
75 | 82 | + theme( |
76 | | - plot_title=element_text(size=28, face="bold"), |
77 | | - axis_title=element_text(size=22), |
78 | | - axis_text=element_text(size=18), |
79 | | - axis_text_x=element_text(size=18), |
80 | | - legend_title=element_text(size=20), |
81 | | - legend_text=element_text(size=18), |
| 83 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 84 | + panel_background=element_rect(fill=PAGE_BG), |
| 85 | + panel_grid_major=element_line(color=INK_SOFT, size=0.3), |
| 86 | + plot_title=element_text(size=24, face="bold", color=INK), |
| 87 | + axis_title=element_text(size=20, color=INK), |
| 88 | + axis_text=element_text(size=16, color=INK_SOFT), |
| 89 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), |
| 90 | + legend_title=element_text(size=16, color=INK), |
| 91 | + legend_text=element_text(size=16, color=INK_SOFT), |
82 | 92 | legend_position="right", |
83 | 93 | ) |
84 | 94 | + ggsize(1600, 900) |
85 | 95 | ) |
86 | 96 |
|
87 | | -# Save as PNG (scale 3x for 4800x2700 px) |
88 | | -ggsave(plot, "plot.png", path=".", scale=3) |
| 97 | +# Save |
| 98 | +ggsave(plot, f"plot-{THEME}.png", scale=3) |
| 99 | +ggsave(plot, f"plot-{THEME}.html") |
89 | 100 |
|
90 | | -# Save as HTML for interactivity |
91 | | -ggsave(plot, "plot.html", path=".") |
| 101 | +# Move files from lets-plot-images to current directory |
| 102 | +shutil.move(f"lets-plot-images/plot-{THEME}.png", f"plot-{THEME}.png") |
| 103 | +shutil.move(f"lets-plot-images/plot-{THEME}.html", f"plot-{THEME}.html") |
0 commit comments