Skip to content

Commit 9ef6e6b

Browse files
feat(letsplot): implement violin-box (#2690)
## Implementation: `violin-box` - letsplot Implements the **letsplot** version of `violin-box`. **File:** `plots/violin-box/implementations/letsplot.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20595343277)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 665f45b commit 9ef6e6b

2 files changed

Lines changed: 86 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
""" pyplots.ai
2+
violin-box: Violin Plot with Embedded Box Plot
3+
Library: letsplot 4.8.2 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import *
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Data - Response times (ms) for different server configurations
15+
np.random.seed(42)
16+
17+
n_per_group = 150
18+
groups = ["Standard", "Optimized", "Premium", "Enterprise"]
19+
20+
data = []
21+
# Standard: higher mean, moderate spread
22+
data.extend([(np.random.normal(85, 15), "Standard") for _ in range(n_per_group)])
23+
# Optimized: lower mean, some outliers
24+
vals = np.concatenate([np.random.normal(55, 12, n_per_group - 5), np.random.normal(100, 5, 5)])
25+
data.extend([(v, "Optimized") for v in vals])
26+
# Premium: bimodal distribution
27+
vals = np.concatenate([np.random.normal(40, 8, n_per_group // 2), np.random.normal(60, 8, n_per_group // 2)])
28+
data.extend([(v, "Premium") for v in vals])
29+
# Enterprise: low mean, tight spread, few outliers
30+
vals = np.concatenate([np.random.normal(30, 6, n_per_group - 3), np.random.normal(70, 3, 3)])
31+
data.extend([(v, "Enterprise") for v in vals])
32+
33+
df = pd.DataFrame(data, columns=["response_time", "configuration"])
34+
35+
# Create violin plot with embedded box plot
36+
plot = (
37+
ggplot(df, aes(x="configuration", y="response_time", fill="configuration"))
38+
+ geom_violin(alpha=0.7, color="#306998", size=1.0, trim=False)
39+
+ geom_boxplot(width=0.15, fill="white", color="#306998", alpha=0.9, outlier_shape=21, outlier_size=3)
40+
+ scale_fill_manual(values=["#306998", "#FFD43B", "#4A90A4", "#7CB342"])
41+
+ labs(x="Server Configuration", y="Response Time (ms)", title="violin-box · letsplot · pyplots.ai")
42+
+ theme_minimal()
43+
+ theme(
44+
plot_title=element_text(size=24, face="bold"),
45+
axis_title=element_text(size=20),
46+
axis_text=element_text(size=16),
47+
legend_position="none",
48+
panel_grid_major_x=element_blank(),
49+
panel_grid_minor=element_blank(),
50+
)
51+
+ ggsize(1600, 900)
52+
)
53+
54+
# Save as PNG (scale 3x for 4800x2700 px)
55+
ggsave(plot, "plot.png", path=".", scale=3)
56+
57+
# Save as HTML for interactive version
58+
ggsave(plot, "plot.html", path=".")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: letsplot
2+
specification_id: violin-box
3+
created: '2025-12-30T11:30:38Z'
4+
updated: '2025-12-30T11:37:21Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20595343277
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 4.8.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/violin-box/letsplot/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/violin-box/letsplot/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/violin-box/letsplot/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent combination of violin and box plot showing both KDE density and summary
17+
statistics
18+
- Clean ggplot2-style grammar implementation with proper layering
19+
- Colorblind-friendly palette with four distinct colors
20+
- Realistic server response time scenario with varied distributions
21+
- Proper font sizing following lets-plot guidelines (24pt title, 20pt labels, 16pt
22+
ticks)
23+
- Outliers clearly visible as hollow circles
24+
weaknesses:
25+
- Legend is hidden (legend_position=none) - while x-axis labels work as identifiers,
26+
keeping a legend could add clarity
27+
- Could leverage lets-plot interactive tooltip features for the HTML export
28+
- Bimodal distribution in Premium group is somewhat subtle, could be more pronounced

0 commit comments

Comments
 (0)