|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | dumbbell-basic: Basic Dumbbell Chart |
3 | | -Library: letsplot 4.8.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: letsplot 4.9.0 | Python 3.14.4 |
| 4 | +Quality: 86/100 | Updated: 2026-04-26 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import pandas as pd |
8 | 10 | from lets_plot import ( |
9 | 11 | LetsPlot, |
10 | 12 | aes, |
11 | 13 | element_blank, |
| 14 | + element_line, |
| 15 | + element_rect, |
12 | 16 | element_text, |
13 | 17 | geom_point, |
14 | 18 | geom_segment, |
|
26 | 30 |
|
27 | 31 | LetsPlot.setup_html() |
28 | 32 |
|
29 | | -# Data - Employee satisfaction scores before and after policy changes |
| 33 | +# Theme tokens |
| 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 | +GRID = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 40 | + |
| 41 | +# Okabe-Ito palette — "After" comes first alphabetically → brand green |
| 42 | +BRAND = "#009E73" |
| 43 | +ACCENT = "#D55E00" |
| 44 | +SEGMENT = INK_SOFT |
| 45 | + |
| 46 | +# Data — Employee satisfaction scores before and after policy changes. |
| 47 | +# Mix of strong gains, modest shifts, and a regression to show full plot capability. |
30 | 48 | categories = [ |
31 | 49 | "Engineering", |
32 | 50 | "Marketing", |
|
39 | 57 | "Legal", |
40 | 58 | "R&D", |
41 | 59 | ] |
42 | | -before_scores = [62, 58, 71, 55, 68, 64, 59, 73, 66, 61] |
43 | | -after_scores = [78, 72, 85, 74, 81, 76, 71, 88, 79, 75] |
| 60 | +before_scores = [62, 58, 71, 55, 68, 64, 72, 73, 66, 61] |
| 61 | +after_scores = [78, 72, 85, 80, 81, 70, 65, 88, 67, 75] |
44 | 62 |
|
45 | 63 | df = pd.DataFrame({"category": categories, "before": before_scores, "after": after_scores}) |
46 | | - |
47 | | -# Calculate difference for sorting - sort by improvement (largest first) |
48 | 64 | df["diff"] = df["after"] - df["before"] |
49 | 65 | df = df.sort_values("diff", ascending=True).reset_index(drop=True) |
50 | | - |
51 | | -# Create numeric y positions for categories (horizontal orientation) |
52 | 66 | df["y_pos"] = range(len(df)) |
53 | 67 |
|
54 | | -# Create long-format data for points (to get legend) |
55 | 68 | df_points = pd.concat( |
56 | 69 | [ |
57 | 70 | pd.DataFrame({"y_pos": df["y_pos"], "value": df["before"], "period": "Before"}), |
58 | 71 | pd.DataFrame({"y_pos": df["y_pos"], "value": df["after"], "period": "After"}), |
59 | 72 | ] |
60 | 73 | ) |
61 | 74 |
|
62 | | -# Plot - Horizontal dumbbell chart |
| 75 | +# Plot — horizontal dumbbell |
63 | 76 | plot = ( |
64 | 77 | ggplot() |
65 | | - # Connecting lines - thin and subtle |
66 | | - + geom_segment(data=df, mapping=aes(x="before", xend="after", y="y_pos", yend="y_pos"), size=1.5, color="#888888") |
67 | | - # Points with color mapping for legend |
| 78 | + + geom_segment(data=df, mapping=aes(x="before", xend="after", y="y_pos", yend="y_pos"), size=1.5, color=SEGMENT) |
68 | 79 | + geom_point(data=df_points, mapping=aes(x="value", y="y_pos", color="period"), size=8) |
69 | | - + scale_color_manual(values=["#FFD43B", "#306998"], name="Period") |
70 | | - + labs( |
71 | | - x="Satisfaction Score", y="Department", title="Employee Satisfaction · dumbbell-basic · letsplot · pyplots.ai" |
72 | | - ) |
| 80 | + + scale_color_manual(values=[BRAND, ACCENT], name="Period") |
73 | 81 | + scale_x_continuous(limits=[50, 95]) |
74 | 82 | + scale_y_continuous(breaks=list(range(len(df))), labels=df["category"].tolist()) |
| 83 | + + labs( |
| 84 | + x="Satisfaction Score", y="Department", title="Employee Satisfaction · dumbbell-basic · letsplot · anyplot.ai" |
| 85 | + ) |
75 | 86 | + ggsize(1600, 900) |
76 | 87 | + theme_minimal() |
77 | 88 | + theme( |
78 | | - axis_text_x=element_text(size=16), |
79 | | - axis_text_y=element_text(size=16), |
80 | | - axis_title=element_text(size=20), |
81 | | - plot_title=element_text(size=24, hjust=0.5), |
82 | | - legend_title=element_text(size=18), |
83 | | - legend_text=element_text(size=16), |
| 89 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 90 | + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 91 | + legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT), |
| 92 | + panel_grid_major_x=element_line(color=GRID, size=0.3), |
| 93 | + panel_grid_minor_x=element_line(color=GRID, size=0.2), |
84 | 94 | panel_grid_major_y=element_blank(), |
| 95 | + panel_grid_minor_y=element_blank(), |
| 96 | + axis_line=element_line(color=INK_SOFT), |
| 97 | + axis_ticks=element_blank(), |
| 98 | + axis_title=element_text(size=20, color=INK), |
| 99 | + axis_text=element_text(size=16, color=INK_SOFT), |
| 100 | + plot_title=element_text(size=24, color=INK, hjust=0.5), |
| 101 | + legend_title=element_text(size=18, color=INK), |
| 102 | + legend_text=element_text(size=16, color=INK_SOFT), |
| 103 | + legend_position=[0.88, 0.18], |
| 104 | + legend_justification=[1, 0], |
85 | 105 | ) |
86 | 106 | ) |
87 | 107 |
|
88 | 108 | # Save |
89 | | -ggsave(plot, filename="plot.png", path=".", scale=3) |
90 | | -ggsave(plot, filename="plot.html", path=".") |
| 109 | +ggsave(plot, filename=f"plot-{THEME}.png", path=".", scale=3) |
| 110 | +ggsave(plot, filename=f"plot-{THEME}.html", path=".") |
0 commit comments