|
1 | 1 | """ pyplots.ai |
2 | 2 | raincloud-basic: Basic Raincloud Plot |
3 | 3 | Library: highcharts unknown | Python 3.13.11 |
4 | | -Quality: 91/100 | Created: 2025-12-24 |
| 4 | +Quality: 78/100 | Created: 2025-12-25 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import json |
|
19 | 19 | np.random.seed(42) |
20 | 20 | categories = ["Control", "Treatment A", "Treatment B", "Treatment C"] |
21 | 21 | colors = ["#306998", "#FFD43B", "#9467BD", "#17BECF"] |
| 22 | +# Fill colors for box plots with good visibility (increased opacity) |
| 23 | +box_fill_colors = ["rgba(48,105,152,0.7)", "rgba(255,212,59,0.7)", "rgba(148,103,189,0.7)", "rgba(23,190,207,0.7)"] |
22 | 24 |
|
23 | 25 | # Generate realistic reaction time data with different distributions |
24 | 26 | control = np.random.normal(450, 60, 80) # Normal distribution |
|
52 | 54 | } |
53 | 55 | ) |
54 | 56 |
|
55 | | -# Create jittered scatter data (the "rain") |
| 57 | +# Create jittered scatter data (the "rain" - falls LEFT of the cloud for vertical orientation) |
56 | 58 | scatter_data = [] |
57 | 59 | for i, data in enumerate(all_data): |
58 | 60 | for val in data: |
59 | 61 | jitter = np.random.uniform(-0.08, 0.08) |
60 | | - scatter_data.append({"x": i + 0.25 + jitter, "y": float(val), "color": colors[i]}) |
| 62 | + # Rain on LEFT side (negative offset from category center) |
| 63 | + scatter_data.append({"x": i - 0.25 + jitter, "y": float(val), "color": colors[i]}) |
61 | 64 |
|
62 | | -# Box plot series data |
| 65 | +# Box plot series data with semi-transparent fill and dark borders |
63 | 66 | box_series_data = [] |
64 | 67 | for i, box in enumerate(box_data): |
65 | 68 | box_series_data.append( |
|
69 | 72 | "median": box["median"], |
70 | 73 | "q3": box["q3"], |
71 | 74 | "high": box["high"], |
72 | | - "color": colors[i], |
73 | | - "fillColor": f"rgba({int(colors[i][1:3], 16)}, {int(colors[i][3:5], 16)}, {int(colors[i][5:7], 16)}, 0.6)", |
| 75 | + "color": "#1a1a1a", # Dark border for visibility |
| 76 | + "fillColor": box_fill_colors[i], |
74 | 77 | } |
75 | 78 | ) |
76 | 79 |
|
77 | 80 | # Create polygon data for half-violin (the "cloud") - inline KDE |
| 81 | +# Cloud on RIGHT side for vertical orientation (rain falls from cloud, so cloud is RIGHT/TOP) |
78 | 82 | violin_polygons = [] |
79 | 83 | for i, data in enumerate(all_data): |
80 | 84 | # Inline KDE computation (Gaussian kernel) |
|
90 | 94 | density = density / (n * bandwidth * np.sqrt(2 * np.pi)) |
91 | 95 | density = density / density.max() * 0.35 |
92 | 96 |
|
93 | | - # Create polygon points for filled half-violin (close the polygon) |
| 97 | + # Create polygon points for filled half-violin on RIGHT side (close the polygon) |
94 | 98 | polygon_points = [] |
| 99 | + # Right side: baseline at category, extend RIGHT (positive direction) |
95 | 100 | for y, d in zip(y_range, density, strict=True): |
96 | | - polygon_points.append([float(i - d - 0.05), float(y)]) |
| 101 | + polygon_points.append([float(i + d + 0.05), float(y)]) |
97 | 102 | # Close polygon by going back along the baseline |
98 | 103 | for y in reversed(y_range): |
99 | | - polygon_points.append([float(i - 0.05), float(y)]) |
| 104 | + polygon_points.append([float(i + 0.05), float(y)]) |
100 | 105 | # Close the polygon |
101 | 106 | polygon_points.append(polygon_points[0]) |
102 | 107 | violin_polygons.append({"points": polygon_points, "color": colors[i]}) |
|
117 | 122 | enableMouseTracking: false, |
118 | 123 | showInLegend: {show_legend}, |
119 | 124 | {linked} |
| 125 | + legendSymbol: 'areaMarker', |
120 | 126 | marker: {{ enabled: false }} |
121 | 127 | }}""") |
122 | 128 |
|
|
128 | 134 | backgroundColor: '#ffffff', |
129 | 135 | marginBottom: 280, |
130 | 136 | marginLeft: 220, |
131 | | - marginRight: 350, |
| 137 | + marginRight: 200, |
132 | 138 | spacingBottom: 80 |
133 | 139 | }}, |
134 | 140 | title: {{ |
|
158 | 164 | labels: {{ |
159 | 165 | style: {{ fontSize: '36px' }} |
160 | 166 | }}, |
161 | | - gridLineWidth: 1, |
162 | | - gridLineDashStyle: 'Dash', |
163 | | - min: 200, |
164 | | - max: 660 |
| 167 | + gridLineWidth: 2, |
| 168 | + gridLineColor: 'rgba(0, 0, 0, 0.35)', |
| 169 | + gridLineDashStyle: 'Solid', |
| 170 | + tickInterval: 50, |
| 171 | + min: 250, |
| 172 | + max: 650 |
165 | 173 | }}, |
166 | 174 | legend: {{ |
167 | 175 | enabled: true, |
168 | | - itemStyle: {{ fontSize: '32px' }}, |
| 176 | + itemStyle: {{ fontSize: '36px' }}, |
169 | 177 | align: 'right', |
170 | 178 | verticalAlign: 'top', |
171 | 179 | layout: 'vertical', |
172 | 180 | x: -50, |
173 | | - y: 100 |
| 181 | + y: 100, |
| 182 | + backgroundColor: 'rgba(255, 255, 255, 0.9)', |
| 183 | + borderWidth: 1, |
| 184 | + borderColor: '#cccccc', |
| 185 | + padding: 20 |
174 | 186 | }}, |
175 | 187 | plotOptions: {{ |
176 | 188 | boxplot: {{ |
177 | | - medianColor: '#1a1a1a', |
178 | | - medianWidth: 6, |
| 189 | + medianColor: '#000000', |
| 190 | + medianWidth: 8, |
| 191 | + medianDashStyle: 'Solid', |
| 192 | + stemColor: '#1a1a1a', |
179 | 193 | stemWidth: 4, |
180 | | - whiskerWidth: 4, |
181 | | - whiskerLength: '40%', |
182 | | - lineWidth: 3, |
183 | | - pointWidth: 60 |
| 194 | + whiskerColor: '#1a1a1a', |
| 195 | + whiskerWidth: 5, |
| 196 | + whiskerLength: '50%', |
| 197 | + lineWidth: 4, |
| 198 | + pointWidth: 70 |
184 | 199 | }}, |
185 | 200 | scatter: {{ |
186 | 201 | marker: {{ |
187 | | - radius: 10, |
| 202 | + radius: 18, |
188 | 203 | symbol: 'circle' |
189 | 204 | }} |
190 | 205 | }}, |
|
196 | 211 | series: [ |
197 | 212 | {",".join(polygon_series_js)}, |
198 | 213 | {{ |
199 | | - name: 'Box Plot', |
| 214 | + name: 'Box Plot (Q1-Q3)', |
200 | 215 | type: 'boxplot', |
201 | 216 | data: {json.dumps(box_series_data)}, |
202 | 217 | colorByPoint: true, |
| 218 | + showInLegend: true, |
| 219 | + legendSymbol: 'rectangle', |
| 220 | + color: '#1a1a1a', |
203 | 221 | tooltip: {{ |
204 | 222 | headerFormat: '<b>{{point.key}}</b><br/>', |
205 | 223 | pointFormat: 'Max: {{point.high:.0f}} ms<br/>Q3: {{point.q3:.0f}} ms<br/>Median: {{point.median:.0f}} ms<br/>Q1: {{point.q1:.0f}} ms<br/>Min: {{point.low:.0f}} ms' |
|
210 | 228 | type: 'scatter', |
211 | 229 | data: {json.dumps(scatter_data)}, |
212 | 230 | marker: {{ |
213 | | - radius: 8, |
214 | | - lineWidth: 1, |
215 | | - lineColor: 'rgba(0,0,0,0.3)' |
| 231 | + radius: 16, |
| 232 | + lineWidth: 2, |
| 233 | + lineColor: 'rgba(0,0,0,0.4)' |
216 | 234 | }}, |
217 | | - opacity: 0.6, |
| 235 | + opacity: 0.65, |
218 | 236 | tooltip: {{ |
219 | 237 | pointFormat: 'Value: {{point.y:.0f}} ms' |
220 | 238 | }} |
|
0 commit comments