|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | dumbbell-basic: Basic Dumbbell Chart |
3 | | -Library: plotnine 0.15.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: plotnine 0.15.3 | Python 3.14.4 |
| 4 | +Quality: 89/100 | Updated: 2026-04-26 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import pandas as pd |
8 | 10 | from plotnine import ( |
9 | 11 | aes, |
10 | 12 | element_blank, |
| 13 | + element_line, |
| 14 | + element_rect, |
11 | 15 | element_text, |
12 | 16 | geom_point, |
13 | 17 | geom_segment, |
|
20 | 24 | ) |
21 | 25 |
|
22 | 26 |
|
| 27 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 28 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 29 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 30 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 31 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 32 | + |
| 33 | +OKABE_ITO = ["#009E73", "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442"] |
| 34 | + |
23 | 35 | # Data: Employee satisfaction scores before and after workplace policy changes |
24 | 36 | categories = ["Engineering", "Marketing", "Sales", "HR", "Finance", "Operations", "Customer Support", "Product"] |
25 | 37 | before_scores = [65, 58, 72, 45, 68, 52, 40, 75] |
26 | 38 | after_scores = [82, 71, 78, 73, 75, 68, 62, 88] |
27 | 39 |
|
28 | | -# Calculate differences and sort by largest improvement first |
29 | 40 | differences = [a - b for a, b in zip(after_scores, before_scores, strict=True)] |
30 | | -sorted_data = sorted( |
31 | | - zip(categories, before_scores, after_scores, differences, strict=True), key=lambda x: x[3], reverse=True |
32 | | -) |
| 41 | +sorted_data = sorted(zip(categories, before_scores, after_scores, differences, strict=True), key=lambda x: x[3]) |
33 | 42 | categories = [d[0] for d in sorted_data] |
34 | 43 | before_scores = [d[1] for d in sorted_data] |
35 | 44 | after_scores = [d[2] for d in sorted_data] |
| 45 | +differences = [d[3] for d in sorted_data] |
36 | 46 |
|
37 | | -# Create DataFrame for segments (connecting lines) |
38 | | -df_segments = pd.DataFrame({"category": categories, "start": before_scores, "end": after_scores}) |
39 | | - |
40 | | -# Create DataFrame for points (long format for legend) |
| 47 | +df_segments = pd.DataFrame({"category": categories, "start": before_scores, "end": after_scores, "diff": differences}) |
41 | 48 | df_points = pd.DataFrame( |
42 | 49 | { |
43 | 50 | "category": categories * 2, |
|
46 | 53 | } |
47 | 54 | ) |
48 | 55 |
|
49 | | -# Create ordered categorical for proper y-axis ordering |
50 | 56 | df_segments["category"] = pd.Categorical(df_segments["category"], categories=categories, ordered=True) |
51 | 57 | df_points["category"] = pd.Categorical(df_points["category"], categories=categories, ordered=True) |
| 58 | +df_points["period"] = pd.Categorical(df_points["period"], categories=["Before", "After"], ordered=True) |
52 | 59 |
|
53 | | -# Plot |
54 | 60 | plot = ( |
55 | 61 | ggplot() |
56 | | - # Connecting lines (thin and subtle) |
57 | 62 | + geom_segment( |
58 | | - aes(x="start", xend="end", y="category", yend="category"), data=df_segments, color="#888888", size=1.5 |
| 63 | + aes(x="start", xend="end", y="category", yend="category"), data=df_segments, color=INK_SOFT, size=1.5, alpha=0.6 |
| 64 | + ) |
| 65 | + + geom_point(aes(x="value", y="category", color="period"), data=df_points, size=7) |
| 66 | + + scale_color_manual(values={"Before": OKABE_ITO[0], "After": OKABE_ITO[1]}) |
| 67 | + + scale_x_continuous(limits=(30, 100), breaks=[30, 40, 50, 60, 70, 80, 90, 100]) |
| 68 | + + labs( |
| 69 | + x="Satisfaction Score", |
| 70 | + y="Department", |
| 71 | + title="Employee Satisfaction · dumbbell-basic · plotnine · anyplot.ai", |
| 72 | + color="", |
59 | 73 | ) |
60 | | - # Dots with distinct colors for before/after |
61 | | - + geom_point(aes(x="value", y="category", color="period"), data=df_points, size=6, stroke=0.5) |
62 | | - + scale_color_manual(values={"Before": "#306998", "After": "#FFD43B"}) |
63 | | - + scale_x_continuous(limits=(30, 100)) |
64 | | - + labs(x="Satisfaction Score", y="Department", title="dumbbell-basic · plotnine · pyplots.ai", color="") |
65 | 74 | + theme_minimal() |
66 | 75 | + theme( |
67 | 76 | figure_size=(16, 9), |
68 | | - plot_title=element_text(size=24), |
69 | | - axis_title=element_text(size=20), |
70 | | - axis_text=element_text(size=16), |
71 | | - legend_text=element_text(size=16), |
| 77 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 78 | + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 79 | + plot_title=element_text(size=24, color=INK, weight="bold"), |
| 80 | + axis_title=element_text(size=20, color=INK), |
| 81 | + axis_text=element_text(size=16, color=INK_SOFT), |
| 82 | + legend_text=element_text(size=16, color=INK_SOFT), |
| 83 | + legend_title=element_text(color=INK), |
| 84 | + legend_background=element_rect(fill=ELEVATED_BG, color=ELEVATED_BG), |
72 | 85 | legend_position="right", |
| 86 | + panel_grid_major_x=element_line(color=INK, size=0.3, alpha=0.10), |
| 87 | + panel_grid_minor=element_blank(), |
73 | 88 | panel_grid_major_y=element_blank(), |
74 | 89 | ) |
75 | 90 | ) |
76 | 91 |
|
77 | | -plot.save("plot.png", dpi=300, verbose=False) |
| 92 | +plot.save(f"plot-{THEME}.png", dpi=300, verbose=False) |
0 commit comments