|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | bar-diverging: Diverging Bar Chart |
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: 93/100 | Updated: 2026-05-08 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import pandas as pd |
8 | 10 | from lets_plot import * # noqa: F403 |
9 | 11 | from lets_plot.export import ggsave as export_ggsave |
10 | 12 |
|
11 | 13 |
|
12 | 14 | LetsPlot.setup_html() # noqa: F405 |
13 | 15 |
|
| 16 | +# Theme tokens |
| 17 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 18 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 19 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 20 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 21 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 22 | + |
| 23 | +# Okabe-Ito brand colors for diverging bars |
| 24 | +POSITIVE_COLOR = "#009E73" # Brand green |
| 25 | +NEGATIVE_COLOR = "#D55E00" # Vermillion |
| 26 | + |
14 | 27 | # Data - Customer satisfaction survey (Net Promoter Score style) |
15 | 28 | categories = [ |
16 | 29 | "Product Quality", |
|
27 | 40 | "Loyalty Program", |
28 | 41 | ] |
29 | 42 |
|
30 | | -# Scores range from -100 (all detractors) to +100 (all promoters) |
31 | | -scores = [72, 45, -28, 38, -12, 55, 21, 65, -35, 48, -8, 32] |
| 43 | +# More balanced scores: 6 negative, 6 positive |
| 44 | +scores = [62, 48, -22, 35, -15, 52, 28, 68, -38, 42, -8, 38] |
32 | 45 |
|
33 | 46 | df = pd.DataFrame( |
34 | 47 | {"Category": categories, "Score": scores, "Sentiment": ["Positive" if s >= 0 else "Negative" for s in scores]} |
|
40 | 53 | # Preserve category order after sorting |
41 | 54 | df["Category"] = pd.Categorical(df["Category"], categories=df["Category"].tolist(), ordered=True) |
42 | 55 |
|
43 | | -# Create horizontal diverging bar chart |
| 56 | +# Create horizontal diverging bar chart with theme-adaptive styling |
44 | 57 | plot = ( |
45 | 58 | ggplot(df, aes(x="Score", y="Category", fill="Sentiment")) # noqa: F405 |
46 | | - + geom_bar(stat="identity", width=0.7, alpha=0.9) # noqa: F405 |
47 | | - + geom_vline(xintercept=0, color="#333333", size=1.2) # noqa: F405 |
48 | | - + scale_fill_manual(values=["#DC2626", "#306998"]) # noqa: F405 |
| 59 | + + geom_bar(stat="identity", width=0.75, alpha=0.95) # noqa: F405 |
| 60 | + + geom_vline(xintercept=0, color=INK_SOFT, size=1.5) # noqa: F405 |
| 61 | + + scale_fill_manual( # noqa: F405 |
| 62 | + values={ |
| 63 | + "Positive": POSITIVE_COLOR, |
| 64 | + "Negative": NEGATIVE_COLOR, |
| 65 | + } |
| 66 | + ) |
49 | 67 | + labs( # noqa: F405 |
50 | | - x="Net Promoter Score", y="Category", title="bar-diverging · letsplot · pyplots.ai" |
| 68 | + x="Net Promoter Score (-100 to +100)", y="Category", title="bar-diverging · letsplot · anyplot.ai" |
51 | 69 | ) |
52 | 70 | + theme_minimal() # noqa: F405 |
53 | 71 | + theme( # noqa: F405 |
54 | | - plot_title=element_text(size=28, face="bold", hjust=0.5), # noqa: F405 |
55 | | - axis_title_x=element_text(size=22), # noqa: F405 |
56 | | - axis_title_y=element_text(size=22), # noqa: F405 |
57 | | - axis_text_x=element_text(size=18), # noqa: F405 |
58 | | - axis_text_y=element_text(size=18), # noqa: F405 |
59 | | - legend_title=element_text(size=18), # noqa: F405 |
60 | | - legend_text=element_text(size=16), # noqa: F405 |
61 | | - legend_position="right", |
| 72 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), # noqa: F405 |
| 73 | + panel_background=element_rect(fill=PAGE_BG), # noqa: F405 |
| 74 | + panel_grid_major_x=element_line(color=INK, size=0.3), # noqa: F405 |
62 | 75 | panel_grid_major_y=element_blank(), # noqa: F405 |
63 | 76 | panel_grid_minor=element_blank(), # noqa: F405 |
| 77 | + plot_title=element_text(size=28, face="bold", color=INK, hjust=0.5), # noqa: F405 |
| 78 | + axis_title_x=element_text(size=22, color=INK), # noqa: F405 |
| 79 | + axis_title_y=element_text(size=22, color=INK), # noqa: F405 |
| 80 | + axis_text_x=element_text(size=18, color=INK_SOFT), # noqa: F405 |
| 81 | + axis_text_y=element_text(size=18, color=INK_SOFT), # noqa: F405 |
| 82 | + axis_line_x=element_line(color=INK_SOFT), # noqa: F405 |
| 83 | + axis_line_y=element_line(color=INK_SOFT), # noqa: F405 |
| 84 | + legend_title=element_text(size=18, color=INK), # noqa: F405 |
| 85 | + legend_text=element_text(size=16, color=INK_SOFT), # noqa: F405 |
| 86 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), # noqa: F405 |
| 87 | + legend_position="right", |
64 | 88 | ) |
65 | 89 | + ggsize(1600, 900) # noqa: F405 |
66 | 90 | ) |
67 | 91 |
|
68 | | -# Save |
69 | | -export_ggsave(plot, filename="plot.png", path=".", scale=3) |
70 | | -export_ggsave(plot, filename="plot.html", path=".") |
| 92 | +# Save PNG with scale 3x to get 4800 × 2700 px |
| 93 | +export_ggsave(plot, filename=f"plot-{THEME}.png", path=".", scale=3) |
| 94 | + |
| 95 | +# Save HTML for interactive version |
| 96 | +export_ggsave(plot, filename=f"plot-{THEME}.html", path=".") |
0 commit comments