ggaction 0.0.12 → 0.0.15
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/CHANGELOG.md +127 -0
- package/README.md +20 -11
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/action-cards.schema.json +1 -1
- package/knowledge/extension-authoring.md +2 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +10 -3
- package/src/actions/basic.js +4 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +13 -4
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/canvas/actions.js +3 -1
- package/src/actions/canvas/fitting.js +211 -0
- package/src/actions/canvas/index.js +2 -0
- package/src/actions/categoryOrder/index.js +18 -6
- package/src/actions/charts/area.js +51 -0
- package/src/actions/charts/bar.js +13 -6
- package/src/actions/charts/beeswarm.js +70 -0
- package/src/actions/charts/density.js +48 -0
- package/src/actions/charts/ecdf.js +186 -0
- package/src/actions/charts/endpoints.js +473 -0
- package/src/actions/charts/heatmap.js +4 -3
- package/src/actions/charts/histogram.js +5 -4
- package/src/actions/charts/horizon.js +73 -0
- package/src/actions/charts/index.js +37 -0
- package/src/actions/charts/interval-regression.js +172 -0
- package/src/actions/charts/line.js +8 -6
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +52 -0
- package/src/actions/charts/polar.js +134 -0
- package/src/actions/charts/radar.js +305 -0
- package/src/actions/charts/radial.js +50 -0
- package/src/actions/charts/raincloud.js +422 -0
- package/src/actions/charts/rug-strip.js +283 -0
- package/src/actions/charts/scatter.js +11 -3
- package/src/actions/charts/shared.js +69 -10
- package/src/actions/composition/actions.js +129 -3
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +29 -0
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +24 -0
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +111 -15
- package/src/actions/data/ecdf.js +38 -0
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +226 -30
- package/src/actions/data/fold.js +24 -0
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +38 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/intervalEdit.js +86 -4
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +19 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/stack.js +26 -0
- package/src/actions/data/summary.js +26 -0
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/appearance.js +32 -13
- package/src/actions/encodings/barWidth.js +8 -0
- package/src/actions/encodings/basic.js +2 -0
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/continuous.js +19 -59
- package/src/actions/encodings/color/index.js +30 -23
- package/src/actions/encodings/color/layout.js +3 -97
- package/src/actions/encodings/color/policy.js +5 -8
- package/src/actions/encodings/density/resolve.js +37 -5
- package/src/actions/encodings/density.js +124 -34
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/horizon/resolve.js +8 -4
- package/src/actions/encodings/horizon.js +18 -8
- package/src/actions/encodings/index.js +6 -0
- package/src/actions/encodings/offset.js +30 -29
- package/src/actions/encodings/parallel.js +2 -8
- package/src/actions/encodings/position/apply.js +63 -11
- package/src/actions/encodings/position/index.js +52 -6
- package/src/actions/encodings/position/policies/arc.js +14 -0
- package/src/actions/encodings/position/policies/area.js +4 -42
- package/src/actions/encodings/position/policies/bar.js +57 -13
- package/src/actions/encodings/position/resolve.js +88 -26
- package/src/actions/encodings/ranged.js +123 -97
- package/src/actions/encodings/remove.js +44 -34
- package/src/actions/encodings/ruleAppearance.js +24 -32
- package/src/actions/encodings/seriesLayout.js +105 -0
- package/src/actions/encodings/shared.js +107 -37
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/encodings/strokeDash.js +9 -6
- package/src/actions/encodings/temporal.js +7 -0
- package/src/actions/encodings/text.js +30 -15
- package/src/actions/errorBands/create.js +41 -8
- package/src/actions/errorBands/edit.js +360 -25
- package/src/actions/errorBars/create.js +56 -17
- package/src/actions/errorBars/edit.js +372 -21
- package/src/actions/facets/actions.js +619 -41
- package/src/actions/facets/derive.js +139 -25
- package/src/actions/gradientPlots/components.js +43 -13
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +8 -8
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +28 -19
- package/src/actions/guides/axes/axes.js +4 -3
- package/src/actions/guides/axes/axis.js +18 -18
- package/src/actions/guides/axes/components.js +43 -0
- package/src/actions/guides/axes/edit.js +16 -51
- package/src/actions/guides/axes/labels.js +184 -28
- package/src/actions/guides/axes/lines.js +13 -7
- package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
- package/src/actions/guides/axes/parallel/policy.js +137 -0
- package/src/actions/guides/axes/parallel/resolve.js +50 -25
- package/src/actions/guides/axes/parallel.js +93 -217
- package/src/actions/guides/axes/policy.js +21 -67
- package/src/actions/guides/axes/tickGroups.js +15 -8
- package/src/actions/guides/axes/ticks.js +11 -6
- package/src/actions/guides/axes/titles.js +32 -11
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facade.js +106 -0
- package/src/actions/guides/facadeAxes.js +200 -0
- package/src/actions/guides/facadeLegend.js +63 -0
- package/src/actions/guides/grids/grid.js +37 -33
- package/src/actions/guides/grids/resolve.js +17 -4
- package/src/actions/guides/guides.js +54 -31
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +309 -279
- package/src/actions/guides/legends/categorical/components.js +21 -17
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +123 -354
- package/src/actions/guides/legends/categorical/options.js +32 -16
- package/src/actions/guides/legends/categorical/recipes.js +114 -9
- package/src/actions/guides/legends/categorical/resolve.js +30 -15
- package/src/actions/guides/legends/categorical/symbols.js +87 -33
- package/src/actions/guides/legends/continuous/common.js +99 -26
- package/src/actions/guides/legends/continuous/gradient.js +120 -89
- package/src/actions/guides/legends/continuous/index.js +8 -2
- package/src/actions/guides/legends/continuous/interval.js +79 -95
- package/src/actions/guides/legends/continuous/opacity.js +113 -89
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +40 -0
- package/src/actions/guides/legends/edit.js +321 -90
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +149 -102
- package/src/actions/guides/legends/lifecycle.js +143 -0
- package/src/actions/guides/legends/remove.js +43 -78
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +213 -116
- package/src/actions/guides/legends/strokeWidth.js +121 -73
- package/src/actions/guides/legends/target.js +123 -0
- package/src/actions/guides/legends/transition.js +192 -0
- package/src/actions/guides/polar/axes/facade.js +81 -66
- package/src/actions/guides/polar/axes/labels.js +28 -8
- package/src/actions/guides/polar/axes/lines.js +12 -5
- package/src/actions/guides/polar/axes/shared.js +22 -0
- package/src/actions/guides/polar/axes/ticks.js +6 -5
- package/src/actions/guides/polar/axes/titles.js +6 -5
- package/src/actions/guides/polar/grids.js +8 -8
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/guides/reuse.js +44 -0
- package/src/actions/guides/tickValues.js +19 -21
- package/src/actions/index.js +15 -2
- package/src/actions/intervals/resolve.js +35 -15
- package/src/actions/marks/arc/actions.js +78 -20
- package/src/actions/marks/area/actions.js +51 -12
- package/src/actions/marks/area/materialize.js +22 -3
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +33 -9
- package/src/actions/marks/index.js +14 -0
- package/src/actions/marks/line/actions.js +71 -113
- package/src/actions/marks/line/materialize.js +57 -56
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +29 -21
- package/src/actions/marks/point/index.js +1 -0
- package/src/actions/marks/point/jitter.js +3 -0
- package/src/actions/marks/point/materialize.js +154 -16
- package/src/actions/marks/point/packing.js +68 -0
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +658 -0
- package/src/actions/marks/remove.js +87 -4
- package/src/actions/marks/rule/actions.js +77 -9
- package/src/actions/marks/rule/appearance.js +26 -0
- package/src/actions/marks/shared.js +13 -13
- package/src/actions/marks/text/actions.js +296 -13
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +156 -8
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/createGraphics.js +3 -1
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +3 -2
- package/src/actions/primitives/semanticAction.js +120 -10
- package/src/actions/primitives/semanticValidation/guide.js +10 -0
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +23 -1
- package/src/actions/primitives/semanticValidation/scale.js +38 -8
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +11 -3
- package/src/actions/regression/edit.js +40 -13
- package/src/actions/regression/resolve.js +1 -1
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +21 -54
- package/src/actions/scales/consumers/families.js +28 -14
- package/src/actions/scales/consumers/index.js +42 -4
- package/src/actions/scales/consumers/seriesLayout.js +57 -12
- package/src/actions/scales/create.js +58 -26
- package/src/actions/scales/definitions.js +109 -39
- package/src/actions/scales/edit.js +101 -34
- package/src/actions/scales/editPolicy.js +95 -35
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +14 -49
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +140 -0
- package/src/actions/scales/quantitativeColor.js +9 -5
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +98 -0
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +16 -0
- package/src/actions/theme/reconcile.js +1130 -0
- package/src/actions/theme/state.js +160 -0
- package/src/actions/titles/actions.js +11 -10
- package/src/actions/titles/resolve.js +1 -40
- package/src/actions/violinPlots/create.js +90 -43
- package/src/actions/violinPlots/edit.js +192 -0
- package/src/actions/violinPlots/index.js +2 -0
- package/src/core/ChartProgram.js +7 -0
- package/src/core/action.js +32 -2
- package/src/core/compositionState.js +141 -8
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/aggregate.js +66 -23
- package/src/grammar/arcs.js +58 -1
- package/src/grammar/areaEndpoints.js +46 -0
- package/src/grammar/areaSeries.js +87 -41
- package/src/grammar/bars/aggregate.js +37 -13
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bars/policy.js +9 -7
- package/src/grammar/bin.js +226 -0
- package/src/grammar/categoryOrder.js +34 -0
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +351 -0
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/ecdf.js +198 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +123 -52
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/fold.js +123 -0
- package/src/grammar/horizon.js +6 -4
- package/src/grammar/impute.js +340 -0
- package/src/grammar/interval.js +37 -20
- package/src/grammar/lineSeries.js +20 -63
- package/src/grammar/markFilter.js +22 -4
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +110 -0
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/parallelAxisTitles.js +16 -0
- package/src/grammar/pathSeries.js +109 -0
- package/src/grammar/pointPacking.js +183 -0
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/positionDatum.js +12 -0
- package/src/grammar/rects.js +9 -1
- package/src/grammar/regression/derive.js +4 -0
- package/src/grammar/regression/index.js +8 -2
- package/src/grammar/regression/models.js +22 -7
- package/src/grammar/regression/parameters.js +63 -13
- package/src/grammar/rotation.js +29 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +8 -21
- package/src/grammar/scales/color.js +23 -1
- package/src/grammar/scales/colorConsumers.js +57 -0
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +42 -5
- package/src/grammar/scales/discretized.js +11 -5
- package/src/grammar/scales/fields.js +38 -8
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/mapping.js +2 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/radial.js +55 -0
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/scales/types.js +1 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +23 -4
- package/src/grammar/stack.js +170 -0
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/statistics/confidenceInterval.js +101 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +245 -0
- package/src/grammar/text.js +22 -16
- package/src/grammar/timeUnit.js +234 -13
- package/src/grammar/transformTopology.js +10 -0
- package/src/grammar/transforms.js +418 -16
- package/src/grammar/valueFormat.js +112 -0
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +232 -21
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +100 -12
- package/src/layout/guideCollisions.js +12 -0
- package/src/layout/labels.js +343 -0
- package/src/layout/legend.js +2 -10
- package/src/layout/legendItems.js +102 -0
- package/src/layout/legendLane.js +100 -11
- package/src/materialization/bars/aggregate.js +56 -147
- package/src/materialization/bars/histogram.js +104 -20
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/categorySlotOffset.js +40 -0
- package/src/materialization/composition.js +3 -1
- package/src/materialization/compositionSnapshot.js +1 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +93 -16
- package/src/materialization/encodings.js +1 -1
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +23 -66
- package/src/materialization/guides/layout.js +57 -0
- package/src/materialization/guides/resources.js +28 -2
- package/src/materialization/legends.js +11 -2
- package/src/materialization/marks/capabilities.js +20 -5
- package/src/materialization/marks/grouping.js +52 -0
- package/src/materialization/marks/index.js +68 -27
- package/src/materialization/marks/policies.js +4 -1
- package/src/materialization/rect.js +55 -42
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/rowEncoding.js +13 -13
- package/src/materialization/scaleGuideDependencies.js +8 -5
- package/src/materialization/scales/map.js +6 -3
- package/src/materialization/scales/policies/arc.js +42 -0
- package/src/materialization/scales/policies/binnedPosition.js +7 -4
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +5 -1
- package/src/materialization/scales/resolve.js +65 -17
- package/src/materialization/selection/compatibility.js +11 -0
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +42 -71
- package/src/materialization/selection/items/common.js +27 -6
- package/src/materialization/selection/items/path.js +3 -13
- package/src/materialization/selection/items/rect.js +35 -8
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +324 -14
- package/src/mcp/adapter.js +2 -2
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/svg.js +19 -0
- package/src/selectors/datasets.js +44 -0
- package/src/selectors/layers.js +7 -2
- package/src/selectors/scales.js +6 -0
- package/src/theme/defaults.js +140 -0
- package/types/basic.d.ts +17 -2
- package/types/index.d.ts +211 -0
- package/types/program.d.ts +2182 -204
- package/src/materialization/bars/grouped.js +0 -144
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { cloneAndFreeze } from "../../core/immutable.js";
|
|
2
|
+
import { studentTCriticalValue } from "./studentT.js";
|
|
3
|
+
|
|
4
|
+
export const CONFIDENCE_INTERVAL_METHODS = Object.freeze([
|
|
5
|
+
"normal",
|
|
6
|
+
"student-t"
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
function validateMethod(method, label) {
|
|
10
|
+
if (!CONFIDENCE_INTERVAL_METHODS.includes(method)) {
|
|
11
|
+
throw new Error(`Unsupported ${label} method "${method}".`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function validateLevel(level, label) {
|
|
16
|
+
if (!Number.isFinite(level) || level <= 0 || level >= 1) {
|
|
17
|
+
throw new RangeError(`${label} level must be between 0 and 1.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Peter J. Acklam's inverse-normal approximation. The exact 95% compatibility
|
|
22
|
+
// value is handled by normalCriticalValue before this approximation is used.
|
|
23
|
+
function inverseNormal(probability) {
|
|
24
|
+
const a = [
|
|
25
|
+
-3.969683028665376e1, 2.209460984245205e2,
|
|
26
|
+
-2.759285104469687e2, 1.38357751867269e2,
|
|
27
|
+
-3.066479806614716e1, 2.506628277459239
|
|
28
|
+
];
|
|
29
|
+
const b = [
|
|
30
|
+
-5.447609879822406e1, 1.615858368580409e2,
|
|
31
|
+
-1.556989798598866e2, 6.680131188771972e1,
|
|
32
|
+
-1.328068155288572e1
|
|
33
|
+
];
|
|
34
|
+
const c = [
|
|
35
|
+
-7.784894002430293e-3, -3.223964580411365e-1,
|
|
36
|
+
-2.400758277161838, -2.549732539343734,
|
|
37
|
+
4.374664141464968, 2.938163982698783
|
|
38
|
+
];
|
|
39
|
+
const d = [
|
|
40
|
+
7.784695709041462e-3, 3.224671290700398e-1,
|
|
41
|
+
2.445134137142996, 3.754408661907416
|
|
42
|
+
];
|
|
43
|
+
const low = 0.02425;
|
|
44
|
+
const high = 1 - low;
|
|
45
|
+
if (probability < low) {
|
|
46
|
+
const q = Math.sqrt(-2 * Math.log(probability));
|
|
47
|
+
return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
48
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
49
|
+
}
|
|
50
|
+
if (probability > high) {
|
|
51
|
+
const q = Math.sqrt(-2 * Math.log(1 - probability));
|
|
52
|
+
return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
|
|
53
|
+
((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
|
|
54
|
+
}
|
|
55
|
+
const q = probability - 0.5;
|
|
56
|
+
const r = q * q;
|
|
57
|
+
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
|
|
58
|
+
(((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeConfidenceInterval({
|
|
62
|
+
method,
|
|
63
|
+
level
|
|
64
|
+
} = {}, {
|
|
65
|
+
defaultMethod,
|
|
66
|
+
defaultLevel = 0.95,
|
|
67
|
+
label = "Confidence interval"
|
|
68
|
+
} = {}) {
|
|
69
|
+
validateMethod(defaultMethod, label);
|
|
70
|
+
const resolvedMethod = method ?? defaultMethod;
|
|
71
|
+
const resolvedLevel = level ?? defaultLevel;
|
|
72
|
+
validateMethod(resolvedMethod, label);
|
|
73
|
+
validateLevel(resolvedLevel, label);
|
|
74
|
+
return cloneAndFreeze({ method: resolvedMethod, level: resolvedLevel });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function normalCriticalValue(level) {
|
|
78
|
+
validateLevel(level, "Normal confidence interval");
|
|
79
|
+
if (level === 0.95) return 1.96;
|
|
80
|
+
return inverseNormal((1 + level) / 2);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function confidenceCriticalValue({
|
|
84
|
+
method,
|
|
85
|
+
level,
|
|
86
|
+
degreesOfFreedom
|
|
87
|
+
}) {
|
|
88
|
+
const normalized = normalizeConfidenceInterval(
|
|
89
|
+
{ method, level },
|
|
90
|
+
{ defaultMethod: method, label: "Confidence interval" }
|
|
91
|
+
);
|
|
92
|
+
if (normalized.method === "normal") {
|
|
93
|
+
return normalCriticalValue(normalized.level);
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isInteger(degreesOfFreedom) || degreesOfFreedom < 1) {
|
|
96
|
+
throw new RangeError(
|
|
97
|
+
"Student-t confidence interval degreesOfFreedom must be a positive integer."
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return studentTCriticalValue(normalized.level, degreesOfFreedom);
|
|
101
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { freezeOwned, isPlainObject } from "../core/immutable.js";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_LINE_CAP = "butt";
|
|
4
|
+
export const DEFAULT_LINE_JOIN = "miter";
|
|
5
|
+
export const DEFAULT_MITER_LIMIT = 10;
|
|
6
|
+
|
|
7
|
+
export const LINE_CAPS = Object.freeze(["butt", "round", "square"]);
|
|
8
|
+
export const LINE_JOINS = Object.freeze(["miter", "round", "bevel"]);
|
|
9
|
+
export const STROKE_STYLE_PROPERTIES = Object.freeze([
|
|
10
|
+
"lineCap",
|
|
11
|
+
"lineJoin",
|
|
12
|
+
"miterLimit"
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
function validateEnum(value, values, property, label) {
|
|
16
|
+
if (typeof value !== "string") {
|
|
17
|
+
throw new TypeError(`${label} ${property} must be a string.`);
|
|
18
|
+
}
|
|
19
|
+
if (!values.includes(value)) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`${label} ${property} must be one of ${values.join(", ")}.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function validateLineCap(value, label = "Stroke style") {
|
|
28
|
+
return validateEnum(value, LINE_CAPS, "lineCap", label);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function validateLineJoin(value, label = "Stroke style") {
|
|
32
|
+
return validateEnum(value, LINE_JOINS, "lineJoin", label);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function validateMiterLimit(value, label = "Stroke style") {
|
|
36
|
+
if (!Number.isFinite(value)) {
|
|
37
|
+
throw new TypeError(`${label} miterLimit must be a finite number.`);
|
|
38
|
+
}
|
|
39
|
+
if (!(value > 0)) {
|
|
40
|
+
throw new RangeError(`${label} miterLimit must be positive.`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveStrokeDetails(request = {}) {
|
|
46
|
+
if (!isPlainObject(request)) {
|
|
47
|
+
throw new TypeError("Stroke style details must be a plain object.");
|
|
48
|
+
}
|
|
49
|
+
return freezeOwned({
|
|
50
|
+
lineCap: request.lineCap === undefined
|
|
51
|
+
? DEFAULT_LINE_CAP
|
|
52
|
+
: validateLineCap(request.lineCap),
|
|
53
|
+
lineJoin: request.lineJoin === undefined
|
|
54
|
+
? DEFAULT_LINE_JOIN
|
|
55
|
+
: validateLineJoin(request.lineJoin),
|
|
56
|
+
miterLimit: request.miterLimit === undefined
|
|
57
|
+
? DEFAULT_MITER_LIMIT
|
|
58
|
+
: validateMiterLimit(request.miterLimit)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function requestedStrokeDetails(request = {}, label = "Stroke style") {
|
|
63
|
+
if (!isPlainObject(request)) {
|
|
64
|
+
throw new TypeError(`${label} details must be a plain object.`);
|
|
65
|
+
}
|
|
66
|
+
const result = {};
|
|
67
|
+
if (Object.hasOwn(request, "lineCap")) {
|
|
68
|
+
result.lineCap = validateLineCap(request.lineCap, label);
|
|
69
|
+
}
|
|
70
|
+
if (Object.hasOwn(request, "lineJoin")) {
|
|
71
|
+
result.lineJoin = validateLineJoin(request.lineJoin, label);
|
|
72
|
+
}
|
|
73
|
+
if (Object.hasOwn(request, "miterLimit")) {
|
|
74
|
+
result.miterLimit = validateMiterLimit(request.miterLimit, label);
|
|
75
|
+
}
|
|
76
|
+
return freezeOwned(result);
|
|
77
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
|
|
2
|
+
import {
|
|
3
|
+
aggregateRows,
|
|
4
|
+
isScalarAggregate,
|
|
5
|
+
validateAggregate,
|
|
6
|
+
validateAggregateFieldValues
|
|
7
|
+
} from "./aggregate.js";
|
|
8
|
+
import {
|
|
9
|
+
calculateWeightedAggregate,
|
|
10
|
+
normalizeStatisticalWeight,
|
|
11
|
+
readStatisticalWeights,
|
|
12
|
+
summarizeStatisticalWeights,
|
|
13
|
+
validateWeightedAggregate,
|
|
14
|
+
validateWeightedNumericFields,
|
|
15
|
+
weightedRows
|
|
16
|
+
} from "./weightedStatistics.js";
|
|
17
|
+
|
|
18
|
+
const TRANSFORM_KEYS = Object.freeze([
|
|
19
|
+
"type", "groupBy", "aggregates", "members", "weight"
|
|
20
|
+
]);
|
|
21
|
+
const AGGREGATE_KEYS = Object.freeze(["op", "field", "as"]);
|
|
22
|
+
const NOMINAL_OPERATIONS = new Set(["distinct", "valid", "missing"]);
|
|
23
|
+
const MAX_OUTPUT_ROWS = 10_000;
|
|
24
|
+
|
|
25
|
+
function rejectUnknownKeys(value, supported, label) {
|
|
26
|
+
const unknown = Object.keys(value).find(key => !supported.includes(key));
|
|
27
|
+
if (unknown !== undefined) {
|
|
28
|
+
throw new Error(`Unknown ${label} property "${unknown}".`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function requireField(value, label) {
|
|
33
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
34
|
+
throw new TypeError(`${label} must be a non-empty string.`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeGroupBy(value) {
|
|
40
|
+
if (value === undefined) return [];
|
|
41
|
+
return Array.isArray(value) ? [...value] : [value];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeAggregate(value) {
|
|
45
|
+
if (!isPlainObject(value)) return value;
|
|
46
|
+
return {
|
|
47
|
+
...value,
|
|
48
|
+
op: validateAggregate(value.op),
|
|
49
|
+
...(Object.hasOwn(value, "field") ? { field: value.field } : {}),
|
|
50
|
+
as: value.as
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function normalizeSummaryTransform({ groupBy, aggregates, members, weight } = {}) {
|
|
55
|
+
const transform = {
|
|
56
|
+
type: "summary",
|
|
57
|
+
groupBy: normalizeGroupBy(groupBy),
|
|
58
|
+
aggregates: Array.isArray(aggregates)
|
|
59
|
+
? aggregates.map(normalizeAggregate)
|
|
60
|
+
: aggregates,
|
|
61
|
+
...(members === undefined ? {} : { members }),
|
|
62
|
+
...(weight === undefined
|
|
63
|
+
? {}
|
|
64
|
+
: { weight: normalizeStatisticalWeight(weight, "Summary weight") })
|
|
65
|
+
};
|
|
66
|
+
validateSummaryTransform(transform);
|
|
67
|
+
return cloneAndFreeze(transform);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function validateSummaryTransform(transform) {
|
|
71
|
+
if (!isPlainObject(transform)) {
|
|
72
|
+
throw new TypeError("Summary transform must be a plain object.");
|
|
73
|
+
}
|
|
74
|
+
rejectUnknownKeys(transform, TRANSFORM_KEYS, "summary transform");
|
|
75
|
+
if (transform.type !== "summary") {
|
|
76
|
+
throw new Error(`Unsupported summary transform "${transform.type}".`);
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
!Array.isArray(transform.groupBy) ||
|
|
80
|
+
transform.groupBy.some(field => typeof field !== "string" || field.length === 0)
|
|
81
|
+
) {
|
|
82
|
+
throw new TypeError("Summary groupBy must contain field names.");
|
|
83
|
+
}
|
|
84
|
+
if (new Set(transform.groupBy).size !== transform.groupBy.length) {
|
|
85
|
+
throw new Error("Summary groupBy fields must be unique.");
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(transform.aggregates) || transform.aggregates.length === 0) {
|
|
88
|
+
throw new TypeError("Summary aggregates must be a non-empty array.");
|
|
89
|
+
}
|
|
90
|
+
if (transform.aggregates.length > 64) {
|
|
91
|
+
throw new RangeError("Summary aggregates cannot contain more than 64 outputs.");
|
|
92
|
+
}
|
|
93
|
+
const outputs = new Set(transform.groupBy);
|
|
94
|
+
transform.aggregates.forEach((aggregate, index) => {
|
|
95
|
+
if (!isPlainObject(aggregate)) {
|
|
96
|
+
throw new TypeError(`Summary aggregate ${index} must be a plain object.`);
|
|
97
|
+
}
|
|
98
|
+
rejectUnknownKeys(aggregate, AGGREGATE_KEYS, `summary aggregate ${index}`);
|
|
99
|
+
const operation = validateAggregate(aggregate.op);
|
|
100
|
+
if (transform.weight !== undefined) validateWeightedAggregate(operation);
|
|
101
|
+
if (operation === "count") {
|
|
102
|
+
if (Object.hasOwn(aggregate, "field")) {
|
|
103
|
+
throw new Error("Summary count does not accept a field.");
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
requireField(aggregate.field, `Summary aggregate ${index} field`);
|
|
107
|
+
}
|
|
108
|
+
const output = requireField(aggregate.as, `Summary aggregate ${index} as`);
|
|
109
|
+
if (outputs.has(output)) {
|
|
110
|
+
throw new Error(`Summary output field "${output}" collides with another output.`);
|
|
111
|
+
}
|
|
112
|
+
outputs.add(output);
|
|
113
|
+
});
|
|
114
|
+
if (transform.members !== undefined) {
|
|
115
|
+
const members = requireField(transform.members, "Summary members");
|
|
116
|
+
if (outputs.has(members)) {
|
|
117
|
+
throw new Error(`Summary members field "${members}" collides with another output.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (transform.weight !== undefined) {
|
|
121
|
+
normalizeStatisticalWeight(transform.weight, "Summary weight");
|
|
122
|
+
}
|
|
123
|
+
return transform;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function scalarKey(value, label) {
|
|
127
|
+
if (value === null) return "null";
|
|
128
|
+
if (typeof value === "string") return `string:${value.length}:${value}`;
|
|
129
|
+
if (typeof value === "boolean") return `boolean:${value}`;
|
|
130
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
131
|
+
return `number:${Object.is(value, -0) ? 0 : value}`;
|
|
132
|
+
}
|
|
133
|
+
throw new TypeError(
|
|
134
|
+
`${label} must contain null, strings, booleans, or finite numbers.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function requireSourceFields(rows, transform) {
|
|
139
|
+
if (rows.length === 0) return;
|
|
140
|
+
const fields = new Set(rows.flatMap(row => Object.keys(row)));
|
|
141
|
+
for (const field of transform.groupBy) {
|
|
142
|
+
if (!fields.has(field)) {
|
|
143
|
+
throw new Error(`Summary source does not contain group field "${field}".`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const aggregate of transform.aggregates) {
|
|
147
|
+
if (aggregate.field !== undefined && !fields.has(aggregate.field)) {
|
|
148
|
+
throw new Error(`Summary source does not contain aggregate field "${aggregate.field}".`);
|
|
149
|
+
}
|
|
150
|
+
const orderBy = isPlainObject(aggregate.op) ? aggregate.op.orderBy : undefined;
|
|
151
|
+
if (orderBy !== undefined && !fields.has(orderBy)) {
|
|
152
|
+
throw new Error(`Summary source does not contain order field "${orderBy}".`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (transform.weight !== undefined && !fields.has(transform.weight.field)) {
|
|
156
|
+
throw new Error(`Summary source does not contain weight field "${transform.weight.field}".`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function validateAggregateValues(rows, aggregate) {
|
|
161
|
+
if (aggregate.op === "count") return;
|
|
162
|
+
if (isScalarAggregate(aggregate.op) && NOMINAL_OPERATIONS.has(aggregate.op)) {
|
|
163
|
+
validateAggregateFieldValues(rows, aggregate.field, "nominal");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
validateAggregateFieldValues(rows, aggregate.field, "quantitative");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function deriveSummaryRows(rows, transform) {
|
|
170
|
+
validateSummaryTransform(transform);
|
|
171
|
+
requireSourceFields(rows, transform);
|
|
172
|
+
let weightEntries;
|
|
173
|
+
if (transform.weight === undefined) {
|
|
174
|
+
for (const aggregate of transform.aggregates) {
|
|
175
|
+
validateAggregateValues(rows, aggregate);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
weightEntries = readStatisticalWeights(rows, transform.weight, "Summary").entries;
|
|
179
|
+
validateWeightedNumericFields(
|
|
180
|
+
weightEntries,
|
|
181
|
+
[...new Set(transform.aggregates.flatMap(aggregate =>
|
|
182
|
+
aggregate.field === undefined ? [] : [aggregate.field]
|
|
183
|
+
))],
|
|
184
|
+
"Summary"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const groups = new Map();
|
|
189
|
+
if (transform.groupBy.length === 0) {
|
|
190
|
+
groups.set("all", {
|
|
191
|
+
values: {},
|
|
192
|
+
rows,
|
|
193
|
+
...(weightEntries === undefined ? {} : { entries: weightEntries })
|
|
194
|
+
});
|
|
195
|
+
} else {
|
|
196
|
+
rows.forEach((row, index) => {
|
|
197
|
+
const values = Object.fromEntries(transform.groupBy.map(field => [field, row[field]]));
|
|
198
|
+
const key = transform.groupBy.map(field =>
|
|
199
|
+
scalarKey(row[field], `Summary group field "${field}"`)
|
|
200
|
+
).join("|");
|
|
201
|
+
const group = groups.get(key) ?? {
|
|
202
|
+
values,
|
|
203
|
+
rows: [],
|
|
204
|
+
...(weightEntries === undefined ? {} : { entries: [] })
|
|
205
|
+
};
|
|
206
|
+
group.rows.push(row);
|
|
207
|
+
if (weightEntries !== undefined) group.entries.push(weightEntries[index]);
|
|
208
|
+
groups.set(key, group);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (groups.size > MAX_OUTPUT_ROWS) {
|
|
212
|
+
throw new RangeError(`Summary output cannot exceed ${MAX_OUTPUT_ROWS} groups.`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return [...groups.values()].map(group => {
|
|
216
|
+
const weightSummary = transform.weight === undefined
|
|
217
|
+
? undefined
|
|
218
|
+
: summarizeStatisticalWeights(
|
|
219
|
+
group.entries,
|
|
220
|
+
transform.weight.kind,
|
|
221
|
+
"Summary group"
|
|
222
|
+
);
|
|
223
|
+
return {
|
|
224
|
+
...group.values,
|
|
225
|
+
...Object.fromEntries(transform.aggregates.map(aggregate => [
|
|
226
|
+
aggregate.as,
|
|
227
|
+
weightSummary === undefined
|
|
228
|
+
? aggregateRows(group.rows, aggregate.field ?? "__row", aggregate.op)
|
|
229
|
+
: calculateWeightedAggregate(
|
|
230
|
+
weightSummary,
|
|
231
|
+
aggregate.field,
|
|
232
|
+
aggregate.op,
|
|
233
|
+
`Summary aggregate "${aggregate.as}"`
|
|
234
|
+
)
|
|
235
|
+
])),
|
|
236
|
+
...(transform.members === undefined
|
|
237
|
+
? {}
|
|
238
|
+
: {
|
|
239
|
+
[transform.members]: weightSummary === undefined
|
|
240
|
+
? group.rows
|
|
241
|
+
: weightedRows(weightSummary)
|
|
242
|
+
})
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
package/src/grammar/text.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { cloneAndFreeze } from "../core/immutable.js";
|
|
2
2
|
import { validateConcreteGraphicValue } from "./schemas/concreteGraphic.js";
|
|
3
|
+
import {
|
|
4
|
+
formatValue,
|
|
5
|
+
isUtcValueFormat,
|
|
6
|
+
validateValueFormat
|
|
7
|
+
} from "./valueFormat.js";
|
|
8
|
+
import { resolveRotation } from "./rotation.js";
|
|
9
|
+
|
|
10
|
+
export function isSourceOwnedText(layer) {
|
|
11
|
+
return layer?.mark?.type === "text" && layer.source !== undefined;
|
|
12
|
+
}
|
|
3
13
|
|
|
4
14
|
export const DEFAULT_TEXT_MARK = cloneAndFreeze({
|
|
5
15
|
fill: "#334155",
|
|
@@ -14,20 +24,13 @@ export const DEFAULT_TEXT_MARK = cloneAndFreeze({
|
|
|
14
24
|
dy: 0
|
|
15
25
|
});
|
|
16
26
|
|
|
17
|
-
const FIXED_FORMAT = /^\.(\d{1,2})f$/;
|
|
18
|
-
|
|
19
27
|
export function validateTextFormat(format) {
|
|
20
|
-
if (format
|
|
21
|
-
if (typeof format !== "string" || !FIXED_FORMAT.test(format)) {
|
|
28
|
+
if (format !== undefined && typeof format !== "string") {
|
|
22
29
|
throw new Error(
|
|
23
|
-
'Text format must be "auto" or a
|
|
30
|
+
'Text format must be "auto" or a supported numeric/UTC format string.'
|
|
24
31
|
);
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
if (decimals > 12) {
|
|
28
|
-
throw new RangeError("Text fixed-decimal format supports at most 12 decimals.");
|
|
29
|
-
}
|
|
30
|
-
return format;
|
|
33
|
+
return validateValueFormat(format, "Text format");
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export function formatTextValue(value, format = "auto") {
|
|
@@ -37,10 +40,11 @@ export function formatTextValue(value, format = "auto") {
|
|
|
37
40
|
const text = String(value);
|
|
38
41
|
return text.length === 0 ? undefined : text;
|
|
39
42
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
return formatValue(value, {
|
|
44
|
+
format: resolved,
|
|
45
|
+
valueType: isUtcValueFormat(resolved) ? "temporal" : "quantitative",
|
|
46
|
+
label: "Text format"
|
|
47
|
+
});
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export function normalizeTextMarkConfig(options, base = DEFAULT_TEXT_MARK) {
|
|
@@ -52,14 +56,16 @@ export function normalizeTextMarkConfig(options, base = DEFAULT_TEXT_MARK) {
|
|
|
52
56
|
fontFamily: "fontFamily",
|
|
53
57
|
fontWeight: "fontWeight",
|
|
54
58
|
align: "textAlign",
|
|
55
|
-
baseline: "textBaseline"
|
|
56
|
-
rotation: "rotation"
|
|
59
|
+
baseline: "textBaseline"
|
|
57
60
|
};
|
|
58
61
|
for (const [option, property] of Object.entries(mapping)) {
|
|
59
62
|
if (!Object.hasOwn(options, option)) continue;
|
|
60
63
|
validateConcreteGraphicValue("text", property, options[option]);
|
|
61
64
|
config[option] = options[option];
|
|
62
65
|
}
|
|
66
|
+
if (Object.hasOwn(options, "rotation")) {
|
|
67
|
+
config.rotation = resolveRotation(options.rotation, "Text rotation");
|
|
68
|
+
}
|
|
63
69
|
for (const property of ["dx", "dy"]) {
|
|
64
70
|
if (!Object.hasOwn(options, property)) continue;
|
|
65
71
|
if (!Number.isFinite(options[property])) {
|