@graphysdk/geom-boxplot 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,852 @@
1
+ import { Dataset as me, Geom as ve, GeomCircle as ge, GeomLine as fe, GeomPath as ye, GeomRect as ke, IntroFade as we, POSITION_VARIABLES as B, Stat as xe, UserInputError as We, buildDotsEditOutlineShapes as Me, composePoint as Se, createStableKeyGenerator as Ie, defineGeomRenderer as Oe, defineGeomStyleTarget as be, getGroupVariables as Ee, getScaledAesthetic as b, getXMax as R, getXMin as U, getYMax as $, getYMin as A, readMainAxis as E, readVariableName as N, toPaintColor as T, toPercent as x, toStrokeDasharray as Te, toViewBoxX as _e, toViewBoxY as Be, token as M } from "@graphysdk/react";
2
+ import { useMemo as S } from "react";
3
+ import { Fragment as qe, jsx as k, jsxs as Pe } from "react/jsx-runtime";
4
+ var y = {
5
+ q1: "boxplotQ1",
6
+ median: "boxplotMedian",
7
+ q3: "boxplotQ3",
8
+ lowerWhisker: "boxplotLowerWhisker",
9
+ upperWhisker: "boxplotUpperWhisker",
10
+ count: "boxplotCount",
11
+ mean: "boxplotMean",
12
+ notchLower: "boxplotNotchLower",
13
+ notchUpper: "boxplotNotchUpper",
14
+ relativeWidth: "boxplotRelativeWidth",
15
+ outlier: "boxplotOutlier"
16
+ }, V = (e) => (e.start + e.end) / 2, re = (e, t) => {
17
+ const r = t === "x" ? U(e) : A(e), o = t === "x" ? R(e) : $(e), n = t === "x" ? A(e) : U(e), a = t === "x" ? $(e) : R(e), s = b(e, "q1"), i = b(e, "median"), u = b(e, "q3");
18
+ if (r === null || o === null || n === null || a === null || i === null || s === null || u === null) return null;
19
+ const l = C(e, "notchLower"), h = C(e, "notchUpper"), [p, g] = Ne(r, o, e);
20
+ return {
21
+ observation: e,
22
+ start: p,
23
+ end: g,
24
+ lowerWhisker: n,
25
+ q1: s,
26
+ median: i,
27
+ q3: u,
28
+ upperWhisker: a,
29
+ mean: C(e, "mean"),
30
+ notch: l === null || h === null ? null : {
31
+ lower: l,
32
+ upper: h
33
+ }
34
+ };
35
+ }, oe = (e, t) => {
36
+ const r = t === "x" ? U(e) : A(e), o = t === "x" ? R(e) : $(e), n = b(e, "outlier");
37
+ return r === null || o === null || n === null ? null : {
38
+ observation: e,
39
+ band: V({
40
+ start: r,
41
+ end: o
42
+ }),
43
+ value: n
44
+ };
45
+ }, I = (e, t) => {
46
+ const r = [], o = [];
47
+ for (const n of e) {
48
+ const a = re(n, t);
49
+ if (a !== null) {
50
+ r.push(a);
51
+ continue;
52
+ }
53
+ const s = oe(n, t);
54
+ s !== null && o.push(s);
55
+ }
56
+ return {
57
+ boxes: r,
58
+ outliers: o
59
+ };
60
+ }, C = (e, t) => b(e, t) ?? null, Ne = (e, t, r) => {
61
+ const o = r[y.relativeWidth];
62
+ if (typeof o != "number") return [e, t];
63
+ const n = (e + t) / 2, a = (t - e) * o / 2;
64
+ return [n - a, n + a];
65
+ }, P = 1.5, ne = (e) => e === "min-max" || typeof e == "number" && Number.isFinite(e) && e >= 0, Ve = 1.58, Le = (e, t) => {
66
+ if (e.length === 0) return null;
67
+ const r = [...e].sort((p, g) => p - g), o = D(r, 0.25), n = D(r, 0.5), a = D(r, 0.75), s = t === "min-max" ? 1 / 0 : t * (a - o), i = o - s, u = a + s, l = r.filter((p) => p >= i && p <= u), h = Ve * (a - o) / Math.sqrt(e.length);
68
+ return {
69
+ lowerWhisker: Math.min(l[0] ?? o, o),
70
+ q1: o,
71
+ median: n,
72
+ q3: a,
73
+ upperWhisker: Math.max(l[l.length - 1] ?? a, a),
74
+ count: e.length,
75
+ mean: r.reduce((p, g) => p + g, 0) / r.length,
76
+ notchLower: n - h,
77
+ notchUpper: n + h
78
+ };
79
+ }, D = (e, t) => {
80
+ const r = (e.length - 1) * t, o = Math.floor(r), n = e[o] ?? 0;
81
+ return n + ((e[o + 1] ?? n) - n) * (r - o);
82
+ }, ae = {
83
+ q1: y.q1,
84
+ median: y.median,
85
+ q3: y.q3,
86
+ lowerWhisker: y.lowerWhisker,
87
+ upperWhisker: y.upperWhisker,
88
+ count: y.count,
89
+ outlier: y.outlier
90
+ }, se = /* @__PURE__ */ new Set([
91
+ ...Object.keys(ae),
92
+ "mean",
93
+ "notchLower",
94
+ "notchUpper"
95
+ ]), Ce = class extends xe {
96
+ constructor(...e) {
97
+ super(...e), this.type = "boxplot", this.computedVariables = se;
98
+ }
99
+ computeStat({ data: e, mapping: t, params: r }) {
100
+ const o = N(t.x), n = N(t.y);
101
+ if (o === null || n === null || !e.hasVariable(o) || !e.hasVariable(n)) return {
102
+ data: e,
103
+ mapping: {}
104
+ };
105
+ if (e.size() > 0 && e.getType(n) !== "numeric") {
106
+ const s = e.getType(n);
107
+ throw new We({
108
+ code: "INCOMPATIBLE_TYPE",
109
+ message: `'boxplot' stat requires a numeric 'y' (got '${s}' for variable '${n}')`,
110
+ context: {
111
+ aesthetic: "y",
112
+ variableName: n,
113
+ variableType: s
114
+ },
115
+ suggestion: "Map the raw values to 'y', or summarised columns under stat 'identity'."
116
+ });
117
+ }
118
+ const a = r?.extent;
119
+ return {
120
+ data: De({
121
+ data: e,
122
+ xVariable: o,
123
+ yVariable: n,
124
+ groupVariables: Ee(e, t).filter((s) => s !== o),
125
+ extent: ne(a) ? a : P
126
+ }),
127
+ mapping: ae
128
+ };
129
+ }
130
+ }, De = ({ data: e, xVariable: t, yVariable: r, groupVariables: o, extent: n }) => {
131
+ const a = Ke(e, t, r, o), s = e.getVariableNames(), i = new Map(s.map((d) => [d, e.getValues(d)])), u = new Map(s.map((d) => [d, []])), l = {
132
+ q1: [],
133
+ median: [],
134
+ q3: [],
135
+ lowerWhisker: [],
136
+ upperWhisker: [],
137
+ count: [],
138
+ mean: [],
139
+ notchLower: [],
140
+ notchUpper: [],
141
+ outlier: []
142
+ }, h = [];
143
+ for (const d of a) {
144
+ const m = Le(d.values, n);
145
+ if (m === null) continue;
146
+ for (const c of s) u.get(c)?.push(Re(i.get(c) ?? [], d.indices));
147
+ l.q1.push(m.q1), l.median.push(m.median), l.q3.push(m.q3), l.lowerWhisker.push(m.lowerWhisker), l.upperWhisker.push(m.upperWhisker), l.count.push(m.count), l.mean.push(m.mean), l.notchLower.push(m.notchLower), l.notchUpper.push(m.notchUpper), l.outlier.push(null);
148
+ const w = {
149
+ low: m.lowerWhisker,
150
+ high: m.upperWhisker
151
+ };
152
+ d.indices.forEach((c, f) => {
153
+ const W = d.values[f];
154
+ W !== void 0 && (W < w.low || W > w.high) && h.push({
155
+ index: c,
156
+ value: W
157
+ });
158
+ });
159
+ }
160
+ for (const { index: d, value: m } of h) {
161
+ for (const w of s) u.get(w)?.push(i.get(w)?.[d] ?? null);
162
+ l.q1.push(null), l.median.push(null), l.q3.push(null), l.lowerWhisker.push(null), l.upperWhisker.push(null), l.count.push(null), l.mean.push(null), l.notchLower.push(null), l.notchUpper.push(null), l.outlier.push(m);
163
+ }
164
+ const p = e.getValueFormat(r), g = Object.fromEntries(s.map((d) => [d, {
165
+ type: e.getType(d),
166
+ values: u.get(d) ?? [],
167
+ valueFormat: e.getValueFormat(d)
168
+ }]));
169
+ for (const [d, m] of Object.entries(l)) {
170
+ const w = y[d];
171
+ g[w] = d === "count" ? {
172
+ type: "numeric",
173
+ values: m
174
+ } : {
175
+ type: "numeric",
176
+ values: m,
177
+ valueFormat: p
178
+ };
179
+ }
180
+ return new me(g);
181
+ }, Ke = (e, t, r, o) => {
182
+ const n = [t, ...o].map((i) => e.getValues(i)), a = e.getValues(r, { type: "numeric" }), s = /* @__PURE__ */ new Map();
183
+ return a.forEach((i, u) => {
184
+ if (i === null || !Number.isFinite(i)) return;
185
+ const l = n.map((p) => H(p[u] ?? null)).join("\0"), h = s.get(l) ?? {
186
+ indices: [],
187
+ values: []
188
+ };
189
+ h.indices.push(u), h.values.push(i), s.set(l, h);
190
+ }), [...s.values()];
191
+ }, Re = (e, t) => {
192
+ const r = e[t[0] ?? -1] ?? null, o = H(r);
193
+ return t.every((n) => H(e[n] ?? null) === o) ? r : null;
194
+ }, H = (e) => e === null ? "\0null" : e instanceof Date ? `date:${e.getTime()}` : `${typeof e}:${String(e)}`, le = 0.6, Ue = 1.5, $e = 0.3, Ae = 2, z = 1.5, He = 6, Fe = 1.5, Ge = 7, Xe = 1.5, ze = [
195
+ "q1",
196
+ "median",
197
+ "q3"
198
+ ], Ye = { strokeWidth: "pixels" }, K = {
199
+ stroke: "color",
200
+ alpha: "unitInterval",
201
+ strokeWidth: "pixels",
202
+ dashArray: "dashArray"
203
+ }, Y = {
204
+ fill: "color",
205
+ fillAlpha: "unitInterval",
206
+ stroke: "color",
207
+ alpha: "unitInterval",
208
+ size: "pixels",
209
+ strokeWidth: "pixels"
210
+ }, je = class extends ve {
211
+ constructor(...e) {
212
+ super(...e), this.type = "boxplot", this.defaultParams = {
213
+ extent: P,
214
+ outliers: !0,
215
+ notch: !1,
216
+ showMean: !1,
217
+ varwidth: !1
218
+ }, this.defaultStat = new Ce(), this.derivedVariables = Object.values(y), this.positionRoles = [
219
+ {
220
+ axis: "x",
221
+ role: "point",
222
+ valueKind: "value"
223
+ },
224
+ {
225
+ axis: "y",
226
+ role: "min",
227
+ valueKind: "value",
228
+ aes: "lowerWhisker"
229
+ },
230
+ {
231
+ axis: "y",
232
+ role: "max",
233
+ valueKind: "value",
234
+ aes: "upperWhisker"
235
+ },
236
+ {
237
+ axis: "y",
238
+ role: "scalar",
239
+ valueKind: "value",
240
+ aes: "q1"
241
+ },
242
+ {
243
+ axis: "y",
244
+ role: "scalar",
245
+ valueKind: "value",
246
+ aes: "median"
247
+ },
248
+ {
249
+ axis: "y",
250
+ role: "scalar",
251
+ valueKind: "value",
252
+ aes: "q3"
253
+ },
254
+ {
255
+ axis: "y",
256
+ role: "scalar",
257
+ valueKind: "value",
258
+ aes: "outlier"
259
+ },
260
+ {
261
+ axis: "y",
262
+ role: "scalar",
263
+ valueKind: "value",
264
+ aes: "mean"
265
+ },
266
+ {
267
+ axis: "y",
268
+ role: "scalar",
269
+ valueKind: "value",
270
+ aes: "notchLower"
271
+ },
272
+ {
273
+ axis: "y",
274
+ role: "scalar",
275
+ valueKind: "value",
276
+ aes: "notchUpper"
277
+ }
278
+ ], this.aesthetics = [
279
+ {
280
+ kind: "visual",
281
+ name: "color"
282
+ },
283
+ {
284
+ kind: "data",
285
+ name: "y"
286
+ },
287
+ {
288
+ kind: "data",
289
+ name: "count"
290
+ }
291
+ ], this.defaultPosition = "dodge", this.supportedPositions = ["identity", "dodge"], this.supportedCoordTypes = ["cartesian", "flip"], this.scaleConstraints = { discreteMainAxis: !0 }, this.legend = { suppressWhenSingleItem: !0 }, this.spatialKind = "rects", this.highlightStrategy = "observation-rerender", this.tooltip = [
292
+ {
293
+ key: "Upper whisker",
294
+ aes: "upperWhisker",
295
+ optional: !0
296
+ },
297
+ {
298
+ key: "Q3",
299
+ aes: "q3",
300
+ optional: !0
301
+ },
302
+ {
303
+ key: "Median",
304
+ aes: "median",
305
+ optional: !0
306
+ },
307
+ {
308
+ key: "Q1",
309
+ aes: "q1",
310
+ optional: !0
311
+ },
312
+ {
313
+ key: "Lower whisker",
314
+ aes: "lowerWhisker",
315
+ optional: !0
316
+ },
317
+ {
318
+ key: "Count",
319
+ aes: "count",
320
+ optional: !0
321
+ },
322
+ {
323
+ key: "Mean",
324
+ aes: "mean",
325
+ optional: !0
326
+ },
327
+ {
328
+ key: "Outlier",
329
+ aes: "outlier",
330
+ optional: !0
331
+ }
332
+ ], this.styleTarget = be({
333
+ observation: {
334
+ vocabulary: Ye,
335
+ aesthetics: {
336
+ fill: "color",
337
+ stroke: "color"
338
+ },
339
+ rest: {
340
+ fill: M("geom"),
341
+ fillAlpha: $e,
342
+ stroke: M("geom"),
343
+ strokeWidth: Ue
344
+ }
345
+ },
346
+ parts: {
347
+ median: {
348
+ vocabulary: K,
349
+ aesthetics: { stroke: "color" },
350
+ rest: {
351
+ stroke: M("geom"),
352
+ alpha: 1,
353
+ strokeWidth: Ae,
354
+ dashArray: []
355
+ }
356
+ },
357
+ whisker: {
358
+ vocabulary: K,
359
+ aesthetics: { stroke: "color" },
360
+ rest: {
361
+ stroke: M("geom"),
362
+ alpha: 1,
363
+ strokeWidth: z,
364
+ dashArray: []
365
+ }
366
+ },
367
+ cap: {
368
+ vocabulary: K,
369
+ aesthetics: { stroke: "color" },
370
+ rest: {
371
+ stroke: M("geom"),
372
+ alpha: 1,
373
+ strokeWidth: z,
374
+ dashArray: []
375
+ }
376
+ },
377
+ outlier: {
378
+ vocabulary: Y,
379
+ aesthetics: {
380
+ fill: "color",
381
+ stroke: "color"
382
+ },
383
+ rest: {
384
+ fill: M("geom"),
385
+ fillAlpha: 0,
386
+ stroke: M("geom"),
387
+ alpha: 1,
388
+ size: He,
389
+ strokeWidth: Fe
390
+ }
391
+ },
392
+ mean: {
393
+ vocabulary: Y,
394
+ aesthetics: { fill: "color" },
395
+ rest: {
396
+ fill: M("geom"),
397
+ fillAlpha: 1,
398
+ stroke: M("graphBackground"),
399
+ alpha: 1,
400
+ size: Ge,
401
+ strokeWidth: Xe
402
+ }
403
+ }
404
+ }
405
+ }), this.resolveBandFraction = (t) => j(t), this.resolveValueSource = (t) => t.median ?? { variable: y.median }, this.validateMapping = ({ mapping: t, computedVariables: r }) => r.has("y") ? [{
406
+ code: "CONFLICTING_STAT_MAPPING",
407
+ message: "boxplot geom draws a summary, not a single 'y'; use the 'boxplot' stat or 'identity'"
408
+ }] : r.has("median") ? [...[...se].filter((o) => t[o] !== void 0).map((o) => ({
409
+ code: "CONFLICTING_STAT_MAPPING",
410
+ message: `boxplot stat computes '${o}' from 'y'; remove its mapping, or set the layer's stat to 'identity'`
411
+ })), ...t.y === void 0 ? [{
412
+ code: "MISSING_AESTHETIC",
413
+ message: "boxplot stat summarises 'y'; map it to the raw values"
414
+ }] : []] : ze.filter((o) => t[o] === void 0).map((o) => ({
415
+ code: "MISSING_AESTHETIC",
416
+ message: `boxplot geom requires '${o}' when its data is already summarised`,
417
+ context: { aesthetic: o }
418
+ })), this.resolveAnchorPosition = (t, { coordSystem: r }) => {
419
+ const o = E(r), n = Qe(t, o);
420
+ return n === null ? null : o === "x" ? {
421
+ x: n.band,
422
+ y: n.value
423
+ } : {
424
+ x: n.value,
425
+ y: n.band
426
+ };
427
+ };
428
+ }
429
+ resolveParams(e) {
430
+ const { extent: t, width: r, ...o } = super.resolveParams(e);
431
+ return {
432
+ ...o,
433
+ extent: this.resolveExtent(t, e.diagnostics),
434
+ ...this.resolveWidth(r, e.diagnostics)
435
+ };
436
+ }
437
+ compile({ data: e, mapping: t, params: r }) {
438
+ const o = q(e, t.lowerWhisker), n = q(e, t.upperWhisker);
439
+ if (o === null || n === null) return {
440
+ data: e,
441
+ mapping: {}
442
+ };
443
+ const { outliers: a, notch: s, showMean: i, varwidth: u } = r, l = q(e, t.outlier), h = a === !1 && l !== null ? et(e, l) : e, p = q(h, t.count), g = j(r) / 2;
444
+ return {
445
+ data: h.addVariable(y.relativeWidth, "numeric", u === !0 && p !== null ? Ze(h, p) : Je(h)).addVariable(B.yMin, "numeric", Q(h, o, l)).addVariable(B.yMax, "numeric", Q(h, n, l)).addConstantVariable(B.xMin, "numeric", -g).addConstantVariable(B.xMax, "numeric", g),
446
+ mapping: {
447
+ ...t.y === void 0 && { y: t.median },
448
+ ...i === !0 && t.mean === void 0 && h.hasVariable(y.mean) && { mean: y.mean },
449
+ ...s === !0 && t.notchLower === void 0 && h.hasVariable(y.notchLower) && {
450
+ notchLower: y.notchLower,
451
+ notchUpper: y.notchUpper
452
+ }
453
+ }
454
+ };
455
+ }
456
+ resolveExtent(e, t) {
457
+ return ne(e) ? e : (t?.addWarning({
458
+ code: "INVALID_GEOM_PARAM",
459
+ message: `Boxplot extent must be a non-negative number or 'min-max', got ${String(e)}; falling back to ${P}.`,
460
+ context: {
461
+ geom: this.type,
462
+ param: "extent",
463
+ expected: "extent >= 0 or 'min-max'",
464
+ actual: String(e)
465
+ },
466
+ suggestion: "Set params.extent to a number of interquartile ranges, or 'min-max'."
467
+ }), P);
468
+ }
469
+ resolveWidth(e, t) {
470
+ if (e === void 0) return {};
471
+ const r = typeof e == "number" && Number.isFinite(e) ? e : null;
472
+ if (r !== null && r > 0 && r <= 1) return { width: r };
473
+ const o = r !== null && r > 1 ? 1 : void 0;
474
+ return t?.addWarning({
475
+ code: "INVALID_GEOM_PARAM",
476
+ message: `Boxplot width must be a fraction in (0, 1], got ${String(e)}; falling back to ${o ?? le}.`,
477
+ context: {
478
+ geom: this.type,
479
+ param: "width",
480
+ expected: "0 < width <= 1",
481
+ actual: r ?? String(e)
482
+ },
483
+ suggestion: "Set params.width to a fraction of the category band in (0, 1]."
484
+ }), o !== void 0 ? { width: o } : {};
485
+ }
486
+ }, Qe = (e, t) => {
487
+ const r = re(e, t);
488
+ if (r !== null) return {
489
+ band: V(r),
490
+ value: r.median
491
+ };
492
+ const o = oe(e, t);
493
+ return o === null ? null : {
494
+ band: o.band,
495
+ value: o.value
496
+ };
497
+ }, j = ({ width: e }) => e ?? le, Ze = (e, t) => {
498
+ const r = e.getValues(t, { type: "numeric" }), o = Math.max(0, ...r.map((n) => n ?? 0));
499
+ return r.map((n) => n === null || o === 0 ? null : Math.sqrt(n / o));
500
+ }, Je = (e) => Array.from({ length: e.size() }, () => null), Q = (e, t, r) => {
501
+ const o = r === null ? [] : e.getValues(r, { type: "numeric" });
502
+ return e.getValues(t, { type: "numeric" }).map((n, a) => n ?? o[a] ?? null);
503
+ }, et = (e, t) => {
504
+ const r = e.getValues(t, { type: "numeric" });
505
+ return e.take(r.flatMap((o, n) => o === null ? [n] : []));
506
+ }, q = (e, t) => {
507
+ const r = N(t);
508
+ return r !== null && e.hasVariable(r) ? r : null;
509
+ }, tt = 5e-4, rt = 0.5, ot = 0.5, ie = (e, t) => {
510
+ const r = (e.q1 + e.q3) / 2, o = Math.max(Math.abs(e.q3 - e.q1), tt) / 2, n = v(t, e.start, r - o), a = v(t, e.end, r + o), [s, i] = Z(n.x, a.x), [u, l] = Z(n.y, a.y);
511
+ return {
512
+ x: s,
513
+ y: u,
514
+ width: i - s,
515
+ height: l - u
516
+ };
517
+ }, ue = (e, t) => {
518
+ if (e.notch === null) return null;
519
+ const { lower: r, upper: o } = e.notch, [n, a] = he(e);
520
+ return [
521
+ v(t, e.start, e.q1),
522
+ v(t, e.start, r),
523
+ v(t, n, e.median),
524
+ v(t, e.start, o),
525
+ v(t, e.start, e.q3),
526
+ v(t, e.end, e.q3),
527
+ v(t, e.end, o),
528
+ v(t, a, e.median),
529
+ v(t, e.end, r),
530
+ v(t, e.end, e.q1)
531
+ ];
532
+ }, nt = (e, t) => {
533
+ const [r, o] = e.notch === null ? [e.start, e.end] : he(e);
534
+ return {
535
+ from: v(t, r, e.median),
536
+ to: v(t, o, e.median)
537
+ };
538
+ }, ce = (e, t) => e.mean === null ? null : v(t, V(e), e.mean), he = (e) => {
539
+ const t = (e.end - e.start) * (1 - ot) / 2;
540
+ return [e.start + t, e.end - t];
541
+ }, de = (e, t) => {
542
+ const r = V(e), o = (e.end - e.start) * rt / 2;
543
+ return [{
544
+ side: "lower",
545
+ quartile: e.q1,
546
+ reach: e.lowerWhisker
547
+ }, {
548
+ side: "upper",
549
+ quartile: e.q3,
550
+ reach: e.upperWhisker
551
+ }].filter(({ quartile: n, reach: a }) => n !== a).map(({ side: n, quartile: a, reach: s }) => ({
552
+ side: n,
553
+ stem: {
554
+ from: v(t, r, a),
555
+ to: v(t, r, s)
556
+ },
557
+ cap: {
558
+ from: v(t, r - o, s),
559
+ to: v(t, r + o, s)
560
+ }
561
+ }));
562
+ }, v = (e, t, r) => {
563
+ const o = Se(e, {
564
+ main: t,
565
+ cross: r
566
+ });
567
+ return {
568
+ x: _e(o.x),
569
+ y: Be(o.y)
570
+ };
571
+ }, Z = (e, t) => e <= t ? [e, t] : [t, e], _ = (e, t) => ({
572
+ x: e.x * t.width,
573
+ y: e.y * t.height
574
+ }), F = (e, t) => `${e.map((r, o) => {
575
+ const { x: n, y: a } = _(r, t);
576
+ return `${o === 0 ? "M" : "L"}${n},${a}`;
577
+ }).join("")}Z`, at = (e, t) => F([
578
+ {
579
+ x: e.x,
580
+ y: e.y
581
+ },
582
+ {
583
+ x: e.x + e.width,
584
+ y: e.y
585
+ },
586
+ {
587
+ x: e.x + e.width,
588
+ y: e.y + e.height
589
+ },
590
+ {
591
+ x: e.x,
592
+ y: e.y + e.height
593
+ }
594
+ ], t), st = (e, t) => {
595
+ const r = _(e.from, t), o = _(e.to, t);
596
+ return `M${r.x},${r.y}L${o.x},${o.y}`;
597
+ }, lt = ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o }) => {
598
+ const n = E(t), { boxes: a, outliers: s } = I(e.data, n), i = a.map((c) => {
599
+ const f = ue(c, n);
600
+ return f !== null ? {
601
+ kind: "region",
602
+ pathData: F(f, r)
603
+ } : {
604
+ kind: "region",
605
+ pathData: at(ie(c, n), r)
606
+ };
607
+ }), u = o.part("whisker"), l = o.part("cap"), h = a.flatMap((c) => de(c, n).flatMap((f) => [{
608
+ segment: f.stem,
609
+ readers: u
610
+ }, {
611
+ segment: f.cap,
612
+ readers: l
613
+ }].flatMap(({ segment: W, readers: L }) => {
614
+ const O = L?.get("strokeWidth", c.observation) ?? 0;
615
+ return O > 0 ? [{
616
+ kind: "stroke",
617
+ pathData: st(W, r),
618
+ width: O
619
+ }] : [];
620
+ }))), p = o.part("outlier"), g = p === void 0 ? [] : s.map((c) => ({
621
+ center: _(v(n, c.band, c.value), r),
622
+ size: J(p, c.observation)
623
+ })), d = o.part("mean"), m = d === void 0 ? [] : a.flatMap((c) => {
624
+ const f = ce(c, n);
625
+ return f === null ? [] : [{
626
+ center: _(f, r),
627
+ size: J(d, c.observation)
628
+ }];
629
+ }), w = Me([...g, ...m]);
630
+ return [
631
+ ...h,
632
+ ...i,
633
+ ...w
634
+ ];
635
+ }, J = (e, t) => (e.get("size", t) ?? 0) + (e.get("strokeWidth", t) ?? 0), G = (e) => {
636
+ const t = Ie(e.data, e.mapping, e.id), r = N(e.mapping.outlier), o = (n) => `${t(n.observation)}-outlier-${String(r === null ? n.value : n.observation[r])}`;
637
+ return {
638
+ readBoxKey: t,
639
+ readOutlierKeys: (n) => {
640
+ const a = /* @__PURE__ */ new Map();
641
+ return n.map((s) => {
642
+ const i = o(s), u = a.get(i) ?? 0;
643
+ return a.set(i, u + 1), u === 0 ? i : `${i}-${u}`;
644
+ });
645
+ }
646
+ };
647
+ }, it = (e, t, r) => {
648
+ const o = e.data.getVariableNames(), n = (s) => JSON.stringify(o.map((i) => s.observation[i] ?? null)), a = new Set(r.map(n));
649
+ return t.filter((s) => a.has(n(s)));
650
+ }, pe = (e, t, r) => {
651
+ const o = new Set(t.map((n) => r(n.observation)));
652
+ return e.filter((n) => o.has(r(n.observation)));
653
+ }, X = ({ boxes: e, outliers: t, mainAxis: r, panelSize: o, source: n, styleReaders: a, keys: s, isHovered: i = !1, shouldAnimateTransitions: u = !1 }) => {
654
+ const l = i ? "hovered" : void 0, h = a.part("whisker"), p = a.part("cap"), g = a.part("median"), d = a.part("mean"), m = a.part("outlier"), w = s.readOutlierKeys(t);
655
+ return /* @__PURE__ */ Pe(qe, { children: [
656
+ e.flatMap((c) => de(c, r).flatMap((f) => [{
657
+ piece: "stem",
658
+ testId: "boxplot-whisker",
659
+ readers: h
660
+ }, {
661
+ piece: "cap",
662
+ testId: "boxplot-cap",
663
+ readers: p
664
+ }].map(({ piece: W, testId: L, readers: O }) => O === void 0 ? null : /* @__PURE__ */ k(ee, {
665
+ testId: L,
666
+ segment: f[W],
667
+ observation: c.observation,
668
+ readers: O,
669
+ state: l,
670
+ shouldAnimateTransitions: u
671
+ }, `${s.readBoxKey(c.observation)}-${f.side}-${W}`)))),
672
+ e.map((c) => /* @__PURE__ */ k(ut, {
673
+ box: c,
674
+ mainAxis: r,
675
+ panelSize: o,
676
+ source: n,
677
+ styleReaders: a,
678
+ state: l,
679
+ shouldAnimateTransitions: u
680
+ }, s.readBoxKey(c.observation))),
681
+ g && e.map((c) => /* @__PURE__ */ k(ee, {
682
+ testId: "boxplot-median",
683
+ segment: nt(c, r),
684
+ observation: c.observation,
685
+ readers: g,
686
+ state: l,
687
+ shouldAnimateTransitions: u
688
+ }, s.readBoxKey(c.observation))),
689
+ d && e.map((c) => {
690
+ const f = ce(c, r);
691
+ return f === null ? null : /* @__PURE__ */ k(te, {
692
+ testId: "boxplot-mean",
693
+ centre: f,
694
+ observation: c.observation,
695
+ readers: d,
696
+ state: l,
697
+ shouldAnimateTransitions: u
698
+ }, s.readBoxKey(c.observation));
699
+ }),
700
+ m && t.map((c, f) => /* @__PURE__ */ k(te, {
701
+ testId: "boxplot-outlier",
702
+ centre: v(r, c.band, c.value),
703
+ observation: c.observation,
704
+ readers: m,
705
+ state: l,
706
+ shouldAnimateTransitions: u
707
+ }, w[f]))
708
+ ] });
709
+ }, ee = ({ testId: e, segment: t, observation: r, readers: o, state: n, shouldAnimateTransitions: a }) => {
710
+ const s = o.get("strokeWidth", r, n) ?? 0, i = o.get("stroke", r, n);
711
+ return /* @__PURE__ */ k(fe, {
712
+ "data-testid": e,
713
+ x1: x(t.from.x),
714
+ y1: x(t.from.y),
715
+ x2: x(t.to.x),
716
+ y2: x(t.to.y),
717
+ stroke: i === void 0 ? void 0 : T(i),
718
+ strokeOpacity: o.get("alpha", r, n),
719
+ strokeWidth: s,
720
+ strokeDasharray: Te(o.get("dashArray", r, n), s),
721
+ shouldAnimateTransitions: a
722
+ });
723
+ }, ut = ({ box: e, mainAxis: t, panelSize: r, source: o, styleReaders: n, state: a, shouldAnimateTransitions: s }) => {
724
+ const i = n.get("fill", e.observation, a), u = n.get("stroke", e.observation, a), l = {
725
+ fill: i === void 0 ? void 0 : T(i),
726
+ opacity: n.get("alpha", e.observation, a),
727
+ fillOpacity: n.get("fillAlpha", e.observation, a),
728
+ stroke: u === void 0 ? void 0 : T(u),
729
+ strokeOpacity: n.get("strokeAlpha", e.observation, a),
730
+ strokeWidth: n.get("strokeWidth", e.observation, a),
731
+ shouldAnimateTransitions: s
732
+ }, h = ue(e, t);
733
+ if (h !== null) return /* @__PURE__ */ k(ye, {
734
+ "data-testid": "boxplot-box",
735
+ d: F(h, r),
736
+ source: o,
737
+ ...l
738
+ });
739
+ const p = ie(e, t);
740
+ return /* @__PURE__ */ k(ke, {
741
+ "data-testid": "boxplot-box",
742
+ x: x(p.x),
743
+ y: x(p.y),
744
+ width: x(p.width),
745
+ height: x(p.height),
746
+ ...l
747
+ });
748
+ }, te = ({ testId: e, centre: t, observation: r, readers: o, state: n, shouldAnimateTransitions: a }) => {
749
+ const s = o.get("fill", r, n), i = o.get("stroke", r, n);
750
+ return /* @__PURE__ */ k(ge, {
751
+ "data-testid": e,
752
+ cx: x(t.x),
753
+ cy: x(t.y),
754
+ r: (o.get("size", r, n) ?? 0) / 2,
755
+ fill: s === void 0 ? void 0 : T(s),
756
+ opacity: o.get("alpha", r, n),
757
+ fillOpacity: o.get("fillAlpha", r, n),
758
+ stroke: i === void 0 ? void 0 : T(i),
759
+ strokeWidth: o.get("strokeWidth", r, n),
760
+ shouldAnimateTransitions: a
761
+ });
762
+ }, ct = ({ layer: e, sourceLayer: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
763
+ const a = S(() => G(t), [t]), s = S(() => I(t.data, r).outliers, [t.data, r]), { boxes: i, outliers: u } = S(() => {
764
+ const l = I(e.data, r), h = new Set(it(t, s, l.outliers)), p = new Set(pe(s, l.boxes, a.readBoxKey)), g = s.filter((d) => p.has(d) || h.has(d));
765
+ return {
766
+ boxes: l.boxes,
767
+ outliers: g
768
+ };
769
+ }, [
770
+ e.data,
771
+ r,
772
+ t,
773
+ s,
774
+ a
775
+ ]);
776
+ return /* @__PURE__ */ k(X, {
777
+ boxes: i,
778
+ outliers: u,
779
+ mainAxis: r,
780
+ panelSize: o,
781
+ source: t,
782
+ styleReaders: n,
783
+ keys: a
784
+ });
785
+ }, ht = ({ layer: e, primary: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
786
+ const a = S(() => I(e.data, r).outliers, [e.data, r]), s = S(() => G(e), [e]), { boxes: i, outliers: u } = I([t.observation], r);
787
+ return /* @__PURE__ */ k("g", {
788
+ "data-testid": "hovered-boxes",
789
+ children: /* @__PURE__ */ k(X, {
790
+ boxes: i,
791
+ outliers: i.length > 0 ? pe(a, i, s.readBoxKey) : u,
792
+ mainAxis: r,
793
+ panelSize: o,
794
+ source: e,
795
+ styleReaders: n,
796
+ keys: s,
797
+ isHovered: !0
798
+ })
799
+ });
800
+ }, dt = ({ layer: e, mainAxis: t, panelSize: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => {
801
+ const { boxes: s, outliers: i } = S(() => I(e.data, t), [e.data, t]), u = /* @__PURE__ */ k(X, {
802
+ boxes: s,
803
+ outliers: i,
804
+ mainAxis: t,
805
+ panelSize: r,
806
+ source: e,
807
+ styleReaders: o,
808
+ keys: S(() => G(e), [e]),
809
+ shouldAnimateTransitions: n
810
+ });
811
+ return a == null ? u : /* @__PURE__ */ k(we, {
812
+ intro: {
813
+ type: "fade",
814
+ layerId: a.layerId,
815
+ durationSeconds: a.durationSeconds
816
+ },
817
+ children: u
818
+ });
819
+ }, gt = Oe(new je(), {
820
+ coord: "cartesian",
821
+ swatchShape: "square",
822
+ guideMode: "band",
823
+ render: ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => /* @__PURE__ */ k(dt, {
824
+ layer: e,
825
+ mainAxis: E(t),
826
+ panelSize: r,
827
+ styleReaders: o,
828
+ shouldAnimateTransitions: n,
829
+ intro: a
830
+ }),
831
+ renderHighlight: ({ layer: e, sourceLayer: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ k(ct, {
832
+ layer: e,
833
+ sourceLayer: t,
834
+ mainAxis: E(r),
835
+ panelSize: o,
836
+ styleReaders: n
837
+ }),
838
+ renderHover: ({ layer: e, primary: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ k(ht, {
839
+ layer: e,
840
+ primary: t,
841
+ mainAxis: E(r),
842
+ panelSize: o,
843
+ styleReaders: n
844
+ }),
845
+ renderHoverCompanions: () => null,
846
+ getEditOutlineShapes: lt
847
+ });
848
+ export {
849
+ y as BOXPLOT_COLUMNS,
850
+ je as BoxplotGeom,
851
+ gt as boxplot
852
+ };