@voila.dev/chart 4.0.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/README.md +85 -0
- package/dist/brush/brush-layer.d.ts +16 -0
- package/dist/brush/brush-layer.d.ts.map +1 -0
- package/dist/brush/brush-layer.js +137 -0
- package/dist/brush/brush-layer.js.map +7 -0
- package/dist/brush/index.d.ts +10 -0
- package/dist/brush/index.d.ts.map +1 -0
- package/dist/brush/index.js +8 -0
- package/dist/brush/index.js.map +7 -0
- package/dist/canvas/canvas-renderer.d.ts +11 -0
- package/dist/canvas/canvas-renderer.d.ts.map +1 -0
- package/dist/canvas/canvas-renderer.js +49 -0
- package/dist/canvas/canvas-renderer.js.map +7 -0
- package/dist/canvas/index.d.ts +2 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +5 -0
- package/dist/canvas/index.js.map +7 -0
- package/dist/canvas/paint-scene.d.ts +4 -0
- package/dist/canvas/paint-scene.d.ts.map +1 -0
- package/dist/canvas/paint-scene.js +130 -0
- package/dist/canvas/paint-scene.js.map +7 -0
- package/dist/canvas/resolve-color.d.ts +18 -0
- package/dist/canvas/resolve-color.d.ts.map +1 -0
- package/dist/canvas/resolve-color.js +52 -0
- package/dist/canvas/resolve-color.js.map +7 -0
- package/dist/contour/density.d.ts +20 -0
- package/dist/contour/density.d.ts.map +1 -0
- package/dist/contour/density.js +59 -0
- package/dist/contour/density.js.map +7 -0
- package/dist/contour/index.d.ts +2 -0
- package/dist/contour/index.d.ts.map +1 -0
- package/dist/contour/index.js +5 -0
- package/dist/contour/index.js.map +7 -0
- package/dist/core/channel.d.ts +11 -0
- package/dist/core/channel.d.ts.map +1 -0
- package/dist/core/channel.js +44 -0
- package/dist/core/channel.js.map +7 -0
- package/dist/core/compile.d.ts +22 -0
- package/dist/core/compile.d.ts.map +1 -0
- package/dist/core/compile.js +295 -0
- package/dist/core/compile.js.map +7 -0
- package/dist/core/define-chart.d.ts +96 -0
- package/dist/core/define-chart.d.ts.map +1 -0
- package/dist/core/define-chart.js +7 -0
- package/dist/core/define-chart.js.map +7 -0
- package/dist/core/facet.d.ts +7 -0
- package/dist/core/facet.d.ts.map +1 -0
- package/dist/core/facet.js +134 -0
- package/dist/core/facet.js.map +7 -0
- package/dist/core/format.d.ts +37 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +108 -0
- package/dist/core/format.js.map +7 -0
- package/dist/core/guides/axes.d.ts +25 -0
- package/dist/core/guides/axes.d.ts.map +1 -0
- package/dist/core/guides/axes.js +204 -0
- package/dist/core/guides/axes.js.map +7 -0
- package/dist/core/marks/area.d.ts +23 -0
- package/dist/core/marks/area.d.ts.map +1 -0
- package/dist/core/marks/area.js +187 -0
- package/dist/core/marks/area.js.map +7 -0
- package/dist/core/marks/bar.d.ts +26 -0
- package/dist/core/marks/bar.d.ts.map +1 -0
- package/dist/core/marks/bar.js +200 -0
- package/dist/core/marks/bar.js.map +7 -0
- package/dist/core/marks/cell.d.ts +16 -0
- package/dist/core/marks/cell.d.ts.map +1 -0
- package/dist/core/marks/cell.js +98 -0
- package/dist/core/marks/cell.js.map +7 -0
- package/dist/core/marks/dot.d.ts +17 -0
- package/dist/core/marks/dot.d.ts.map +1 -0
- package/dist/core/marks/dot.js +125 -0
- package/dist/core/marks/dot.js.map +7 -0
- package/dist/core/marks/funnel-layout.d.ts +37 -0
- package/dist/core/marks/funnel-layout.d.ts.map +1 -0
- package/dist/core/marks/funnel-layout.js +61 -0
- package/dist/core/marks/funnel-layout.js.map +7 -0
- package/dist/core/marks/funnel.d.ts +19 -0
- package/dist/core/marks/funnel.d.ts.map +1 -0
- package/dist/core/marks/funnel.js +143 -0
- package/dist/core/marks/funnel.js.map +7 -0
- package/dist/core/marks/line.d.ts +17 -0
- package/dist/core/marks/line.d.ts.map +1 -0
- package/dist/core/marks/line.js +150 -0
- package/dist/core/marks/line.js.map +7 -0
- package/dist/core/marks/polar.d.ts +61 -0
- package/dist/core/marks/polar.d.ts.map +1 -0
- package/dist/core/marks/polar.js +436 -0
- package/dist/core/marks/polar.js.map +7 -0
- package/dist/core/marks/rect.d.ts +22 -0
- package/dist/core/marks/rect.d.ts.map +1 -0
- package/dist/core/marks/rect.js +119 -0
- package/dist/core/marks/rect.js.map +7 -0
- package/dist/core/marks/rule.d.ts +17 -0
- package/dist/core/marks/rule.d.ts.map +1 -0
- package/dist/core/marks/rule.js +73 -0
- package/dist/core/marks/rule.js.map +7 -0
- package/dist/core/marks/shared.d.ts +41 -0
- package/dist/core/marks/shared.d.ts.map +1 -0
- package/dist/core/marks/shared.js +74 -0
- package/dist/core/marks/shared.js.map +7 -0
- package/dist/core/marks/stack.d.ts +13 -0
- package/dist/core/marks/stack.d.ts.map +1 -0
- package/dist/core/marks/stack.js +28 -0
- package/dist/core/marks/stack.js.map +7 -0
- package/dist/core/marks/stats/difference.d.ts +20 -0
- package/dist/core/marks/stats/difference.d.ts.map +1 -0
- package/dist/core/marks/stats/difference.js +168 -0
- package/dist/core/marks/stats/difference.js.map +7 -0
- package/dist/core/marks/stats/distribution.d.ts +41 -0
- package/dist/core/marks/stats/distribution.d.ts.map +1 -0
- package/dist/core/marks/stats/distribution.js +354 -0
- package/dist/core/marks/stats/distribution.js.map +7 -0
- package/dist/core/marks/stats/dodge.d.ts +16 -0
- package/dist/core/marks/stats/dodge.d.ts.map +1 -0
- package/dist/core/marks/stats/dodge.js +104 -0
- package/dist/core/marks/stats/dodge.js.map +7 -0
- package/dist/core/marks/stats/hexbin.d.ts +26 -0
- package/dist/core/marks/stats/hexbin.d.ts.map +1 -0
- package/dist/core/marks/stats/hexbin.js +113 -0
- package/dist/core/marks/stats/hexbin.js.map +7 -0
- package/dist/core/marks/stats/histogram.d.ts +27 -0
- package/dist/core/marks/stats/histogram.d.ts.map +1 -0
- package/dist/core/marks/stats/histogram.js +70 -0
- package/dist/core/marks/stats/histogram.js.map +7 -0
- package/dist/core/marks/stats/regression.d.ts +13 -0
- package/dist/core/marks/stats/regression.d.ts.map +1 -0
- package/dist/core/marks/stats/regression.js +80 -0
- package/dist/core/marks/stats/regression.js.map +7 -0
- package/dist/core/marks/stats/statistics.d.ts +34 -0
- package/dist/core/marks/stats/statistics.d.ts.map +1 -0
- package/dist/core/marks/stats/statistics.js +111 -0
- package/dist/core/marks/stats/statistics.js.map +7 -0
- package/dist/core/marks/stats/waffle.d.ts +18 -0
- package/dist/core/marks/stats/waffle.d.ts.map +1 -0
- package/dist/core/marks/stats/waffle.js +117 -0
- package/dist/core/marks/stats/waffle.js.map +7 -0
- package/dist/core/marks/text.d.ts +19 -0
- package/dist/core/marks/text.d.ts.map +1 -0
- package/dist/core/marks/text.js +60 -0
- package/dist/core/marks/text.js.map +7 -0
- package/dist/core/motion/tween.d.ts +6 -0
- package/dist/core/motion/tween.d.ts.map +1 -0
- package/dist/core/motion/tween.js +98 -0
- package/dist/core/motion/tween.js.map +7 -0
- package/dist/core/nearest.d.ts +25 -0
- package/dist/core/nearest.d.ts.map +1 -0
- package/dist/core/nearest.js +151 -0
- package/dist/core/nearest.js.map +7 -0
- package/dist/core/paths.d.ts +65 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +192 -0
- package/dist/core/paths.js.map +7 -0
- package/dist/core/scales/color.d.ts +11 -0
- package/dist/core/scales/color.d.ts.map +1 -0
- package/dist/core/scales/color.js +35 -0
- package/dist/core/scales/color.js.map +7 -0
- package/dist/core/scales/continuous.d.ts +18 -0
- package/dist/core/scales/continuous.d.ts.map +1 -0
- package/dist/core/scales/continuous.js +82 -0
- package/dist/core/scales/continuous.js.map +7 -0
- package/dist/core/scales/discrete.d.ts +22 -0
- package/dist/core/scales/discrete.d.ts.map +1 -0
- package/dist/core/scales/discrete.js +103 -0
- package/dist/core/scales/discrete.js.map +7 -0
- package/dist/core/scales/resolve.d.ts +18 -0
- package/dist/core/scales/resolve.d.ts.map +1 -0
- package/dist/core/scales/resolve.js +112 -0
- package/dist/core/scales/resolve.js.map +7 -0
- package/dist/core/scales/time.d.ts +4 -0
- package/dist/core/scales/time.d.ts.map +1 -0
- package/dist/core/scales/time.js +140 -0
- package/dist/core/scales/time.js.map +7 -0
- package/dist/core/text.d.ts +3 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +29 -0
- package/dist/core/text.js.map +7 -0
- package/dist/core/theme.d.ts +8 -0
- package/dist/core/theme.d.ts.map +1 -0
- package/dist/core/theme.js +21 -0
- package/dist/core/theme.js.map +7 -0
- package/dist/core/ticks.d.ts +27 -0
- package/dist/core/ticks.d.ts.map +1 -0
- package/dist/core/ticks.js +76 -0
- package/dist/core/ticks.js.map +7 -0
- package/dist/core/types.d.ts +305 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +7 -0
- package/dist/force/force.d.ts +30 -0
- package/dist/force/force.d.ts.map +1 -0
- package/dist/force/force.js +143 -0
- package/dist/force/force.js.map +7 -0
- package/dist/force/index.d.ts +2 -0
- package/dist/force/index.d.ts.map +1 -0
- package/dist/force/index.js +7 -0
- package/dist/force/index.js.map +7 -0
- package/dist/geo/geo.d.ts +59 -0
- package/dist/geo/geo.d.ts.map +1 -0
- package/dist/geo/geo.js +231 -0
- package/dist/geo/geo.js.map +7 -0
- package/dist/geo/index.d.ts +2 -0
- package/dist/geo/index.d.ts.map +1 -0
- package/dist/geo/index.js +13 -0
- package/dist/geo/index.js.map +7 -0
- package/dist/hierarchy/hierarchy.d.ts +24 -0
- package/dist/hierarchy/hierarchy.d.ts.map +1 -0
- package/dist/hierarchy/hierarchy.js +279 -0
- package/dist/hierarchy/hierarchy.js.map +7 -0
- package/dist/hierarchy/index.d.ts +2 -0
- package/dist/hierarchy/index.d.ts.map +1 -0
- package/dist/hierarchy/index.js +11 -0
- package/dist/hierarchy/index.js.map +7 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +7 -0
- package/dist/react/brush-contract.d.ts +23 -0
- package/dist/react/brush-contract.d.ts.map +1 -0
- package/dist/react/brush-contract.js +7 -0
- package/dist/react/brush-contract.js.map +7 -0
- package/dist/react/chart-data-table.d.ts +18 -0
- package/dist/react/chart-data-table.d.ts.map +1 -0
- package/dist/react/chart-data-table.js +55 -0
- package/dist/react/chart-data-table.js.map +7 -0
- package/dist/react/chart-legend.d.ts +17 -0
- package/dist/react/chart-legend.d.ts.map +1 -0
- package/dist/react/chart-legend.js +126 -0
- package/dist/react/chart-legend.js.map +7 -0
- package/dist/react/chart-tooltip.d.ts +18 -0
- package/dist/react/chart-tooltip.d.ts.map +1 -0
- package/dist/react/chart-tooltip.js +95 -0
- package/dist/react/chart-tooltip.js.map +7 -0
- package/dist/react/chart.d.ts +45 -0
- package/dist/react/chart.d.ts.map +1 -0
- package/dist/react/chart.js +383 -0
- package/dist/react/chart.js.map +7 -0
- package/dist/react/focus-overlay.d.ts +14 -0
- package/dist/react/focus-overlay.d.ts.map +1 -0
- package/dist/react/focus-overlay.js +147 -0
- package/dist/react/focus-overlay.js.map +7 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/messages.d.ts +16 -0
- package/dist/react/messages.d.ts.map +1 -0
- package/dist/react/messages.js +33 -0
- package/dist/react/messages.js.map +7 -0
- package/dist/react/renderer.d.ts +14 -0
- package/dist/react/renderer.d.ts.map +1 -0
- package/dist/react/renderer.js +1 -0
- package/dist/react/renderer.js.map +7 -0
- package/dist/react/sr-only.d.ts +4 -0
- package/dist/react/sr-only.d.ts.map +1 -0
- package/dist/react/sr-only.js +15 -0
- package/dist/react/sr-only.js.map +7 -0
- package/dist/react/svg-renderer.d.ts +4 -0
- package/dist/react/svg-renderer.d.ts.map +1 -0
- package/dist/react/svg-renderer.js +20 -0
- package/dist/react/svg-renderer.js.map +7 -0
- package/dist/react/svg-scene.d.ts +8 -0
- package/dist/react/svg-scene.d.ts.map +1 -0
- package/dist/react/svg-scene.js +121 -0
- package/dist/react/svg-scene.js.map +7 -0
- package/dist/react/use-animated-scene.d.ts +10 -0
- package/dist/react/use-animated-scene.d.ts.map +1 -0
- package/dist/react/use-animated-scene.js +41 -0
- package/dist/react/use-animated-scene.js.map +7 -0
- package/dist/react/use-chart-focus.d.ts +29 -0
- package/dist/react/use-chart-focus.d.ts.map +1 -0
- package/dist/react/use-chart-focus.js +180 -0
- package/dist/react/use-chart-focus.js.map +7 -0
- package/dist/react/use-chart-width.d.ts +12 -0
- package/dist/react/use-chart-width.d.ts.map +1 -0
- package/dist/react/use-chart-width.js +23 -0
- package/dist/react/use-chart-width.js.map +7 -0
- package/dist/react/use-chart-zoom.d.ts +13 -0
- package/dist/react/use-chart-zoom.d.ts.map +1 -0
- package/dist/react/use-chart-zoom.js +52 -0
- package/dist/react/use-chart-zoom.js.map +7 -0
- package/dist/react/use-text-measurer.d.ts +8 -0
- package/dist/react/use-text-measurer.d.ts.map +1 -0
- package/dist/react/use-text-measurer.js +44 -0
- package/dist/react/use-text-measurer.js.map +7 -0
- package/dist/sankey/index.d.ts +2 -0
- package/dist/sankey/index.d.ts.map +1 -0
- package/dist/sankey/index.js +7 -0
- package/dist/sankey/index.js.map +7 -0
- package/dist/sankey/sankey.d.ts +29 -0
- package/dist/sankey/sankey.d.ts.map +1 -0
- package/dist/sankey/sankey.js +127 -0
- package/dist/sankey/sankey.js.map +7 -0
- package/dist/voronoi/index.d.ts +2 -0
- package/dist/voronoi/index.d.ts.map +1 -0
- package/dist/voronoi/index.js +5 -0
- package/dist/voronoi/index.js.map +7 -0
- package/dist/voronoi/voronoi.d.ts +16 -0
- package/dist/voronoi/voronoi.d.ts.map +1 -0
- package/dist/voronoi/voronoi.js +62 -0
- package/dist/voronoi/voronoi.js.map +7 -0
- package/package.json +74 -0
- package/src/brush/brush-layer.tsx +174 -0
- package/src/brush/index.ts +14 -0
- package/src/canvas/canvas-renderer.tsx +56 -0
- package/src/canvas/index.ts +1 -0
- package/src/canvas/paint-scene.ts +152 -0
- package/src/canvas/resolve-color.ts +65 -0
- package/src/contour/density.ts +90 -0
- package/src/contour/index.ts +1 -0
- package/src/core/channel.ts +53 -0
- package/src/core/compile.ts +436 -0
- package/src/core/define-chart.ts +128 -0
- package/src/core/facet.ts +189 -0
- package/src/core/format.ts +163 -0
- package/src/core/guides/axes.ts +283 -0
- package/src/core/marks/area.ts +238 -0
- package/src/core/marks/bar.ts +278 -0
- package/src/core/marks/cell.ts +123 -0
- package/src/core/marks/dot.ts +157 -0
- package/src/core/marks/funnel-layout.ts +113 -0
- package/src/core/marks/funnel.ts +172 -0
- package/src/core/marks/line.ts +206 -0
- package/src/core/marks/polar.ts +568 -0
- package/src/core/marks/rect.ts +156 -0
- package/src/core/marks/rule.ts +104 -0
- package/src/core/marks/shared.ts +134 -0
- package/src/core/marks/stack.ts +42 -0
- package/src/core/marks/stats/difference.ts +205 -0
- package/src/core/marks/stats/distribution.ts +425 -0
- package/src/core/marks/stats/dodge.ts +136 -0
- package/src/core/marks/stats/hexbin.ts +152 -0
- package/src/core/marks/stats/histogram.ts +108 -0
- package/src/core/marks/stats/regression.ts +108 -0
- package/src/core/marks/stats/statistics.ts +153 -0
- package/src/core/marks/stats/waffle.ts +141 -0
- package/src/core/marks/text.ts +97 -0
- package/src/core/motion/tween.ts +131 -0
- package/src/core/nearest.ts +223 -0
- package/src/core/paths.ts +323 -0
- package/src/core/scales/color.ts +51 -0
- package/src/core/scales/continuous.ts +111 -0
- package/src/core/scales/discrete.ts +156 -0
- package/src/core/scales/resolve.ts +158 -0
- package/src/core/scales/time.ts +180 -0
- package/src/core/text.ts +42 -0
- package/src/core/theme.ts +26 -0
- package/src/core/ticks.ts +118 -0
- package/src/core/types.ts +358 -0
- package/src/force/force.ts +200 -0
- package/src/force/index.ts +6 -0
- package/src/geo/geo.ts +346 -0
- package/src/geo/index.ts +10 -0
- package/src/hierarchy/hierarchy.ts +377 -0
- package/src/hierarchy/index.ts +6 -0
- package/src/index.ts +70 -0
- package/src/react/brush-contract.ts +35 -0
- package/src/react/chart-data-table.tsx +98 -0
- package/src/react/chart-legend.tsx +130 -0
- package/src/react/chart-tooltip.tsx +111 -0
- package/src/react/chart.tsx +489 -0
- package/src/react/focus-overlay.tsx +164 -0
- package/src/react/index.ts +9 -0
- package/src/react/messages.ts +49 -0
- package/src/react/renderer.ts +15 -0
- package/src/react/sr-only.ts +14 -0
- package/src/react/svg-renderer.tsx +18 -0
- package/src/react/svg-scene.tsx +146 -0
- package/src/react/use-animated-scene.ts +57 -0
- package/src/react/use-chart-focus.ts +245 -0
- package/src/react/use-chart-width.ts +27 -0
- package/src/react/use-chart-zoom.ts +67 -0
- package/src/react/use-text-measurer.ts +49 -0
- package/src/sankey/index.ts +6 -0
- package/src/sankey/sankey.ts +179 -0
- package/src/voronoi/index.ts +1 -0
- package/src/voronoi/voronoi.ts +84 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { channelLabel, isPlaceable, readChannel } from "../../channel.js";
|
|
2
|
+
import { markId, valueOn } from "../shared.js";
|
|
3
|
+
import { polygonPath } from "../../paths.js";
|
|
4
|
+
const DEFAULT_RADIUS = 10;
|
|
5
|
+
const SQRT3 = Math.sqrt(3);
|
|
6
|
+
function hexCenter(x, y, radius) {
|
|
7
|
+
const dx = radius * SQRT3;
|
|
8
|
+
const dy = radius * 1.5;
|
|
9
|
+
const row = Math.round(y / dy);
|
|
10
|
+
const neighbourRow = y / dy > row ? row + 1 : row - 1;
|
|
11
|
+
const centres = [row, neighbourRow].map((candidate) => {
|
|
12
|
+
const shift = (candidate & 1) / 2;
|
|
13
|
+
return { x: (Math.round(x / dx - shift) + shift) * dx, y: candidate * dy };
|
|
14
|
+
});
|
|
15
|
+
return Math.hypot(centres[0].x - x, centres[0].y - y) <= Math.hypot(centres[1].x - x, centres[1].y - y) ? centres[0] : centres[1];
|
|
16
|
+
}
|
|
17
|
+
function hexagon(cx, cy, radius) {
|
|
18
|
+
return Array.from({ length: 6 }, (_unused, index) => {
|
|
19
|
+
const angle = Math.PI / 3 * index + Math.PI / 6;
|
|
20
|
+
return {
|
|
21
|
+
x: cx + radius * Math.cos(angle),
|
|
22
|
+
y: cy + radius * Math.sin(angle)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function hexbin(data, options) {
|
|
27
|
+
const xs = readChannel(data, options.x);
|
|
28
|
+
const ys = readChannel(data, options.y);
|
|
29
|
+
const reach = options.radius ?? DEFAULT_RADIUS;
|
|
30
|
+
return {
|
|
31
|
+
id: options.id,
|
|
32
|
+
coordinate: "cartesian",
|
|
33
|
+
focusOrder: "point",
|
|
34
|
+
channels: {
|
|
35
|
+
x: {
|
|
36
|
+
values: xs.filter(isPlaceable),
|
|
37
|
+
label: channelLabel(options.x),
|
|
38
|
+
inset: reach
|
|
39
|
+
},
|
|
40
|
+
y: {
|
|
41
|
+
values: ys.filter(isPlaceable),
|
|
42
|
+
label: channelLabel(options.y),
|
|
43
|
+
inset: reach
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
render(context) {
|
|
47
|
+
const id = markId(options.id, "hexbin", context);
|
|
48
|
+
const { x: xScale, y: yScale } = context.scales;
|
|
49
|
+
if (xScale === void 0 || yScale === void 0) {
|
|
50
|
+
return { nodes: [] };
|
|
51
|
+
}
|
|
52
|
+
const radius = options.radius ?? DEFAULT_RADIUS;
|
|
53
|
+
const bins = /* @__PURE__ */ new Map();
|
|
54
|
+
for (const [index, raw] of xs.entries()) {
|
|
55
|
+
const x = valueOn(xScale, raw);
|
|
56
|
+
const y = valueOn(yScale, ys[index]);
|
|
57
|
+
if (x === void 0 || y === void 0) continue;
|
|
58
|
+
const center = hexCenter(xScale.center(x), yScale.center(y), radius);
|
|
59
|
+
const key = `${Math.round(center.x)}:${Math.round(center.y)}`;
|
|
60
|
+
const bin = bins.get(key) ?? { ...center, members: [] };
|
|
61
|
+
bin.members.push(index);
|
|
62
|
+
bins.set(key, bin);
|
|
63
|
+
}
|
|
64
|
+
const fullest = Math.max(
|
|
65
|
+
1,
|
|
66
|
+
...[...bins.values()].map((bin) => bin.members.length)
|
|
67
|
+
);
|
|
68
|
+
const color = options.fill ?? context.paletteColor(context.markIndex);
|
|
69
|
+
const nodes = [];
|
|
70
|
+
const points = [];
|
|
71
|
+
for (const [key, bin] of bins) {
|
|
72
|
+
const share = bin.members.length / fullest;
|
|
73
|
+
const fill = `color-mix(in oklab, ${color} ${Math.round(15 + 85 * share)}%, ${context.theme.background})`;
|
|
74
|
+
nodes.push({
|
|
75
|
+
kind: "path",
|
|
76
|
+
key: `${id}:${key}`,
|
|
77
|
+
role: "mark",
|
|
78
|
+
d: polygonPath(hexagon(bin.x, bin.y, radius - 0.5)),
|
|
79
|
+
paint: { fill }
|
|
80
|
+
});
|
|
81
|
+
points.push({
|
|
82
|
+
key: `${id}:${key}`,
|
|
83
|
+
markId: id,
|
|
84
|
+
index: bin.members[0],
|
|
85
|
+
datum: bin.members.map((index) => data[index]),
|
|
86
|
+
x: bin.x,
|
|
87
|
+
y: bin.y,
|
|
88
|
+
xValue: xScale.invert(bin.x),
|
|
89
|
+
yValue: yScale.invert(bin.y),
|
|
90
|
+
seriesLabel: options.label,
|
|
91
|
+
color: fill,
|
|
92
|
+
title: `${context.formatX(xScale.invert(bin.x))} \xB7 ${context.formatY(yScale.invert(bin.y))}`,
|
|
93
|
+
value: String(bin.members.length),
|
|
94
|
+
hit: {
|
|
95
|
+
kind: "rect",
|
|
96
|
+
rect: {
|
|
97
|
+
x: bin.x - radius,
|
|
98
|
+
y: bin.y - radius,
|
|
99
|
+
width: radius * 2,
|
|
100
|
+
height: radius * 2
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return { nodes, points };
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
hexCenter,
|
|
111
|
+
hexbin
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=hexbin.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/core/marks/stats/hexbin.ts"],
|
|
4
|
+
"sourcesContent": ["import { channelLabel, isPlaceable, readChannel } from \"#/core/channel.ts\";\nimport { markId, valueOn } from \"#/core/marks/shared.ts\";\nimport { polygonPath } from \"#/core/paths.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartPoint,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface HexbinOptions<TDatum> {\n\treadonly id?: string;\n\treadonly x: ChartAccessor<TDatum, ChartValue>;\n\treadonly y: ChartAccessor<TDatum, ChartValue>;\n\t/** Pixel radius of a hexagon. */\n\treadonly radius?: number;\n\t/** Colour of the fullest bin; emptier bins fade towards the background. */\n\treadonly fill?: string;\n\treadonly label?: string;\n}\n\nconst DEFAULT_RADIUS = 10;\nconst SQRT3 = Math.sqrt(3);\n\n/**\n * The centre of the hexagon a pixel falls in, on a pointy-top grid whose odd\n * rows are offset by half a hexagon. Near a slanted edge the nearest row is\n * not always the right one, so the closer of the two candidate centres wins.\n */\nexport function hexCenter(\n\tx: number,\n\ty: number,\n\tradius: number,\n): { x: number; y: number } {\n\tconst dx = radius * SQRT3;\n\tconst dy = radius * 1.5;\n\tconst row = Math.round(y / dy);\n\tconst neighbourRow = y / dy > row ? row + 1 : row - 1;\n\tconst centres = [row, neighbourRow].map((candidate) => {\n\t\tconst shift = (candidate & 1) / 2;\n\t\treturn { x: (Math.round(x / dx - shift) + shift) * dx, y: candidate * dy };\n\t});\n\treturn Math.hypot(centres[0].x - x, centres[0].y - y) <=\n\t\tMath.hypot(centres[1].x - x, centres[1].y - y)\n\t\t? centres[0]\n\t\t: centres[1];\n}\n\nfunction hexagon(cx: number, cy: number, radius: number) {\n\treturn Array.from({ length: 6 }, (_unused, index) => {\n\t\tconst angle = (Math.PI / 3) * index + Math.PI / 6;\n\t\treturn {\n\t\t\tx: cx + radius * Math.cos(angle),\n\t\t\ty: cy + radius * Math.sin(angle),\n\t\t};\n\t});\n}\n\n/**\n * Points counted into hexagons: a scatter too dense to read as dots. The\n * bins are found in pixels, so they stay hexagonal whatever the scales.\n */\nexport function hexbin<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: HexbinOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x);\n\tconst ys = readChannel(data, options.y);\n\tconst reach = options.radius ?? DEFAULT_RADIUS;\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tfocusOrder: \"point\",\n\t\tchannels: {\n\t\t\tx: {\n\t\t\t\tvalues: xs.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.x),\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t\ty: {\n\t\t\t\tvalues: ys.filter(isPlaceable),\n\t\t\t\tlabel: channelLabel(options.y),\n\t\t\t\tinset: reach,\n\t\t\t},\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"hexbin\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (xScale === undefined || yScale === undefined) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst radius = options.radius ?? DEFAULT_RADIUS;\n\t\t\tconst bins = new Map<\n\t\t\t\tstring,\n\t\t\t\t{ x: number; y: number; members: number[] }\n\t\t\t>();\n\t\t\tfor (const [index, raw] of xs.entries()) {\n\t\t\t\tconst x = valueOn(xScale, raw);\n\t\t\t\tconst y = valueOn(yScale, ys[index]);\n\t\t\t\tif (x === undefined || y === undefined) continue;\n\t\t\t\tconst center = hexCenter(xScale.center(x), yScale.center(y), radius);\n\t\t\t\tconst key = `${Math.round(center.x)}:${Math.round(center.y)}`;\n\t\t\t\tconst bin = bins.get(key) ?? { ...center, members: [] };\n\t\t\t\tbin.members.push(index);\n\t\t\t\tbins.set(key, bin);\n\t\t\t}\n\t\t\tconst fullest = Math.max(\n\t\t\t\t1,\n\t\t\t\t...[...bins.values()].map((bin) => bin.members.length),\n\t\t\t);\n\t\t\tconst color = options.fill ?? context.paletteColor(context.markIndex);\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tconst points: ChartPoint[] = [];\n\t\t\tfor (const [key, bin] of bins) {\n\t\t\t\tconst share = bin.members.length / fullest;\n\t\t\t\tconst fill = `color-mix(in oklab, ${color} ${Math.round(15 + 85 * share)}%, ${context.theme.background})`;\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: polygonPath(hexagon(bin.x, bin.y, radius - 0.5)),\n\t\t\t\t\tpaint: { fill },\n\t\t\t\t});\n\t\t\t\tpoints.push({\n\t\t\t\t\tkey: `${id}:${key}`,\n\t\t\t\t\tmarkId: id,\n\t\t\t\t\tindex: bin.members[0],\n\t\t\t\t\tdatum: bin.members.map((index) => data[index]),\n\t\t\t\t\tx: bin.x,\n\t\t\t\t\ty: bin.y,\n\t\t\t\t\txValue: xScale.invert(bin.x),\n\t\t\t\t\tyValue: yScale.invert(bin.y),\n\t\t\t\t\tseriesLabel: options.label,\n\t\t\t\t\tcolor: fill,\n\t\t\t\t\ttitle: `${context.formatX(xScale.invert(bin.x))} \u00B7 ${context.formatY(yScale.invert(bin.y))}`,\n\t\t\t\t\tvalue: String(bin.members.length),\n\t\t\t\t\thit: {\n\t\t\t\t\t\tkind: \"rect\",\n\t\t\t\t\t\trect: {\n\t\t\t\t\t\t\tx: bin.x - radius,\n\t\t\t\t\t\t\ty: bin.y - radius,\n\t\t\t\t\t\t\twidth: radius * 2,\n\t\t\t\t\t\t\theight: radius * 2,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn { nodes, points };\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChartMarkOptions } from "../shared.js";
|
|
2
|
+
import type { ChartAccessor, ChartMark, ChartValue } from "../../types.js";
|
|
3
|
+
export interface HistogramOptions<TDatum> extends ChartMarkOptions<TDatum> {
|
|
4
|
+
readonly x: ChartAccessor<TDatum, ChartValue>;
|
|
5
|
+
/** About how many bins: the edges land on round numbers, so the count is approximate. */
|
|
6
|
+
readonly bins?: number;
|
|
7
|
+
readonly fill?: string;
|
|
8
|
+
/** Each bin's share of the total instead of its count. */
|
|
9
|
+
readonly normalize?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface Bin {
|
|
12
|
+
readonly x1: number;
|
|
13
|
+
readonly x2: number;
|
|
14
|
+
readonly count: number;
|
|
15
|
+
readonly series?: ChartValue;
|
|
16
|
+
}
|
|
17
|
+
/** Round-edged bins over the values' extent, every value in exactly one. */
|
|
18
|
+
export declare function binValues(values: ReadonlyArray<number>, target: number): ReadonlyArray<{
|
|
19
|
+
x1: number;
|
|
20
|
+
x2: number;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Counts per bin, as touching rectangles over a continuous x. With a color
|
|
24
|
+
* channel the series stack inside each bin.
|
|
25
|
+
*/
|
|
26
|
+
export declare function histogram<TDatum>(data: ReadonlyArray<TDatum>, options: HistogramOptions<TDatum>): ChartMark;
|
|
27
|
+
//# sourceMappingURL=histogram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"histogram.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/histogram.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE5E,MAAM,WAAW,gBAAgB,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACzE,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;CAC7B;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CACxB,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,MAAM,GACZ,aAAa,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB3C;AAID;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAC/B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAC/B,SAAS,CAqDX"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isPlaceable, numericValue, readChannel } from "../../channel.js";
|
|
2
|
+
import { rectY } from "../rect.js";
|
|
3
|
+
import { tickStep } from "../../ticks.js";
|
|
4
|
+
function binValues(values, target) {
|
|
5
|
+
const finite = values.filter(Number.isFinite);
|
|
6
|
+
if (finite.length === 0) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const low = Math.min(...finite);
|
|
10
|
+
const high = Math.max(...finite);
|
|
11
|
+
const step = tickStep(low, high, target) || 1;
|
|
12
|
+
const first = Math.floor(low / step) * step;
|
|
13
|
+
const count = Math.max(
|
|
14
|
+
1,
|
|
15
|
+
Math.ceil((high - first) / step + (high === first ? 1 : 0))
|
|
16
|
+
);
|
|
17
|
+
return Array.from({ length: count }, (_unused, index) => ({
|
|
18
|
+
x1: first + index * step,
|
|
19
|
+
x2: first + (index + 1) * step
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
const DEFAULT_BINS = 20;
|
|
23
|
+
function histogram(data, options) {
|
|
24
|
+
const xs = readChannel(data, options.x).map(numericValue);
|
|
25
|
+
const colors = readChannel(data, options.color);
|
|
26
|
+
const edges = binValues(
|
|
27
|
+
xs.filter((value) => value !== void 0),
|
|
28
|
+
options.bins ?? DEFAULT_BINS
|
|
29
|
+
);
|
|
30
|
+
const seriesValues = options.color === void 0 ? [void 0] : [
|
|
31
|
+
...new Map(
|
|
32
|
+
colors.filter(isPlaceable).map((value) => [String(value), value])
|
|
33
|
+
).values()
|
|
34
|
+
];
|
|
35
|
+
const total = xs.filter((value) => value !== void 0).length || 1;
|
|
36
|
+
const bins = [];
|
|
37
|
+
for (const series of seriesValues) {
|
|
38
|
+
for (const [binIndex, edge] of edges.entries()) {
|
|
39
|
+
const last = binIndex === edges.length - 1;
|
|
40
|
+
let count = 0;
|
|
41
|
+
for (const [index, x] of xs.entries()) {
|
|
42
|
+
const inSeries = series === void 0 || String(colors[index]) === String(series);
|
|
43
|
+
if (inSeries && x !== void 0 && x >= edge.x1 && (x < edge.x2 || last && x <= edge.x2)) {
|
|
44
|
+
count += 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
bins.push({
|
|
48
|
+
...edge,
|
|
49
|
+
count: options.normalize ? count / total : count,
|
|
50
|
+
series
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return rectY(bins, {
|
|
55
|
+
id: options.id,
|
|
56
|
+
label: options.label,
|
|
57
|
+
tip: options.tip,
|
|
58
|
+
opacity: options.opacity,
|
|
59
|
+
fill: options.fill,
|
|
60
|
+
x1: (bin) => bin.x1,
|
|
61
|
+
x2: (bin) => bin.x2,
|
|
62
|
+
y: (bin) => bin.count,
|
|
63
|
+
...options.color === void 0 ? {} : { color: (bin) => bin.series }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
binValues,
|
|
68
|
+
histogram
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=histogram.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/core/marks/stats/histogram.ts"],
|
|
4
|
+
"sourcesContent": ["import { isPlaceable, numericValue, readChannel } from \"#/core/channel.ts\";\nimport { rectY } from \"#/core/marks/rect.ts\";\nimport type { ChartMarkOptions } from \"#/core/marks/shared.ts\";\nimport { tickStep } from \"#/core/ticks.ts\";\nimport type { ChartAccessor, ChartMark, ChartValue } from \"#/core/types.ts\";\n\nexport interface HistogramOptions<TDatum> extends ChartMarkOptions<TDatum> {\n\treadonly x: ChartAccessor<TDatum, ChartValue>;\n\t/** About how many bins: the edges land on round numbers, so the count is approximate. */\n\treadonly bins?: number;\n\treadonly fill?: string;\n\t/** Each bin's share of the total instead of its count. */\n\treadonly normalize?: boolean;\n}\n\nexport interface Bin {\n\treadonly x1: number;\n\treadonly x2: number;\n\treadonly count: number;\n\treadonly series?: ChartValue;\n}\n\n/** Round-edged bins over the values' extent, every value in exactly one. */\nexport function binValues(\n\tvalues: ReadonlyArray<number>,\n\ttarget: number,\n): ReadonlyArray<{ x1: number; x2: number }> {\n\tconst finite = values.filter(Number.isFinite);\n\tif (finite.length === 0) {\n\t\treturn [];\n\t}\n\tconst low = Math.min(...finite);\n\tconst high = Math.max(...finite);\n\tconst step = tickStep(low, high, target) || 1;\n\tconst first = Math.floor(low / step) * step;\n\tconst count = Math.max(\n\t\t1,\n\t\tMath.ceil((high - first) / step + (high === first ? 1 : 0)),\n\t);\n\treturn Array.from({ length: count }, (_unused, index) => ({\n\t\tx1: first + index * step,\n\t\tx2: first + (index + 1) * step,\n\t}));\n}\n\nconst DEFAULT_BINS = 20;\n\n/**\n * Counts per bin, as touching rectangles over a continuous x. With a color\n * channel the series stack inside each bin.\n */\nexport function histogram<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: HistogramOptions<TDatum>,\n): ChartMark {\n\tconst xs = readChannel(data, options.x).map(numericValue);\n\tconst colors = readChannel(data, options.color);\n\tconst edges = binValues(\n\t\txs.filter((value): value is number => value !== undefined),\n\t\toptions.bins ?? DEFAULT_BINS,\n\t);\n\tconst seriesValues =\n\t\toptions.color === undefined\n\t\t\t? [undefined]\n\t\t\t: [\n\t\t\t\t\t...new Map(\n\t\t\t\t\t\tcolors.filter(isPlaceable).map((value) => [String(value), value]),\n\t\t\t\t\t).values(),\n\t\t\t\t];\n\tconst total = xs.filter((value) => value !== undefined).length || 1;\n\tconst bins: Bin[] = [];\n\tfor (const series of seriesValues) {\n\t\tfor (const [binIndex, edge] of edges.entries()) {\n\t\t\tconst last = binIndex === edges.length - 1;\n\t\t\tlet count = 0;\n\t\t\tfor (const [index, x] of xs.entries()) {\n\t\t\t\tconst inSeries =\n\t\t\t\t\tseries === undefined || String(colors[index]) === String(series);\n\t\t\t\tif (\n\t\t\t\t\tinSeries &&\n\t\t\t\t\tx !== undefined &&\n\t\t\t\t\tx >= edge.x1 &&\n\t\t\t\t\t(x < edge.x2 || (last && x <= edge.x2))\n\t\t\t\t) {\n\t\t\t\t\tcount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbins.push({\n\t\t\t\t...edge,\n\t\t\t\tcount: options.normalize ? count / total : count,\n\t\t\t\tseries,\n\t\t\t});\n\t\t}\n\t}\n\treturn rectY(bins, {\n\t\tid: options.id,\n\t\tlabel: options.label,\n\t\ttip: options.tip,\n\t\topacity: options.opacity,\n\t\tfill: options.fill,\n\t\tx1: (bin) => bin.x1,\n\t\tx2: (bin) => bin.x2,\n\t\ty: (bin) => bin.count,\n\t\t...(options.color === undefined\n\t\t\t? {}\n\t\t\t: { color: (bin: Bin) => bin.series as ChartValue }),\n\t});\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,aAAa,cAAc,mBAAmB;AACvD,SAAS,aAAa;AAEtB,SAAS,gBAAgB;AAoBlB,SAAS,UACf,QACA,QAC4C;AAC5C,QAAM,SAAS,OAAO,OAAO,OAAO,QAAQ;AAC5C,MAAI,OAAO,WAAW,GAAG;AACxB,WAAO,CAAC;AAAA,EACT;AACA,QAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,QAAM,OAAO,KAAK,IAAI,GAAG,MAAM;AAC/B,QAAM,OAAO,SAAS,KAAK,MAAM,MAAM,KAAK;AAC5C,QAAM,QAAQ,KAAK,MAAM,MAAM,IAAI,IAAI;AACvC,QAAM,QAAQ,KAAK;AAAA,IAClB;AAAA,IACA,KAAK,MAAM,OAAO,SAAS,QAAQ,SAAS,QAAQ,IAAI,EAAE;AAAA,EAC3D;AACA,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,SAAS,WAAW;AAAA,IACzD,IAAI,QAAQ,QAAQ;AAAA,IACpB,IAAI,SAAS,QAAQ,KAAK;AAAA,EAC3B,EAAE;AACH;AAEA,MAAM,eAAe;AAMd,SAAS,UACf,MACA,SACY;AACZ,QAAM,KAAK,YAAY,MAAM,QAAQ,CAAC,EAAE,IAAI,YAAY;AACxD,QAAM,SAAS,YAAY,MAAM,QAAQ,KAAK;AAC9C,QAAM,QAAQ;AAAA,IACb,GAAG,OAAO,CAAC,UAA2B,UAAU,MAAS;AAAA,IACzD,QAAQ,QAAQ;AAAA,EACjB;AACA,QAAM,eACL,QAAQ,UAAU,SACf,CAAC,MAAS,IACV;AAAA,IACA,GAAG,IAAI;AAAA,MACN,OAAO,OAAO,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,KAAK,GAAG,KAAK,CAAC;AAAA,IACjE,EAAE,OAAO;AAAA,EACV;AACH,QAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,UAAU,MAAS,EAAE,UAAU;AAClE,QAAM,OAAc,CAAC;AACrB,aAAW,UAAU,cAAc;AAClC,eAAW,CAAC,UAAU,IAAI,KAAK,MAAM,QAAQ,GAAG;AAC/C,YAAM,OAAO,aAAa,MAAM,SAAS;AACzC,UAAI,QAAQ;AACZ,iBAAW,CAAC,OAAO,CAAC,KAAK,GAAG,QAAQ,GAAG;AACtC,cAAM,WACL,WAAW,UAAa,OAAO,OAAO,KAAK,CAAC,MAAM,OAAO,MAAM;AAChE,YACC,YACA,MAAM,UACN,KAAK,KAAK,OACT,IAAI,KAAK,MAAO,QAAQ,KAAK,KAAK,KAClC;AACD,mBAAS;AAAA,QACV;AAAA,MACD;AACA,WAAK,KAAK;AAAA,QACT,GAAG;AAAA,QACH,OAAO,QAAQ,YAAY,QAAQ,QAAQ;AAAA,QAC3C;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AACA,SAAO,MAAM,MAAM;AAAA,IAClB,IAAI,QAAQ;AAAA,IACZ,OAAO,QAAQ;AAAA,IACf,KAAK,QAAQ;AAAA,IACb,SAAS,QAAQ;AAAA,IACjB,MAAM,QAAQ;AAAA,IACd,IAAI,CAAC,QAAQ,IAAI;AAAA,IACjB,IAAI,CAAC,QAAQ,IAAI;AAAA,IACjB,GAAG,CAAC,QAAQ,IAAI;AAAA,IAChB,GAAI,QAAQ,UAAU,SACnB,CAAC,IACD,EAAE,OAAO,CAAC,QAAa,IAAI,OAAqB;AAAA,EACpD,CAAC;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChartAccessor, ChartMark, ChartValue } from "../../types.js";
|
|
2
|
+
export interface RegressionOptions<TDatum> {
|
|
3
|
+
readonly id?: string;
|
|
4
|
+
readonly x: ChartAccessor<TDatum, ChartValue>;
|
|
5
|
+
readonly y: ChartAccessor<TDatum, ChartValue>;
|
|
6
|
+
readonly stroke?: string;
|
|
7
|
+
/** Shade the 95 % confidence band of the fitted mean. */
|
|
8
|
+
readonly band?: boolean;
|
|
9
|
+
readonly label?: string;
|
|
10
|
+
}
|
|
11
|
+
/** The least-squares line through x and y, and how sure it is. Drawn over a scatter. */
|
|
12
|
+
export declare function regressionY<TDatum>(data: ReadonlyArray<TDatum>, options: RegressionOptions<TDatum>): ChartMark;
|
|
13
|
+
//# sourceMappingURL=regression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regression.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/regression.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,iBAAiB,CAAC,MAAM;IACxC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAChC,SAAS,CA+EX"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { isPlaceable, numericValue, readChannel } from "../../channel.js";
|
|
2
|
+
import { markId, paint } from "../shared.js";
|
|
3
|
+
import { linearFit } from "./statistics.js";
|
|
4
|
+
import { areaPath, linePath } from "../../paths.js";
|
|
5
|
+
import { toNumber } from "../../scales/continuous.js";
|
|
6
|
+
const BAND_SAMPLES = 24;
|
|
7
|
+
function regressionY(data, options) {
|
|
8
|
+
const pairs = readChannel(data, options.x).map((x, index) => ({
|
|
9
|
+
x,
|
|
10
|
+
y: numericValue(readChannel(data, options.y)[index])
|
|
11
|
+
})).filter(
|
|
12
|
+
(pair) => isPlaceable(pair.x) && pair.y !== void 0
|
|
13
|
+
);
|
|
14
|
+
const xs = pairs.map((pair) => toNumber(pair.x));
|
|
15
|
+
const fit = linearFit(
|
|
16
|
+
xs,
|
|
17
|
+
pairs.map((pair) => pair.y)
|
|
18
|
+
);
|
|
19
|
+
const dates = pairs.some((pair) => pair.x instanceof Date);
|
|
20
|
+
return {
|
|
21
|
+
id: options.id,
|
|
22
|
+
coordinate: "cartesian",
|
|
23
|
+
channels: {
|
|
24
|
+
x: { values: pairs.map((pair) => pair.x) },
|
|
25
|
+
y: { values: pairs.map((pair) => pair.y) }
|
|
26
|
+
},
|
|
27
|
+
render(context) {
|
|
28
|
+
const id = markId(options.id, "regressionY", context);
|
|
29
|
+
const { x: xScale, y: yScale } = context.scales;
|
|
30
|
+
if (fit === void 0 || xScale === void 0 || yScale === void 0 || xs.length === 0) {
|
|
31
|
+
return { nodes: [] };
|
|
32
|
+
}
|
|
33
|
+
const low = Math.min(...xs);
|
|
34
|
+
const high = Math.max(...xs);
|
|
35
|
+
const at = Array.from(
|
|
36
|
+
{ length: BAND_SAMPLES },
|
|
37
|
+
(_unused, index) => low + (high - low) * index / (BAND_SAMPLES - 1)
|
|
38
|
+
);
|
|
39
|
+
const place = (x) => xScale.map(dates ? new Date(x) : x);
|
|
40
|
+
const stroke = options.stroke ?? context.theme.foreground;
|
|
41
|
+
const nodes = [];
|
|
42
|
+
if (options.band !== false) {
|
|
43
|
+
nodes.push({
|
|
44
|
+
kind: "path",
|
|
45
|
+
key: `${id}:band`,
|
|
46
|
+
role: "mark",
|
|
47
|
+
d: areaPath(
|
|
48
|
+
at.map((x) => ({
|
|
49
|
+
x: place(x),
|
|
50
|
+
y: yScale.map(fit.intercept + fit.slope * x + fit.margin(x))
|
|
51
|
+
})),
|
|
52
|
+
at.map((x) => ({
|
|
53
|
+
x: place(x),
|
|
54
|
+
y: yScale.map(fit.intercept + fit.slope * x - fit.margin(x))
|
|
55
|
+
}))
|
|
56
|
+
),
|
|
57
|
+
paint: { fill: stroke, fillOpacity: 0.12 }
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
nodes.push({
|
|
61
|
+
kind: "path",
|
|
62
|
+
key: `${id}:line`,
|
|
63
|
+
role: "mark",
|
|
64
|
+
d: linePath([
|
|
65
|
+
{ x: place(low), y: yScale.map(fit.intercept + fit.slope * low) },
|
|
66
|
+
{ x: place(high), y: yScale.map(fit.intercept + fit.slope * high) }
|
|
67
|
+
]),
|
|
68
|
+
paint: paint({ fill: "none", stroke, strokeWidth: 2 })
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
nodes,
|
|
72
|
+
legend: options.label ? [{ key: id, label: options.label, color: stroke, shape: "line" }] : []
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
regressionY
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=regression.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/core/marks/stats/regression.ts"],
|
|
4
|
+
"sourcesContent": ["import { isPlaceable, numericValue, readChannel } from \"#/core/channel.ts\";\nimport { markId, paint } from \"#/core/marks/shared.ts\";\nimport { linearFit } from \"#/core/marks/stats/statistics.ts\";\nimport { areaPath, linePath } from \"#/core/paths.ts\";\nimport { toNumber } from \"#/core/scales/continuous.ts\";\nimport type {\n\tChartAccessor,\n\tChartMark,\n\tChartValue,\n\tSceneNode,\n} from \"#/core/types.ts\";\n\nexport interface RegressionOptions<TDatum> {\n\treadonly id?: string;\n\treadonly x: ChartAccessor<TDatum, ChartValue>;\n\treadonly y: ChartAccessor<TDatum, ChartValue>;\n\treadonly stroke?: string;\n\t/** Shade the 95 % confidence band of the fitted mean. */\n\treadonly band?: boolean;\n\treadonly label?: string;\n}\n\nconst BAND_SAMPLES = 24;\n\n/** The least-squares line through x and y, and how sure it is. Drawn over a scatter. */\nexport function regressionY<TDatum>(\n\tdata: ReadonlyArray<TDatum>,\n\toptions: RegressionOptions<TDatum>,\n): ChartMark {\n\tconst pairs = readChannel(data, options.x)\n\t\t.map((x, index) => ({\n\t\t\tx,\n\t\t\ty: numericValue(readChannel(data, options.y)[index]),\n\t\t}))\n\t\t.filter(\n\t\t\t(pair): pair is { x: ChartValue; y: number } =>\n\t\t\t\tisPlaceable(pair.x) && pair.y !== undefined,\n\t\t);\n\tconst xs = pairs.map((pair) => toNumber(pair.x));\n\tconst fit = linearFit(\n\t\txs,\n\t\tpairs.map((pair) => pair.y),\n\t);\n\tconst dates = pairs.some((pair) => pair.x instanceof Date);\n\treturn {\n\t\tid: options.id,\n\t\tcoordinate: \"cartesian\",\n\t\tchannels: {\n\t\t\tx: { values: pairs.map((pair) => pair.x) },\n\t\t\ty: { values: pairs.map((pair) => pair.y) },\n\t\t},\n\t\trender(context) {\n\t\t\tconst id = markId(options.id, \"regressionY\", context);\n\t\t\tconst { x: xScale, y: yScale } = context.scales;\n\t\t\tif (\n\t\t\t\tfit === undefined ||\n\t\t\t\txScale === undefined ||\n\t\t\t\tyScale === undefined ||\n\t\t\t\txs.length === 0\n\t\t\t) {\n\t\t\t\treturn { nodes: [] };\n\t\t\t}\n\t\t\tconst low = Math.min(...xs);\n\t\t\tconst high = Math.max(...xs);\n\t\t\tconst at = Array.from(\n\t\t\t\t{ length: BAND_SAMPLES },\n\t\t\t\t(_unused, index) => low + ((high - low) * index) / (BAND_SAMPLES - 1),\n\t\t\t);\n\t\t\tconst place = (x: number) => xScale.map(dates ? new Date(x) : x);\n\t\t\tconst stroke = options.stroke ?? context.theme.foreground;\n\t\t\tconst nodes: SceneNode[] = [];\n\t\t\tif (options.band !== false) {\n\t\t\t\tnodes.push({\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: `${id}:band`,\n\t\t\t\t\trole: \"mark\",\n\t\t\t\t\td: areaPath(\n\t\t\t\t\t\tat.map((x) => ({\n\t\t\t\t\t\t\tx: place(x),\n\t\t\t\t\t\t\ty: yScale.map(fit.intercept + fit.slope * x + fit.margin(x)),\n\t\t\t\t\t\t})),\n\t\t\t\t\t\tat.map((x) => ({\n\t\t\t\t\t\t\tx: place(x),\n\t\t\t\t\t\t\ty: yScale.map(fit.intercept + fit.slope * x - fit.margin(x)),\n\t\t\t\t\t\t})),\n\t\t\t\t\t),\n\t\t\t\t\tpaint: { fill: stroke, fillOpacity: 0.12 },\n\t\t\t\t});\n\t\t\t}\n\t\t\tnodes.push({\n\t\t\t\tkind: \"path\",\n\t\t\t\tkey: `${id}:line`,\n\t\t\t\trole: \"mark\",\n\t\t\t\td: linePath([\n\t\t\t\t\t{ x: place(low), y: yScale.map(fit.intercept + fit.slope * low) },\n\t\t\t\t\t{ x: place(high), y: yScale.map(fit.intercept + fit.slope * high) },\n\t\t\t\t]),\n\t\t\t\tpaint: paint({ fill: \"none\", stroke, strokeWidth: 2 }),\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tnodes,\n\t\t\t\tlegend: options.label\n\t\t\t\t\t? [{ key: id, label: options.label, color: stroke, shape: \"line\" }]\n\t\t\t\t\t: [],\n\t\t\t};\n\t\t},\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The few statistics the statistical marks need, written out: quantiles,
|
|
3
|
+
* kernel density, least squares. Each is a handful of lines, which is less to
|
|
4
|
+
* carry than a dependency.
|
|
5
|
+
*/
|
|
6
|
+
/** Linear-interpolated quantile of sorted values (the R-7 / Excel definition). */
|
|
7
|
+
export declare function quantile(sorted: ReadonlyArray<number>, p: number): number;
|
|
8
|
+
export interface BoxSummary {
|
|
9
|
+
readonly min: number;
|
|
10
|
+
readonly q1: number;
|
|
11
|
+
readonly median: number;
|
|
12
|
+
readonly q3: number;
|
|
13
|
+
readonly max: number;
|
|
14
|
+
/** Whisker ends: the furthest values within 1.5 IQR of the box. */
|
|
15
|
+
readonly low: number;
|
|
16
|
+
readonly high: number;
|
|
17
|
+
readonly outliers: ReadonlyArray<number>;
|
|
18
|
+
readonly count: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function boxSummary(values: ReadonlyArray<number>): BoxSummary | undefined;
|
|
21
|
+
/** Silverman's rule of thumb: a bandwidth that suits most unimodal data. */
|
|
22
|
+
export declare function silvermanBandwidth(values: ReadonlyArray<number>): number;
|
|
23
|
+
/** Gaussian kernel density of `values`, sampled at `at`. */
|
|
24
|
+
export declare function kernelDensity(values: ReadonlyArray<number>, at: ReadonlyArray<number>, bandwidth?: number): ReadonlyArray<number>;
|
|
25
|
+
/** `count` evenly spaced samples over `[low, high]`. */
|
|
26
|
+
export declare function samples(low: number, high: number, count: number): number[];
|
|
27
|
+
export interface LinearFit {
|
|
28
|
+
readonly slope: number;
|
|
29
|
+
readonly intercept: number;
|
|
30
|
+
/** Half-width of the 95 % confidence band of the mean at `x`. */
|
|
31
|
+
readonly margin: (x: number) => number;
|
|
32
|
+
}
|
|
33
|
+
export declare function linearFit(xs: ReadonlyArray<number>, ys: ReadonlyArray<number>): LinearFit | undefined;
|
|
34
|
+
//# sourceMappingURL=statistics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statistics.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/statistics.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQzE;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAID,wBAAgB,UAAU,CACzB,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAC3B,UAAU,GAAG,SAAS,CA0BxB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAexE;AAID,4DAA4D;AAC5D,wBAAgB,aAAa,CAC5B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAC7B,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,EACzB,SAAS,SAA6B,GACpC,aAAa,CAAC,MAAM,CAAC,CAUvB;AAED,wDAAwD;AACxD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ1E;AAED,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAKD,wBAAgB,SAAS,CACxB,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,EACzB,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,GACvB,SAAS,GAAG,SAAS,CA6BvB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
function quantile(sorted, p) {
|
|
2
|
+
if (sorted.length === 0) {
|
|
3
|
+
return Number.NaN;
|
|
4
|
+
}
|
|
5
|
+
const position = (sorted.length - 1) * p;
|
|
6
|
+
const below = Math.floor(position);
|
|
7
|
+
const above = Math.ceil(position);
|
|
8
|
+
return sorted[below] + (sorted[above] - sorted[below]) * (position - below);
|
|
9
|
+
}
|
|
10
|
+
const TUKEY_FENCE = 1.5;
|
|
11
|
+
function boxSummary(values) {
|
|
12
|
+
const sorted = values.filter(Number.isFinite).sort((left, right) => left - right);
|
|
13
|
+
if (sorted.length === 0) {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
const q1 = quantile(sorted, 0.25);
|
|
17
|
+
const q3 = quantile(sorted, 0.75);
|
|
18
|
+
const fence = (q3 - q1) * TUKEY_FENCE;
|
|
19
|
+
const inside = sorted.filter(
|
|
20
|
+
(value) => value >= q1 - fence && value <= q3 + fence
|
|
21
|
+
);
|
|
22
|
+
return {
|
|
23
|
+
min: sorted[0],
|
|
24
|
+
q1,
|
|
25
|
+
median: quantile(sorted, 0.5),
|
|
26
|
+
q3,
|
|
27
|
+
max: sorted[sorted.length - 1],
|
|
28
|
+
low: inside[0] ?? q1,
|
|
29
|
+
high: inside[inside.length - 1] ?? q3,
|
|
30
|
+
outliers: sorted.filter(
|
|
31
|
+
(value) => value < q1 - fence || value > q3 + fence
|
|
32
|
+
),
|
|
33
|
+
count: sorted.length
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function silvermanBandwidth(values) {
|
|
37
|
+
const count = values.length;
|
|
38
|
+
if (count < 2) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / count;
|
|
42
|
+
const deviation = Math.sqrt(
|
|
43
|
+
values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / (count - 1)
|
|
44
|
+
);
|
|
45
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
46
|
+
const spread = Math.min(
|
|
47
|
+
deviation,
|
|
48
|
+
(quantile(sorted, 0.75) - quantile(sorted, 0.25)) / 1.34
|
|
49
|
+
);
|
|
50
|
+
return 0.9 * (spread || deviation || 1) * count ** -0.2;
|
|
51
|
+
}
|
|
52
|
+
const GAUSSIAN = 1 / Math.sqrt(2 * Math.PI);
|
|
53
|
+
function kernelDensity(values, at, bandwidth = silvermanBandwidth(values)) {
|
|
54
|
+
const count = values.length;
|
|
55
|
+
return at.map((x) => {
|
|
56
|
+
let sum = 0;
|
|
57
|
+
for (const value of values) {
|
|
58
|
+
const u = (x - value) / bandwidth;
|
|
59
|
+
sum += GAUSSIAN * Math.exp(-0.5 * u * u);
|
|
60
|
+
}
|
|
61
|
+
return count === 0 ? 0 : sum / (count * bandwidth);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function samples(low, high, count) {
|
|
65
|
+
if (count < 2 || low === high) {
|
|
66
|
+
return [low];
|
|
67
|
+
}
|
|
68
|
+
return Array.from(
|
|
69
|
+
{ length: count },
|
|
70
|
+
(_unused, index) => low + (high - low) * index / (count - 1)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const Z_95 = 1.96;
|
|
74
|
+
function linearFit(xs, ys) {
|
|
75
|
+
const count = xs.length;
|
|
76
|
+
if (count < 2) {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
const meanX = xs.reduce((sum, x) => sum + x, 0) / count;
|
|
80
|
+
const meanY = ys.reduce((sum, y) => sum + y, 0) / count;
|
|
81
|
+
let sxx = 0;
|
|
82
|
+
let sxy = 0;
|
|
83
|
+
for (let index = 0; index < count; index += 1) {
|
|
84
|
+
sxx += (xs[index] - meanX) ** 2;
|
|
85
|
+
sxy += (xs[index] - meanX) * (ys[index] - meanY);
|
|
86
|
+
}
|
|
87
|
+
if (sxx === 0) {
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
const slope = sxy / sxx;
|
|
91
|
+
const intercept = meanY - slope * meanX;
|
|
92
|
+
let residuals = 0;
|
|
93
|
+
for (let index = 0; index < count; index += 1) {
|
|
94
|
+
residuals += (ys[index] - (intercept + slope * xs[index])) ** 2;
|
|
95
|
+
}
|
|
96
|
+
const standardError = count > 2 ? Math.sqrt(residuals / (count - 2)) : 0;
|
|
97
|
+
return {
|
|
98
|
+
slope,
|
|
99
|
+
intercept,
|
|
100
|
+
margin: (x) => Z_95 * standardError * Math.sqrt(1 / count + (x - meanX) ** 2 / sxx)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
boxSummary,
|
|
105
|
+
kernelDensity,
|
|
106
|
+
linearFit,
|
|
107
|
+
quantile,
|
|
108
|
+
samples,
|
|
109
|
+
silvermanBandwidth
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=statistics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/core/marks/stats/statistics.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The few statistics the statistical marks need, written out: quantiles,\n * kernel density, least squares. Each is a handful of lines, which is less to\n * carry than a dependency.\n */\n\n/** Linear-interpolated quantile of sorted values (the R-7 / Excel definition). */\nexport function quantile(sorted: ReadonlyArray<number>, p: number): number {\n\tif (sorted.length === 0) {\n\t\treturn Number.NaN;\n\t}\n\tconst position = (sorted.length - 1) * p;\n\tconst below = Math.floor(position);\n\tconst above = Math.ceil(position);\n\treturn sorted[below] + (sorted[above] - sorted[below]) * (position - below);\n}\n\nexport interface BoxSummary {\n\treadonly min: number;\n\treadonly q1: number;\n\treadonly median: number;\n\treadonly q3: number;\n\treadonly max: number;\n\t/** Whisker ends: the furthest values within 1.5 IQR of the box. */\n\treadonly low: number;\n\treadonly high: number;\n\treadonly outliers: ReadonlyArray<number>;\n\treadonly count: number;\n}\n\nconst TUKEY_FENCE = 1.5;\n\nexport function boxSummary(\n\tvalues: ReadonlyArray<number>,\n): BoxSummary | undefined {\n\tconst sorted = values\n\t\t.filter(Number.isFinite)\n\t\t.sort((left, right) => left - right);\n\tif (sorted.length === 0) {\n\t\treturn undefined;\n\t}\n\tconst q1 = quantile(sorted, 0.25);\n\tconst q3 = quantile(sorted, 0.75);\n\tconst fence = (q3 - q1) * TUKEY_FENCE;\n\tconst inside = sorted.filter(\n\t\t(value) => value >= q1 - fence && value <= q3 + fence,\n\t);\n\treturn {\n\t\tmin: sorted[0],\n\t\tq1,\n\t\tmedian: quantile(sorted, 0.5),\n\t\tq3,\n\t\tmax: sorted[sorted.length - 1],\n\t\tlow: inside[0] ?? q1,\n\t\thigh: inside[inside.length - 1] ?? q3,\n\t\toutliers: sorted.filter(\n\t\t\t(value) => value < q1 - fence || value > q3 + fence,\n\t\t),\n\t\tcount: sorted.length,\n\t};\n}\n\n/** Silverman's rule of thumb: a bandwidth that suits most unimodal data. */\nexport function silvermanBandwidth(values: ReadonlyArray<number>): number {\n\tconst count = values.length;\n\tif (count < 2) {\n\t\treturn 1;\n\t}\n\tconst mean = values.reduce((sum, value) => sum + value, 0) / count;\n\tconst deviation = Math.sqrt(\n\t\tvalues.reduce((sum, value) => sum + (value - mean) ** 2, 0) / (count - 1),\n\t);\n\tconst sorted = [...values].sort((left, right) => left - right);\n\tconst spread = Math.min(\n\t\tdeviation,\n\t\t(quantile(sorted, 0.75) - quantile(sorted, 0.25)) / 1.34,\n\t);\n\treturn 0.9 * (spread || deviation || 1) * count ** -0.2;\n}\n\nconst GAUSSIAN = 1 / Math.sqrt(2 * Math.PI);\n\n/** Gaussian kernel density of `values`, sampled at `at`. */\nexport function kernelDensity(\n\tvalues: ReadonlyArray<number>,\n\tat: ReadonlyArray<number>,\n\tbandwidth = silvermanBandwidth(values),\n): ReadonlyArray<number> {\n\tconst count = values.length;\n\treturn at.map((x) => {\n\t\tlet sum = 0;\n\t\tfor (const value of values) {\n\t\t\tconst u = (x - value) / bandwidth;\n\t\t\tsum += GAUSSIAN * Math.exp(-0.5 * u * u);\n\t\t}\n\t\treturn count === 0 ? 0 : sum / (count * bandwidth);\n\t});\n}\n\n/** `count` evenly spaced samples over `[low, high]`. */\nexport function samples(low: number, high: number, count: number): number[] {\n\tif (count < 2 || low === high) {\n\t\treturn [low];\n\t}\n\treturn Array.from(\n\t\t{ length: count },\n\t\t(_unused, index) => low + ((high - low) * index) / (count - 1),\n\t);\n}\n\nexport interface LinearFit {\n\treadonly slope: number;\n\treadonly intercept: number;\n\t/** Half-width of the 95 % confidence band of the mean at `x`. */\n\treadonly margin: (x: number) => number;\n}\n\n/** 1.96: a normal approximation of Student's t, fine past a dozen points. */\nconst Z_95 = 1.96;\n\nexport function linearFit(\n\txs: ReadonlyArray<number>,\n\tys: ReadonlyArray<number>,\n): LinearFit | undefined {\n\tconst count = xs.length;\n\tif (count < 2) {\n\t\treturn undefined;\n\t}\n\tconst meanX = xs.reduce((sum, x) => sum + x, 0) / count;\n\tconst meanY = ys.reduce((sum, y) => sum + y, 0) / count;\n\tlet sxx = 0;\n\tlet sxy = 0;\n\tfor (let index = 0; index < count; index += 1) {\n\t\tsxx += (xs[index] - meanX) ** 2;\n\t\tsxy += (xs[index] - meanX) * (ys[index] - meanY);\n\t}\n\tif (sxx === 0) {\n\t\treturn undefined;\n\t}\n\tconst slope = sxy / sxx;\n\tconst intercept = meanY - slope * meanX;\n\tlet residuals = 0;\n\tfor (let index = 0; index < count; index += 1) {\n\t\tresiduals += (ys[index] - (intercept + slope * xs[index])) ** 2;\n\t}\n\tconst standardError = count > 2 ? Math.sqrt(residuals / (count - 2)) : 0;\n\treturn {\n\t\tslope,\n\t\tintercept,\n\t\tmargin: (x) =>\n\t\t\tZ_95 * standardError * Math.sqrt(1 / count + (x - meanX) ** 2 / sxx),\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ChartMarkOptions } from "../shared.js";
|
|
2
|
+
import type { ChartAccessor, ChartMark, ChartValue } from "../../types.js";
|
|
3
|
+
export interface WaffleOptions<TDatum> extends ChartMarkOptions<TDatum> {
|
|
4
|
+
readonly x: ChartAccessor<TDatum, ChartValue>;
|
|
5
|
+
readonly y: ChartAccessor<TDatum, ChartValue>;
|
|
6
|
+
/** What one cell stands for. */
|
|
7
|
+
readonly unit?: number;
|
|
8
|
+
/** Cells across a band. */
|
|
9
|
+
readonly columns?: number;
|
|
10
|
+
readonly fill?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Bars made of countable cells: "12 missions" reads as twelve squares. A
|
|
14
|
+
* row of `columns` cells is `columns × unit` on the y axis, so the waffle
|
|
15
|
+
* still reads against it; series continue one another inside a column.
|
|
16
|
+
*/
|
|
17
|
+
export declare function waffleY<TDatum>(data: ReadonlyArray<TDatum>, options: WaffleOptions<TDatum>): ChartMark;
|
|
18
|
+
//# sourceMappingURL=waffle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waffle.d.ts","sourceRoot":"","sources":["../../../../src/core/marks/stats/waffle.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,gBAAgB,EAKrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EAET,UAAU,EAEV,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,aAAa,CAAC,MAAM,CAAE,SAAQ,gBAAgB,CAAC,MAAM,CAAC;IACtE,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC9C,gCAAgC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAC7B,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC5B,SAAS,CAiGX"}
|