|
1 | 1 | """ pyplots.ai |
2 | 2 | violin-basic: Basic Violin Plot |
3 | | -Library: altair 6.0.0 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-23 |
| 3 | +Library: altair 6.0.0 | Python 3.14.3 |
| 4 | +Quality: /100 | Updated: 2026-02-21 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import altair as alt |
|
16 | 16 |
|
17 | 17 | for cat in categories: |
18 | 18 | if cat == "Engineering": |
19 | | - # Higher salaries with moderate spread |
20 | 19 | values = np.random.normal(92000, 16000, 150) |
21 | 20 | elif cat == "Marketing": |
22 | | - # Mid-range salaries |
23 | 21 | values = np.random.normal(70000, 13000, 150) |
24 | 22 | elif cat == "Sales": |
25 | 23 | # Bimodal: base salary + high performers with commissions |
26 | 24 | values = np.concatenate([np.random.normal(50000, 8000, 75), np.random.normal(92000, 11000, 75)]) |
27 | 25 | else: # Support |
28 | | - # Lower salary, tighter distribution |
29 | 26 | values = np.random.normal(55000, 10000, 150) |
30 | 27 |
|
31 | 28 | for v in values: |
32 | 29 | data.append({"Department": cat, "Salary": v}) |
33 | 30 |
|
34 | 31 | df = pd.DataFrame(data) |
35 | 32 |
|
36 | | -# Calculate statistics for quartile markers |
| 33 | +# Merge quartile statistics for layering |
37 | 34 | stats = ( |
38 | 35 | df.groupby("Department")["Salary"] |
39 | 36 | .agg(q1=lambda x: x.quantile(0.25), median=lambda x: x.quantile(0.5), q3=lambda x: x.quantile(0.75)) |
40 | 37 | .reset_index() |
41 | 38 | ) |
| 39 | +df = df.merge(stats, on="Department") |
42 | 40 |
|
43 | | -# Merge stats for layering |
44 | | -df_with_stats = df.merge(stats, on="Department") |
45 | | - |
46 | | -# Colors - Python palette |
47 | | -colors = ["#306998", "#FFD43B", "#4B8BBE", "#FFE873"] |
| 41 | +# Colors - cohesive colorblind-safe palette starting with Python Blue |
| 42 | +colors = ["#306998", "#E5832D", "#4B8BBE", "#8B6C42"] |
48 | 43 | color_scale = alt.Scale(domain=categories, range=colors) |
49 | 44 |
|
50 | 45 | # Base chart |
51 | | -base = alt.Chart(df_with_stats) |
| 46 | +base = alt.Chart(df) |
52 | 47 |
|
53 | 48 | # Violin shape using kernel density transform |
54 | 49 | violin = ( |
|
69 | 64 | axis=alt.Axis(labels=False, values=[0], grid=False, ticks=False), |
70 | 65 | ), |
71 | 66 | color=alt.Color("Department:N", scale=color_scale, legend=None), |
| 67 | + tooltip=[alt.Tooltip("Department:N"), alt.Tooltip("Salary:Q", format="$,.0f")], |
72 | 68 | ) |
73 | 69 | ) |
74 | 70 |
|
75 | | -# IQR rule (black vertical line) |
76 | | -quartile_rule = base.mark_rule(color="black", strokeWidth=5).encode(y="q1:Q", y2="q3:Q") |
| 71 | +# IQR rule (dark line from Q1 to Q3) |
| 72 | +quartile_rule = base.mark_rule(color="#1a1a1a", strokeWidth=5).encode(y="q1:Q", y2="q3:Q") |
77 | 73 |
|
78 | | -# Median point (white dot with black border) |
79 | | -median_point = base.mark_point(color="white", size=250, filled=True, strokeWidth=3, stroke="black").encode(y="median:Q") |
| 74 | +# Median point (white dot with dark border) |
| 75 | +median_point = base.mark_point(color="white", size=250, filled=True, strokeWidth=3, stroke="#1a1a1a").encode( |
| 76 | + y="median:Q", tooltip=[alt.Tooltip("Department:N"), alt.Tooltip("median:Q", title="Median Salary", format="$,.0f")] |
| 77 | +) |
80 | 78 |
|
81 | 79 | # Combine layers and facet by department |
82 | 80 | chart = ( |
|
92 | 90 | .properties(title=alt.Title("violin-basic · altair · pyplots.ai", fontSize=28, anchor="middle")) |
93 | 91 | .configure_facet(spacing=20) |
94 | 92 | .configure_view(stroke=None, continuousWidth=350, continuousHeight=750) |
95 | | - .configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.3, gridDash=[3, 3]) |
| 93 | + .configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.2, gridDash=[3, 3]) |
96 | 94 | ) |
97 | 95 |
|
98 | 96 | # Save outputs |
|
0 commit comments