|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | facet-grid: Faceted Grid Plot |
3 | | -Library: letsplot 4.8.2 | Python 3.13.11 |
4 | | -Quality: 91/100 | Created: 2025-12-30 |
| 3 | +Library: letsplot 4.9.0 | Python 3.13.13 |
| 4 | +Quality: 95/100 | Updated: 2026-05-13 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import numpy as np |
8 | 10 | import pandas as pd |
9 | 11 | from lets_plot import ( |
10 | 12 | LetsPlot, |
11 | 13 | aes, |
| 14 | + element_blank, |
| 15 | + element_line, |
| 16 | + element_rect, |
12 | 17 | element_text, |
13 | 18 | facet_grid, |
14 | 19 | geom_point, |
|
25 | 30 |
|
26 | 31 | LetsPlot.setup_html() |
27 | 32 |
|
28 | | -# Data - create dataset with two categorical faceting variables |
| 33 | +# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome") |
| 34 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 35 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 36 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 37 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 38 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 39 | +RULE = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" |
| 40 | + |
| 41 | +# Okabe-Ito palette (categorical) |
| 42 | +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2"] |
| 43 | + |
| 44 | +# Data - sales analysis by category and region |
29 | 45 | np.random.seed(42) |
30 | 46 |
|
31 | | -# Product categories and regions for a sales analysis scenario |
32 | 47 | categories = ["Electronics", "Clothing", "Home"] |
33 | 48 | regions = ["North", "South", "East", "West"] |
34 | 49 |
|
35 | | -data = [] |
| 50 | +# Generate all combinations of categories and regions |
| 51 | +category_list = [] |
| 52 | +region_list = [] |
| 53 | +price_list = [] |
| 54 | +units_list = [] |
| 55 | + |
36 | 56 | for cat in categories: |
37 | 57 | for region in regions: |
38 | 58 | n_points = 25 |
39 | | - # Base relationship varies by category and region |
40 | 59 | base_price = {"Electronics": 200, "Clothing": 50, "Home": 100}[cat] |
41 | 60 | region_factor = {"North": 1.2, "South": 0.9, "East": 1.0, "West": 1.1}[region] |
42 | 61 |
|
43 | 62 | price = np.random.uniform(base_price * 0.5, base_price * 1.5, n_points) |
44 | | - # Sales units inversely related to price with some noise |
45 | 63 | units = (base_price * 100 / price) * region_factor + np.random.randn(n_points) * 10 |
| 64 | + units = np.maximum(units, 0) |
| 65 | + |
| 66 | + category_list.extend([cat] * n_points) |
| 67 | + region_list.extend([region] * n_points) |
| 68 | + price_list.extend(price) |
| 69 | + units_list.extend(units) |
46 | 70 |
|
47 | | - for p, u in zip(price, units, strict=True): |
48 | | - data.append({"Category": cat, "Region": region, "Price": p, "Units Sold": max(0, u)}) |
| 71 | +df = pd.DataFrame({"Category": category_list, "Region": region_list, "Price": price_list, "Units Sold": units_list}) |
49 | 72 |
|
50 | | -df = pd.DataFrame(data) |
| 73 | +# Theme-adaptive styling |
| 74 | +anyplot_theme = theme( |
| 75 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 76 | + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 77 | + panel_grid_major=element_line(color=RULE, size=0.3), |
| 78 | + panel_grid_minor=element_blank(), |
| 79 | + axis_title=element_text(color=INK, size=20), |
| 80 | + axis_text=element_text(color=INK_SOFT, size=16), |
| 81 | + axis_line=element_line(color=INK_SOFT, size=0.5), |
| 82 | + plot_title=element_text(color=INK, size=24), |
| 83 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), |
| 84 | + legend_text=element_text(color=INK_SOFT, size=16), |
| 85 | + legend_title=element_text(color=INK, size=18), |
| 86 | + strip_text=element_text(color=INK, size=16), |
| 87 | + strip_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), |
| 88 | +) |
51 | 89 |
|
52 | | -# Plot - faceted grid with scatter and smooth trend |
| 90 | +# Plot |
53 | 91 | plot = ( |
54 | 92 | ggplot(df, aes(x="Price", y="Units Sold", color="Category")) |
55 | 93 | + geom_point(size=3, alpha=0.7) |
56 | 94 | + geom_smooth(method="lm", se=False, size=1.5) |
57 | 95 | + facet_grid(x="Region", y="Category") |
58 | | - + scale_color_manual(values=["#306998", "#FFD43B", "#DC2626"]) |
59 | | - + labs(title="facet-grid \u00b7 letsplot \u00b7 pyplots.ai", x="Unit Price ($)", y="Units Sold") |
| 96 | + + scale_color_manual(values=OKABE_ITO) |
| 97 | + + labs(title="facet-grid · letsplot · anyplot.ai", x="Unit Price ($)", y="Units Sold") |
60 | 98 | + theme_minimal() |
61 | | - + theme( |
62 | | - plot_title=element_text(size=24), |
63 | | - axis_title=element_text(size=20), |
64 | | - axis_text=element_text(size=14), |
65 | | - legend_text=element_text(size=16), |
66 | | - legend_title=element_text(size=18), |
67 | | - strip_text=element_text(size=16), |
68 | | - ) |
| 99 | + + anyplot_theme |
69 | 100 | + ggsize(1600, 900) |
70 | 101 | ) |
71 | 102 |
|
72 | | -# Save PNG and HTML |
73 | | -ggsave(plot, "plot.png", path=".", scale=3) |
74 | | -ggsave(plot, "plot.html", path=".") |
| 103 | +# Save PNG and HTML with theme-suffixed names |
| 104 | +ggsave(plot, f"plot-{THEME}.png", path=".", scale=3) |
| 105 | +ggsave(plot, f"plot-{THEME}.html", path=".") |
0 commit comments