Skip to content

Commit d70b1df

Browse files
update(violin-basic): altair — comprehensive quality review (#4326)
## Summary Updated **altair** implementation for **violin-basic**. **Changes:** Comprehensive quality review improving code quality, data choice, visual design, spec compliance, and library feature usage. ### Changes - Improved data generation with distinct distribution shapes per category - Enhanced visual design (explicit font sizes, refined color palette, layout balance) - Fixed review weaknesses from previous evaluation - Updated metadata with current library/Python versions - Preview images uploaded to GCS staging ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 4a05496 commit d70b1df

2 files changed

Lines changed: 173 additions & 147 deletions

File tree

plots/violin-basic/implementations/altair.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
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: 94/100 | Updated: 2026-02-21
55
"""
66

77
import altair as alt
@@ -16,39 +16,29 @@
1616

1717
for cat in categories:
1818
if cat == "Engineering":
19-
# Higher salaries with moderate spread
2019
values = np.random.normal(92000, 16000, 150)
2120
elif cat == "Marketing":
22-
# Mid-range salaries
2321
values = np.random.normal(70000, 13000, 150)
2422
elif cat == "Sales":
2523
# Bimodal: base salary + high performers with commissions
2624
values = np.concatenate([np.random.normal(50000, 8000, 75), np.random.normal(92000, 11000, 75)])
2725
else: # Support
28-
# Lower salary, tighter distribution
2926
values = np.random.normal(55000, 10000, 150)
3027

3128
for v in values:
3229
data.append({"Department": cat, "Salary": v})
3330

3431
df = pd.DataFrame(data)
3532

36-
# Calculate statistics for quartile markers
37-
stats = (
38-
df.groupby("Department")["Salary"]
39-
.agg(q1=lambda x: x.quantile(0.25), median=lambda x: x.quantile(0.5), q3=lambda x: x.quantile(0.75))
40-
.reset_index()
41-
)
42-
43-
# Merge stats for layering
44-
df_with_stats = df.merge(stats, on="Department")
33+
# Department order: unimodal distributions first, bimodal Sales last as focal point
34+
dept_order = ["Support", "Marketing", "Engineering", "Sales"]
4535

46-
# Colors - Python palette
47-
colors = ["#306998", "#FFD43B", "#4B8BBE", "#FFE873"]
48-
color_scale = alt.Scale(domain=categories, range=colors)
36+
# Colors - four fully distinct colorblind-safe hues with Python Blue
37+
# brown, purple, Python Blue, orange — each maximally distinct
38+
palette = ["#8B6C42", "#9467BD", "#306998", "#E5832D"]
39+
color_scale = alt.Scale(domain=dept_order, range=palette)
4940

50-
# Base chart
51-
base = alt.Chart(df_with_stats)
41+
base = alt.Chart(df)
5242

5343
# Violin shape using kernel density transform
5444
violin = (
@@ -69,14 +59,25 @@
6959
axis=alt.Axis(labels=False, values=[0], grid=False, ticks=False),
7060
),
7161
color=alt.Color("Department:N", scale=color_scale, legend=None),
62+
tooltip=[alt.Tooltip("Department:N"), alt.Tooltip("Salary:Q", format="$,.0f")],
7263
)
7364
)
7465

75-
# IQR rule (black vertical line)
76-
quartile_rule = base.mark_rule(color="black", strokeWidth=5).encode(y="q1:Q", y2="q3:Q")
66+
# IQR rule via declarative aggregate (one rule per department)
67+
quartile_rule = (
68+
base.transform_aggregate(q1="q1(Salary)", q3="q3(Salary)", groupby=["Department"])
69+
.mark_rule(color="#1a1a1a", strokeWidth=5)
70+
.encode(y="q1:Q", y2="q3:Q")
71+
)
7772

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")
73+
# Median point via declarative aggregate (one dot per department)
74+
median_point = (
75+
base.transform_aggregate(med="median(Salary)", groupby=["Department"])
76+
.mark_point(color="white", size=250, filled=True, strokeWidth=3, stroke="#1a1a1a")
77+
.encode(
78+
y="med:Q", tooltip=[alt.Tooltip("Department:N"), alt.Tooltip("med:Q", title="Median Salary", format="$,.0f")]
79+
)
80+
)
8081

8182
# Combine layers and facet by department
8283
chart = (
@@ -85,14 +86,14 @@
8586
column=alt.Column(
8687
"Department:N",
8788
header=alt.Header(labelFontSize=20, labelOrient="bottom", title=None, labelPadding=15),
88-
sort=categories,
89+
sort=dept_order,
8990
)
9091
)
9192
.resolve_scale(x="independent")
9293
.properties(title=alt.Title("violin-basic · altair · pyplots.ai", fontSize=28, anchor="middle"))
9394
.configure_facet(spacing=20)
9495
.configure_view(stroke=None, continuousWidth=350, continuousHeight=750)
95-
.configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.3, gridDash=[3, 3])
96+
.configure_axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.2, gridDash=[3, 3])
9697
)
9798

9899
# Save outputs

0 commit comments

Comments
 (0)