|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | dumbbell-basic: Basic Dumbbell Chart |
3 | | -Library: altair 6.0.0 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: altair 6.1.0 | Python 3.14.4 |
| 4 | +Quality: 89/100 | Updated: 2026-04-26 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import altair as alt |
8 | 10 | import pandas as pd |
9 | 11 |
|
10 | 12 |
|
11 | | -# Data - Employee satisfaction scores before and after policy changes |
12 | | -# Sorted by improvement (difference) to reveal patterns |
| 13 | +# Theme-adaptive chrome |
| 14 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 15 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 16 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 17 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 18 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 19 | + |
| 20 | +# Okabe-Ito palette positions 1 and 2 |
| 21 | +COLOR_BEFORE = "#009E73" |
| 22 | +COLOR_AFTER = "#D55E00" |
| 23 | + |
| 24 | +# Data — Employee satisfaction scores before and after policy changes |
13 | 25 | data = pd.DataFrame( |
14 | 26 | { |
15 | 27 | "category": [ |
|
24 | 36 | "R&D", |
25 | 37 | "IT", |
26 | 38 | ], |
27 | | - "before": [52, 61, 58, 65, 72, 68, 55, 74, 63, 59], |
28 | | - "after": [78, 82, 76, 81, 85, 79, 64, 81, 69, 64], |
| 39 | + "Before": [52, 61, 58, 65, 72, 68, 55, 74, 63, 59], |
| 40 | + "After": [78, 82, 76, 81, 85, 79, 64, 81, 69, 64], |
29 | 41 | } |
30 | 42 | ) |
31 | | - |
32 | | -# Calculate difference and sort by improvement |
33 | | -data["difference"] = data["after"] - data["before"] |
| 43 | +data["difference"] = data["After"] - data["Before"] |
34 | 44 | data = data.sort_values("difference", ascending=True) |
35 | 45 |
|
36 | | -# Reshape data for Altair (long format for dots) |
| 46 | +# Long-form data for the two dot series |
37 | 47 | dots_data = pd.melt( |
38 | | - data, id_vars=["category", "difference"], value_vars=["before", "after"], var_name="period", value_name="score" |
| 48 | + data, id_vars=["category", "difference"], value_vars=["Before", "After"], var_name="period", value_name="score" |
39 | 49 | ) |
40 | 50 |
|
41 | | -# Shared scale for x-axis |
42 | 51 | x_scale = alt.Scale(domain=[45, 90]) |
| 52 | +y_sort = alt.EncodingSortField(field="difference", order="ascending") |
43 | 53 |
|
44 | | -# Create the connecting lines (using before/after as x values for line) |
| 54 | +# Connecting lines (theme-adaptive subtle ink) |
45 | 55 | lines = ( |
46 | 56 | alt.Chart(data) |
47 | | - .mark_rule(strokeWidth=2, color="#999999") |
48 | | - .encode( |
49 | | - y=alt.Y( |
50 | | - "category:N", |
51 | | - sort=alt.EncodingSortField(field="difference", order="ascending"), |
52 | | - axis=alt.Axis(labelFontSize=18, titleFontSize=22, title=None), |
53 | | - ), |
54 | | - x=alt.X("before:Q", scale=x_scale), |
55 | | - x2=alt.X2("after:Q"), |
56 | | - ) |
| 57 | + .mark_rule(strokeWidth=3, color=INK_SOFT, opacity=0.55) |
| 58 | + .encode(y=alt.Y("category:N", sort=y_sort, title=None), x=alt.X("Before:Q", scale=x_scale), x2=alt.X2("After:Q")) |
57 | 59 | ) |
58 | 60 |
|
59 | | -# Create the dots with colors for before/after |
| 61 | +# Dots for Before / After values |
60 | 62 | dots = ( |
61 | 63 | alt.Chart(dots_data) |
62 | | - .mark_circle(size=350) |
| 64 | + .mark_circle(size=420, opacity=1.0, stroke=PAGE_BG, strokeWidth=2) |
63 | 65 | .encode( |
64 | | - y=alt.Y("category:N", sort=alt.EncodingSortField(field="difference", order="ascending")), |
65 | | - x=alt.X( |
66 | | - "score:Q", axis=alt.Axis(labelFontSize=18, titleFontSize=22), title="Satisfaction Score", scale=x_scale |
67 | | - ), |
| 66 | + y=alt.Y("category:N", sort=y_sort, title=None), |
| 67 | + x=alt.X("score:Q", scale=x_scale, title="Employee Satisfaction Score (%)"), |
68 | 68 | color=alt.Color( |
69 | 69 | "period:N", |
70 | | - scale=alt.Scale(domain=["before", "after"], range=["#306998", "#FFD43B"]), |
71 | | - legend=alt.Legend(title="Period", labelFontSize=16, titleFontSize=18), |
| 70 | + scale=alt.Scale(domain=["Before", "After"], range=[COLOR_BEFORE, COLOR_AFTER]), |
| 71 | + legend=alt.Legend(title="Policy Change", labelFontSize=16, titleFontSize=18), |
72 | 72 | ), |
73 | 73 | tooltip=["category:N", "period:N", "score:Q"], |
74 | 74 | ) |
75 | 75 | ) |
76 | 76 |
|
77 | | -# Combine lines and dots |
78 | 77 | chart = ( |
79 | 78 | (lines + dots) |
80 | 79 | .properties( |
81 | 80 | width=1600, |
82 | 81 | height=900, |
83 | | - title=alt.Title("Employee Satisfaction · dumbbell-basic · altair · pyplots.ai", fontSize=28), |
| 82 | + title=alt.Title( |
| 83 | + "Employee Satisfaction · dumbbell-basic · altair · anyplot.ai", |
| 84 | + fontSize=28, |
| 85 | + color=INK, |
| 86 | + anchor="start", |
| 87 | + offset=20, |
| 88 | + ), |
| 89 | + background=PAGE_BG, |
| 90 | + ) |
| 91 | + .configure_view(fill=PAGE_BG, stroke=None) |
| 92 | + .configure_axis( |
| 93 | + labelFontSize=18, |
| 94 | + titleFontSize=22, |
| 95 | + domainColor=INK_SOFT, |
| 96 | + tickColor=INK_SOFT, |
| 97 | + gridColor=INK, |
| 98 | + gridOpacity=0.10, |
| 99 | + labelColor=INK_SOFT, |
| 100 | + titleColor=INK, |
84 | 101 | ) |
85 | | - .configure_view(strokeWidth=0) |
86 | | - .configure_axis(grid=True, gridOpacity=0.3, gridDash=[4, 4]) |
| 102 | + .configure_title(color=INK) |
| 103 | + .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK, padding=12) |
87 | 104 | ) |
88 | 105 |
|
89 | | -# Save as PNG and HTML |
90 | | -chart.save("plot.png", scale_factor=3.0) |
91 | | -chart.save("plot.html") |
| 106 | +chart.save(f"plot-{THEME}.png", scale_factor=3.0) |
| 107 | +chart.save(f"plot-{THEME}.html") |
0 commit comments