Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sgratzl/chartjs-chart-boxplot

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sgratzl/chartjs-chart-boxplot - npm Package Compare versions

Comparing version 3.0.0-beta.9 to 3.0.0-rc.0

build/__tests__/createChart.d.ts

381

build/index.d.ts

@@ -1,378 +0,3 @@

/**
* @sgratzl/chartjs-chart-boxplot
* https://github.com/sgratzl/chartjs-chart-boxplot
*
* Copyright (c) 2021 Samuel Gratzl <sam@sgratzl.com>
*/
import { TooltipModel, Element, BarController, Scale, ChartMeta, UpdateMode, ControllerDatasetOptions, ScriptableAndArrayOptions, ScriptableContext, CommonHoverOptions, CartesianScaleTypeRegistry, Chart, ChartItem, ChartConfiguration } from 'chart.js';
interface ExtendedTooltip extends TooltipModel {
_tooltipOutlier?: {
index: number;
datasetIndex: number;
};
}
interface IStatsBaseOptions {
/**
* @default see rectangle
* @scriptable
* @indexable
*/
backgroundColor: string;
/**
* @default see rectangle
* @scriptable
* @indexable
*/
borderColor: string;
/**
* @default 1
* @scriptable
* @indexable
*/
borderWidth: number;
/**
* item style used to render outliers
* @default circle
*/
outlierStyle: 'circle' | 'triangle' | 'rect' | 'rectRounded' | 'rectRot' | 'cross' | 'crossRot' | 'star' | 'line' | 'dash';
/**
* radius used to render outliers
* @default 2
* @scriptable
* @indexable
*/
outlierRadius: number;
/**
* @default see rectangle.backgroundColor
* @scriptable
* @indexable
*/
outlierBackgroundColor: string;
/**
* @default see rectangle.borderColor
* @scriptable
* @indexable
*/
outlierBorderColor: string;
/**
* @default 1
* @scriptable
* @indexable
*/
outlierBorderWidth: number;
/**
* item style used to render items
* @default circle
*/
itemStyle: 'circle' | 'triangle' | 'rect' | 'rectRounded' | 'rectRot' | 'cross' | 'crossRot' | 'star' | 'line' | 'dash';
/**
* radius used to render items
* @default 0 so disabled
* @scriptable
* @indexable
*/
itemRadius: number;
/**
* background color for items
* @default see rectangle.backgroundColor
* @scriptable
* @indexable
*/
itemBackgroundColor: string;
/**
* border color for items
* @default see rectangle.borderColor
* @scriptable
* @indexable
*/
itemBorderColor: string;
/**
* border width for items
* @default 0
* @scriptable
* @indexable
*/
itemBorderWidth: number;
/**
* padding that is added around the bounding box when computing a mouse hit
* @default 2
* @scriptable
* @indexable
*/
hitPadding: number;
/**
* hit radius for hit test of outliers
* @default 4
* @scriptable
* @indexable
*/
outlierHitRadius: number;
}
interface IStatsBaseProps {
x: number;
y: number;
width: number;
height: number;
items: number[];
outliers: number[];
}
declare class StatsBase<T extends IStatsBaseProps, O extends IStatsBaseOptions> extends Element<T, O> {
_datasetIndex: number;
_index: number;
isVertical(): boolean;
_drawItems(ctx: CanvasRenderingContext2D): void;
_drawOutliers(ctx: CanvasRenderingContext2D): void;
_getBounds(_useFinalPosition?: boolean): {
left: number;
top: number;
right: number;
bottom: number;
};
_getHitBounds(useFinalPosition?: boolean): {
left: number;
top: number;
right: number;
bottom: number;
};
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
_outlierIndexInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): number;
_boxInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
getCenterPoint(useFinalPosition?: boolean): {
x: T["x"];
y: T["y"];
};
_getOutliers(useFinalPosition?: boolean): T["outliers"];
tooltipPosition(eventPosition?: {
x: number;
y: number;
} | boolean, tooltip?: ExtendedTooltip): {
x: number;
y: number;
};
}
interface IBoxAndWhiskersOptions extends IStatsBaseOptions {
/**
* separate color for the median line
* @default 'transparent' takes the current borderColor
* @scriptable
* @indexable
*/
medianColor: string;
/**
* color the lower half (median-q3) of the box in a different color
* @default 'transparent' takes the current borderColor
* @scriptable
* @indexable
*/
lowerBackgroundColor: string;
}
interface IBoxAndWhiskerProps extends IStatsBaseProps {
q1: number;
q3: number;
median: number;
whiskerMin: number;
whiskerMax: number;
mean: number;
}
declare class BoxAndWiskers extends StatsBase<IBoxAndWhiskerProps, IBoxAndWhiskersOptions> {
draw(ctx: CanvasRenderingContext2D): void;
_drawBoxPlot(ctx: CanvasRenderingContext2D): void;
_drawBoxPlotVertical(ctx: CanvasRenderingContext2D): void;
_drawBoxPlotHorizontal(ctx: CanvasRenderingContext2D): void;
_getBounds(useFinalPosition?: boolean): {
left: number;
top: number;
right: number;
bottom: number;
};
static id: string;
static defaults: any;
static defaultRoutes: {
[property: string]: string;
} & {
outlierBackgroundColor: string;
outlierBorderColor: string;
itemBackgroundColor: string;
itemBorderColor: string;
};
}
interface IBaseStats {
min: number;
max: number;
q1: number;
q3: number;
median: number;
}
interface IBoxPlot extends IBaseStats {
items: readonly number[];
outliers: readonly number[];
whiskerMax: number;
whiskerMin: number;
mean: number;
}
interface IKDEPoint {
v: number;
estimate: number;
}
interface IViolin extends IBaseStats {
items: readonly number[];
maxEstimate: number;
coords: IKDEPoint[];
outliers: readonly number[];
}
declare type QuantileMethod = 7 | 'quantiles' | 'hinges' | 'fivenum' | 'linear' | 'lower' | 'higher' | 'nearest' | 'midpoint' | ((arr: ArrayLike<number>, length?: number | undefined) => {
q1: number;
median: number;
q3: number;
});
interface IBaseOptions {
/**
* statistic measure that should be used for computing the minimal data limit
* @default 'min'
*/
minStats?: 'min' | 'q1' | 'whiskerMin';
/**
* statistic measure that should be used for computing the maximal data limit
* @default 'max'
*/
maxStats?: 'max' | 'q3' | 'whiskerMax';
/**
* from the R doc: this determines how far the plot ‘whiskers’ extend out from
* the box. If coef is positive, the whiskers extend to the most extreme data
* point which is no more than coef times the length of the box away from the
* box. A value of zero causes the whiskers to extend to the data extremes
* @default 1.5
*/
coef?: number;
/**
* the method to compute the quantiles.
*
* 7, 'quantiles': the type-7 method as used by R 'quantiles' method.
* 'hinges' and 'fivenum': the method used by R 'boxplot.stats' method.
* 'linear': the interpolation method 'linear' as used by 'numpy.percentile' function
* 'lower': the interpolation method 'lower' as used by 'numpy.percentile' function
* 'higher': the interpolation method 'higher' as used by 'numpy.percentile' function
* 'nearest': the interpolation method 'nearest' as used by 'numpy.percentile' function
* 'midpoint': the interpolation method 'midpoint' as used by 'numpy.percentile' function
* @default 7
*/
quantiles?: QuantileMethod;
}
declare type IBoxplotOptions = IBaseOptions;
interface IViolinOptions extends IBaseOptions {
/**
* number of points that should be samples of the KDE
* @default 100
*/
points: number;
}
declare type IViolinElementOptions = IStatsBaseOptions;
interface IViolinElementProps extends IStatsBaseProps {
min: number;
max: number;
coords: IKDEPoint[];
maxEstimate: number;
}
declare class Violin extends StatsBase<IViolinElementProps, IViolinElementOptions> {
draw(ctx: CanvasRenderingContext2D): void;
_drawCoords(ctx: CanvasRenderingContext2D, props: IViolinElementProps): void;
_getBounds(useFinalPosition?: boolean): {
left: number;
top: number;
right: number;
bottom: number;
};
static id: string;
static defaults: any;
static defaultRoutes: {
[property: string]: string;
} & {
outlierBackgroundColor: string;
outlierBorderColor: string;
itemBackgroundColor: string;
itemBorderColor: string;
};
}
declare abstract class StatsBase$1<S extends {
median: number;
}, C extends Required<IBaseOptions>> extends BarController {
_config: C;
getMinMax(scale: Scale, canStack?: boolean | undefined): {
min: number;
max: number;
};
parsePrimitiveData(meta: ChartMeta, data: any[], start: number, count: number): any[];
parseArrayData(meta: ChartMeta, data: any[], start: number, count: number): any[];
parseObjectData(meta: ChartMeta, data: any[], start: number, count: number): any[];
protected abstract _parseStats(value: any, config: C): S;
getLabelAndValue(index: number): any;
protected abstract _toStringStats(b: S): string;
protected abstract _transformStats<T>(target: Record<keyof S, T | T[]>, source: S, mapper: (v: number) => T, mode: UpdateMode | 'string'): void;
updateElement(rectangle: Element, index: number, properties: any, mode: UpdateMode): void;
}
declare class BoxPlotController extends StatsBase$1<IBoxPlot, Required<IBoxplotOptions>> {
_parseStats(value: any, config: IBoxplotOptions): any;
_toStringStats(b: IBoxPlot): string;
_transformStats<T>(target: any, source: IBoxPlot, mapper: (v: number) => T): void;
static readonly id = "boxplot";
static readonly defaults: any;
}
interface BoxPlotControllerDatasetOptions extends ControllerDatasetOptions, IBoxplotOptions, ScriptableAndArrayOptions<IBoxAndWhiskersOptions, ScriptableContext>, ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext> {
}
declare type BoxPlotDataPoint = number[] | (Partial<IBoxPlot> & IBaseStats);
interface IBoxPlotChartOptions {
}
declare module 'chart.js' {
interface ChartTypeRegistry {
boxplot: {
chartOptions: IBoxPlotChartOptions;
datasetOptions: BoxPlotControllerDatasetOptions;
defaultDataPoint: BoxPlotDataPoint[];
scales: keyof CartesianScaleTypeRegistry;
};
}
}
declare class BoxPlotChart<DATA extends unknown[] = BoxPlotDataPoint[], LABEL = string> extends Chart<'boxplot', DATA, LABEL> {
static id: string;
constructor(item: ChartItem, config: Omit<ChartConfiguration<'boxplot', DATA, LABEL>, 'type'>);
}
declare class ViolinController extends StatsBase$1<IViolin, Required<IViolinOptions>> {
_parseStats(value: any, config: IViolinOptions): any;
_toStringStats(v: IViolin): string;
_transformStats<T>(target: any, source: IViolin, mapper: (v: number) => T): void;
static readonly id = "violin";
static readonly defaults: any;
}
interface ViolinControllerDatasetOptions extends ControllerDatasetOptions, IViolinOptions, ScriptableAndArrayOptions<IViolinElementOptions, ScriptableContext>, ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext> {
}
declare type ViolinDataPoint = number[] | (Partial<IViolin> & IBaseStats);
interface IViolinChartOptions {
}
declare module 'chart.js' {
interface ChartTypeRegistry {
violin: {
chartOptions: IViolinChartOptions;
datasetOptions: ViolinControllerDatasetOptions;
defaultDataPoint: ViolinDataPoint[];
scales: keyof CartesianScaleTypeRegistry;
};
}
}
declare class ViolinChart<DATA extends unknown[] = ViolinDataPoint[], LABEL = string> extends Chart<'violin', DATA, LABEL> {
static id: string;
constructor(item: ChartItem, config: Omit<ChartConfiguration<'violin', DATA, LABEL>, 'type'>);
}
export { BoxAndWiskers, BoxPlotChart, BoxPlotController, BoxPlotControllerDatasetOptions, BoxPlotDataPoint, IBoxAndWhiskerProps, IBoxAndWhiskersOptions, IBoxPlotChartOptions, IStatsBaseOptions, IStatsBaseProps, IViolinChartOptions, IViolinElementOptions, IViolinElementProps, StatsBase, Violin, ViolinChart, ViolinController, ViolinControllerDatasetOptions, ViolinDataPoint };
//# sourceMappingURL=index.d.ts.map
export * from './elements';
export * from './controllers';
//# sourceMappingURL=index.d.ts.map

@@ -8,14 +8,6 @@ /**

'use strict';
import { Element, BarElement, Tooltip, BarController, registry, Chart, LinearScale, CategoryScale } from 'chart.js';
import { drawPoint, merge } from 'chart.js/helpers';
import boxplots, { quantilesType7, quantilesHinges, quantilesFivenum, quantilesLinear, quantilesLower, quantilesHigher, quantilesNearest, quantilesMidpoint } from '@sgratzl/boxplots';
Object.defineProperty(exports, '__esModule', { value: true });
var chart_js = require('chart.js');
var helpers = require('chart.js/helpers');
var boxplots = require('@sgratzl/boxplots');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var boxplots__default = /*#__PURE__*/_interopDefaultLegacy(boxplots);
function gaussian(u) {

@@ -89,17 +81,17 @@ return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * u * u);

const lookup = {
hinges: boxplots.quantilesHinges,
fivenum: boxplots.quantilesFivenum,
7: boxplots.quantilesType7,
quantiles: boxplots.quantilesType7,
linear: boxplots.quantilesLinear,
lower: boxplots.quantilesLower,
higher: boxplots.quantilesHigher,
nearest: boxplots.quantilesNearest,
midpoint: boxplots.quantilesMidpoint,
hinges: quantilesHinges,
fivenum: quantilesFivenum,
7: quantilesType7,
quantiles: quantilesType7,
linear: quantilesLinear,
lower: quantilesLower,
higher: quantilesHigher,
nearest: quantilesNearest,
midpoint: quantilesMidpoint,
};
return lookup[q] || boxplots.quantilesType7;
return lookup[q] || quantilesType7;
}
function determineStatsOptions(options) {
const coef = options == null || typeof options.coef !== 'number' ? defaultStatsOptions.coef : options.coef;
const q = options == null || options.quantiles == null ? boxplots.quantilesType7 : options.quantiles;
const q = options == null || options.quantiles == null ? quantilesType7 : options.quantiles;
const quantiles = determineQuantiles(q);

@@ -112,3 +104,3 @@ return {

function boxplotStats(arr, options) {
const r = boxplots__default['default'](arr, determineStatsOptions(options));
const r = boxplots(arr, determineStatsOptions(options));
return {

@@ -132,2 +124,3 @@ items: Array.from(r.items),

const items = arr.filter((v) => typeof v === 'number' && !Number.isNaN(v)).sort((a, b) => a - b);
const mean = items.reduce((acc, v) => acc + v, 0) / items.length;
const { quantiles } = determineStatsOptions(options);

@@ -152,2 +145,3 @@ const stats = quantiles(items);

items,
mean,
max,

@@ -197,3 +191,3 @@ coords,

const baseDefaults = {
const baseDefaults$1 = {
borderWidth: 1,

@@ -206,2 +200,5 @@ outlierStyle: 'circle',

itemBorderWidth: 0,
meanStyle: 'circle',
meanRadius: 3,
meanBorderWidth: 1,
hitPadding: 2,

@@ -215,5 +212,7 @@ outlierHitRadius: 4,

itemBorderColor: 'borderColor',
meanBackgroundColor: 'backgroundColor',
meanBorderColor: 'borderColor',
};
const baseOptionKeys = (() => Object.keys(baseDefaults).concat(Object.keys(baseRoutes)))();
class StatsBase extends chart_js.Element {
const baseOptionKeys = (() => Object.keys(baseDefaults$1).concat(Object.keys(baseRoutes)))();
class StatsBase$1 extends Element {
isVertical() {

@@ -224,3 +223,3 @@ return this.getProps(['height']).height == null;

const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'items', 'width', 'height']);
const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers']);
const options = this.options;

@@ -240,5 +239,8 @@ if (options.itemRadius <= 0 || !props.items || props.items.length <= 0) {

};
const outliers = new Set(props.outliers || []);
if (vert) {
props.items.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
if (!outliers.has(v)) {
drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
}
});

@@ -248,3 +250,5 @@ }

props.items.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
if (!outliers.has(v)) {
drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
}
});

@@ -272,3 +276,3 @@ }

props.outliers.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, props.x, v);
drawPoint(ctx, pointOptions, props.x, v);
});

@@ -278,3 +282,3 @@ }

props.outliers.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, v, props.y);
drawPoint(ctx, pointOptions, v, props.y);
});

@@ -284,2 +288,26 @@ }

}
_drawMeanDot(ctx) {
const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'mean']);
const options = this.options;
if (options.meanRadius <= 0 || props.mean == null || Number.isNaN(props.mean)) {
return;
}
ctx.save();
ctx.fillStyle = options.meanBackgroundColor;
ctx.strokeStyle = options.meanBorderColor;
ctx.lineWidth = options.meanBorderWidth;
const pointOptions = {
pointStyle: options.meanStyle,
radius: options.meanRadius,
borderWidth: options.meanBorderWidth,
};
if (vert) {
drawPoint(ctx, pointOptions, props.x, props.mean);
}
else {
drawPoint(ctx, pointOptions, props.mean, props.y);
}
ctx.restore();
}
_getBounds(_useFinalPosition) {

@@ -379,3 +407,3 @@ return {

const boxOptionsKeys = baseOptionKeys.concat(['medianColor', 'lowerBackgroundColor']);
class BoxAndWiskers extends StatsBase {
class BoxAndWiskers extends StatsBase$1 {
draw(ctx) {

@@ -388,2 +416,3 @@ ctx.save();

this._drawOutliers(ctx);
this._drawMeanDot(ctx);
ctx.restore();

@@ -535,9 +564,9 @@ this._drawItems(ctx);

BoxAndWiskers.id = 'boxandwhiskers';
BoxAndWiskers.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults, {
BoxAndWiskers.defaults = Object.assign({}, BarElement.defaults, baseDefaults$1, {
medianColor: 'transparent',
lowerBackgroundColor: 'transparent',
});
BoxAndWiskers.defaultRoutes = Object.assign({}, chart_js.BarElement.defaultRoutes, baseRoutes);
BoxAndWiskers.defaultRoutes = Object.assign({}, BarElement.defaultRoutes, baseRoutes);
class Violin extends StatsBase {
class Violin extends StatsBase$1 {
draw(ctx) {

@@ -549,3 +578,3 @@ ctx.save();

const props = this.getProps(['x', 'y', 'width', 'height', 'min', 'max', 'coords', 'maxEstimate']);
helpers.drawPoint(ctx, {
drawPoint(ctx, {
pointStyle: 'rectRot',

@@ -559,2 +588,3 @@ radius: 5,

this._drawOutliers(ctx);
this._drawMeanDot(ctx);
ctx.restore();

@@ -621,4 +651,4 @@ this._drawItems(ctx);

Violin.id = 'violin';
Violin.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults);
Violin.defaultRoutes = Object.assign({}, chart_js.BarElement.defaultRoutes, baseRoutes);
Violin.defaults = Object.assign({}, BarElement.defaults, baseDefaults$1);
Violin.defaultRoutes = Object.assign({}, BarElement.defaultRoutes, baseRoutes);

@@ -688,20 +718,22 @@ const interpolators = {

outlierPositioner.register = () => {
chart_js.Tooltip.positioners.averageInstance = outlierPositioner;
Tooltip.positioners.averageInstance = outlierPositioner;
return outlierPositioner;
};
function baseDefaults$1(keys) {
function baseDefaults(keys) {
const colorKeys = ['borderColor', 'backgroundColor'].concat(keys.filter((c) => c.endsWith('Color')));
return {
datasets: Object.assign({
animation: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
show: {
return Object.assign({
animations: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
},
transitions: {
show: {
animations: {
colors: {

@@ -713,3 +745,5 @@ type: 'color',

},
hide: {
},
hide: {
animations: {
colors: {

@@ -722,9 +756,15 @@ type: 'color',

},
minStats: 'min',
maxStats: 'max',
}, defaultStatsOptions),
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
},
minStats: 'min',
maxStats: 'max',
}, defaultStatsOptions);
}
function defaultOverrides() {
return {
plugins: {
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
},
},

@@ -734,6 +774,19 @@ },

}
class StatsBase$1 extends chart_js.BarController {
class StatsBase extends BarController {
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'mean']) {
const v = source[key];
if (typeof v === 'number') {
target[key] = mapper(v);
}
}
for (const key of ['outliers', 'items']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
}
getMinMax(scale, canStack) {
const bak = scale.axis;
const config = this._config;
const config = this.options;
scale.axis = config.minStats;

@@ -755,3 +808,3 @@ const min = super.getMinMax(scale, canStack).min;

parsed[iScale.axis] = iScale.parse(labels[index], index);
const stats = this._parseStats(data == null ? null : data[index], this._config);
const stats = this._parseStats(data == null ? null : data[index], this.options);
if (stats) {

@@ -782,3 +835,3 @@ Object.assign(parsed, stats);

};
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v), 'string');
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v));
const s = this._toStringStats(r.value);

@@ -793,2 +846,6 @@ r.value.toString = function () {

}
_toStringStats(b) {
const f = (v) => (v == null ? 'NaN' : v.toLocaleString());
return `(min: ${f(b.min)}, 25% quantile: ${f(b.q1)}, median: ${f(b.median)}, mean: ${f(b.mean)}, 75% quantile: ${f(b.q3)}, max: ${f(b.max)})`;
}
updateElement(rectangle, index, properties, mode) {

@@ -801,3 +858,3 @@ const reset = mode === 'reset';

properties._index = index;
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)), mode);
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)));
super.updateElement(rectangle, index, properties, mode);

@@ -808,14 +865,14 @@ }

function patchController(type, config, controller, elements = [], scales = []) {
chart_js.registry.addControllers(controller);
registry.addControllers(controller);
if (Array.isArray(elements)) {
chart_js.registry.addElements(...elements);
registry.addElements(...elements);
}
else {
chart_js.registry.addElements(elements);
registry.addElements(elements);
}
if (Array.isArray(scales)) {
chart_js.registry.addScales(...scales);
registry.addScales(...scales);
}
else {
chart_js.registry.addScales(scales);
registry.addScales(scales);
}

@@ -827,40 +884,31 @@ const c = config;

class BoxPlotController extends StatsBase$1 {
class BoxPlotController extends StatsBase {
_parseStats(value, config) {
return asBoxPlotStats(value, config);
}
_toStringStats(b) {
return `(min: ${b.min}, 25% quantile: ${b.q1}, median: ${b.median}, 75% quantile: ${b.q3}, max: ${b.max})`;
}
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'whiskerMin', 'whiskerMax', 'mean']) {
super._transformStats(target, source, mapper);
for (const key of ['whiskerMin', 'whiskerMax']) {
target[key] = mapper(source[key]);
}
for (const key of ['outliers', 'items']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
}
}
BoxPlotController.id = 'boxplot';
BoxPlotController.defaults = helpers.merge({}, [
chart_js.BarController.defaults,
baseDefaults$1(boxOptionsKeys),
BoxPlotController.defaults = merge({}, [
BarController.defaults,
baseDefaults(boxOptionsKeys),
{
datasets: {
animation: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.datasets.animation.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'], boxOptionsKeys.filter((c) => !c.endsWith('Color'))),
},
animations: {
numbers: {
type: 'number',
properties: BarController.defaults.animations.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'], boxOptionsKeys.filter((c) => !c.endsWith('Color'))),
},
},
dataElementType: BoxAndWiskers.id,
dataElementOptions: chart_js.BarController.defaults.dataElementOptions.concat(boxOptionsKeys),
},
]);
class BoxPlotChart extends chart_js.Chart {
BoxPlotController.overrides = merge({}, [BarController.overrides, defaultOverrides()]);
class BoxPlotChart extends Chart {
constructor(item, config) {
super(item, patchController('boxplot', config, BoxPlotController, BoxAndWiskers, [chart_js.LinearScale, chart_js.CategoryScale]));
super(item, patchController('boxplot', config, BoxPlotController, BoxAndWiskers, [LinearScale, CategoryScale]));
}

@@ -870,21 +918,11 @@ }

class ViolinController extends StatsBase$1 {
class ViolinController extends StatsBase {
_parseStats(value, config) {
return asViolinStats(value, config);
}
_toStringStats(v) {
return `(min: ${v.min}, 25% quantile: ${v.q1}, median: ${v.median}, 75% quantile: ${v.q3}, max: ${v.max})`;
}
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1']) {
target[key] = mapper(source[key]);
}
super._transformStats(target, source, mapper);
target.maxEstimate = source.maxEstimate;
for (const key of ['items', 'outliers']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
if (Array.isArray(source.coords)) {
target.coords = source.coords.map((coord) => Object.assign({}, coord, { v: mapper(coord.v) }));
target.coords = source.coords.map((c) => Object.assign({}, c, { v: mapper(c.v) }));
}

@@ -894,26 +932,24 @@ }

ViolinController.id = 'violin';
ViolinController.defaults = helpers.merge({}, [
chart_js.BarController.defaults,
baseDefaults$1(baseOptionKeys),
ViolinController.defaults = merge({}, [
BarController.defaults,
baseDefaults(baseOptionKeys),
{
datasets: {
points: 100,
animation: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.datasets.animation.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'], baseOptionKeys.filter((c) => !c.endsWith('Color'))),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
points: 100,
animations: {
numbers: {
type: 'number',
properties: BarController.defaults.animations.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'], baseOptionKeys.filter((c) => !c.endsWith('Color'))),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
},
dataElementType: Violin.id,
dataElementOptions: chart_js.BarController.defaults.dataElementOptions.concat(baseOptionKeys),
},
]);
class ViolinChart extends chart_js.Chart {
ViolinController.overrides = merge({}, [BarController.overrides, defaultOverrides()]);
class ViolinChart extends Chart {
constructor(item, config) {
super(item, patchController('violin', config, ViolinController, Violin, [chart_js.LinearScale, chart_js.CategoryScale]));
super(item, patchController('violin', config, ViolinController, Violin, [LinearScale, CategoryScale]));
}

@@ -923,9 +959,3 @@ }

exports.BoxAndWiskers = BoxAndWiskers;
exports.BoxPlotChart = BoxPlotChart;
exports.BoxPlotController = BoxPlotController;
exports.StatsBase = StatsBase;
exports.Violin = Violin;
exports.ViolinChart = ViolinChart;
exports.ViolinController = ViolinController;
export { BoxAndWiskers, BoxPlotChart, BoxPlotController, StatsBase$1 as StatsBase, Violin, ViolinChart, ViolinController };
//# sourceMappingURL=index.js.map

@@ -323,2 +323,3 @@ /**

const items = arr.filter((v) => typeof v === 'number' && !Number.isNaN(v)).sort((a, b) => a - b);
const mean = items.reduce((acc, v) => acc + v, 0) / items.length;
const { quantiles } = determineStatsOptions(options);

@@ -343,2 +344,3 @@ const stats = quantiles(items);

items,
mean,
max,

@@ -455,16 +457,18 @@ coords,

function baseDefaults(keys) {
function baseDefaults$1(keys) {
const colorKeys = ['borderColor', 'backgroundColor'].concat(keys.filter((c) => c.endsWith('Color')));
return {
datasets: Object.assign({
animation: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
show: {
return Object.assign({
animations: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
},
transitions: {
show: {
animations: {
colors: {

@@ -476,3 +480,5 @@ type: 'color',

},
hide: {
},
hide: {
animations: {
colors: {

@@ -485,9 +491,15 @@ type: 'color',

},
minStats: 'min',
maxStats: 'max',
}, defaultStatsOptions),
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
},
minStats: 'min',
maxStats: 'max',
}, defaultStatsOptions);
}
function defaultOverrides() {
return {
plugins: {
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
},
},

@@ -497,6 +509,19 @@ },

}
class StatsBase extends chart_js.BarController {
class StatsBase$1 extends chart_js.BarController {
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'mean']) {
const v = source[key];
if (typeof v === 'number') {
target[key] = mapper(v);
}
}
for (const key of ['outliers', 'items']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
}
getMinMax(scale, canStack) {
const bak = scale.axis;
const config = this._config;
const config = this.options;
scale.axis = config.minStats;

@@ -518,3 +543,3 @@ const min = super.getMinMax(scale, canStack).min;

parsed[iScale.axis] = iScale.parse(labels[index], index);
const stats = this._parseStats(data == null ? null : data[index], this._config);
const stats = this._parseStats(data == null ? null : data[index], this.options);
if (stats) {

@@ -545,3 +570,3 @@ Object.assign(parsed, stats);

};
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v), 'string');
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v));
const s = this._toStringStats(r.value);

@@ -556,2 +581,6 @@ r.value.toString = function () {

}
_toStringStats(b) {
const f = (v) => (v == null ? 'NaN' : v.toLocaleString());
return `(min: ${f(b.min)}, 25% quantile: ${f(b.q1)}, median: ${f(b.median)}, mean: ${f(b.mean)}, 75% quantile: ${f(b.q3)}, max: ${f(b.max)})`;
}
updateElement(rectangle, index, properties, mode) {

@@ -564,3 +593,3 @@ const reset = mode === 'reset';

properties._index = index;
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)), mode);
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)));
super.updateElement(rectangle, index, properties, mode);

@@ -570,3 +599,3 @@ }

const baseDefaults$1 = {
const baseDefaults = {
borderWidth: 1,

@@ -579,2 +608,5 @@ outlierStyle: 'circle',

itemBorderWidth: 0,
meanStyle: 'circle',
meanRadius: 3,
meanBorderWidth: 1,
hitPadding: 2,

@@ -588,5 +620,7 @@ outlierHitRadius: 4,

itemBorderColor: 'borderColor',
meanBackgroundColor: 'backgroundColor',
meanBorderColor: 'borderColor',
};
const baseOptionKeys = (() => Object.keys(baseDefaults$1).concat(Object.keys(baseRoutes)))();
class StatsBase$1 extends chart_js.Element {
const baseOptionKeys = (() => Object.keys(baseDefaults).concat(Object.keys(baseRoutes)))();
class StatsBase extends chart_js.Element {
isVertical() {

@@ -597,3 +631,3 @@ return this.getProps(['height']).height == null;

const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'items', 'width', 'height']);
const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers']);
const options = this.options;

@@ -613,5 +647,8 @@ if (options.itemRadius <= 0 || !props.items || props.items.length <= 0) {

};
const outliers = new Set(props.outliers || []);
if (vert) {
props.items.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
if (!outliers.has(v)) {
helpers.drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
}
});

@@ -621,3 +658,5 @@ }

props.items.forEach((v) => {
helpers.drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
if (!outliers.has(v)) {
helpers.drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
}
});

@@ -655,2 +694,26 @@ }

}
_drawMeanDot(ctx) {
const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'mean']);
const options = this.options;
if (options.meanRadius <= 0 || props.mean == null || Number.isNaN(props.mean)) {
return;
}
ctx.save();
ctx.fillStyle = options.meanBackgroundColor;
ctx.strokeStyle = options.meanBorderColor;
ctx.lineWidth = options.meanBorderWidth;
const pointOptions = {
pointStyle: options.meanStyle,
radius: options.meanRadius,
borderWidth: options.meanBorderWidth,
};
if (vert) {
helpers.drawPoint(ctx, pointOptions, props.x, props.mean);
}
else {
helpers.drawPoint(ctx, pointOptions, props.mean, props.y);
}
ctx.restore();
}
_getBounds(_useFinalPosition) {

@@ -750,3 +813,3 @@ return {

const boxOptionsKeys = baseOptionKeys.concat(['medianColor', 'lowerBackgroundColor']);
class BoxAndWiskers extends StatsBase$1 {
class BoxAndWiskers extends StatsBase {
draw(ctx) {

@@ -759,2 +822,3 @@ ctx.save();

this._drawOutliers(ctx);
this._drawMeanDot(ctx);
ctx.restore();

@@ -906,3 +970,3 @@ this._drawItems(ctx);

BoxAndWiskers.id = 'boxandwhiskers';
BoxAndWiskers.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults$1, {
BoxAndWiskers.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults, {
medianColor: 'transparent',

@@ -913,3 +977,3 @@ lowerBackgroundColor: 'transparent',

class Violin extends StatsBase$1 {
class Violin extends StatsBase {
draw(ctx) {

@@ -930,2 +994,3 @@ ctx.save();

this._drawOutliers(ctx);
this._drawMeanDot(ctx);
ctx.restore();

@@ -992,3 +1057,3 @@ this._drawItems(ctx);

Violin.id = 'violin';
Violin.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults$1);
Violin.defaults = Object.assign({}, chart_js.BarElement.defaults, baseDefaults);
Violin.defaultRoutes = Object.assign({}, chart_js.BarElement.defaultRoutes, baseRoutes);

@@ -1015,18 +1080,11 @@

class BoxPlotController extends StatsBase {
class BoxPlotController extends StatsBase$1 {
_parseStats(value, config) {
return asBoxPlotStats(value, config);
}
_toStringStats(b) {
return `(min: ${b.min}, 25% quantile: ${b.q1}, median: ${b.median}, 75% quantile: ${b.q3}, max: ${b.max})`;
}
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'whiskerMin', 'whiskerMax', 'mean']) {
super._transformStats(target, source, mapper);
for (const key of ['whiskerMin', 'whiskerMax']) {
target[key] = mapper(source[key]);
}
for (const key of ['outliers', 'items']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
}

@@ -1037,16 +1095,14 @@ }

chart_js.BarController.defaults,
baseDefaults(boxOptionsKeys),
baseDefaults$1(boxOptionsKeys),
{
datasets: {
animation: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.datasets.animation.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'], boxOptionsKeys.filter((c) => !c.endsWith('Color'))),
},
animations: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.animations.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'], boxOptionsKeys.filter((c) => !c.endsWith('Color'))),
},
},
dataElementType: BoxAndWiskers.id,
dataElementOptions: chart_js.BarController.defaults.dataElementOptions.concat(boxOptionsKeys),
},
]);
BoxPlotController.overrides = helpers.merge({}, [chart_js.BarController.overrides, defaultOverrides()]);
class BoxPlotChart extends chart_js.Chart {

@@ -1059,21 +1115,11 @@ constructor(item, config) {

class ViolinController extends StatsBase {
class ViolinController extends StatsBase$1 {
_parseStats(value, config) {
return asViolinStats(value, config);
}
_toStringStats(v) {
return `(min: ${v.min}, 25% quantile: ${v.q1}, median: ${v.median}, 75% quantile: ${v.q3}, max: ${v.max})`;
}
_transformStats(target, source, mapper) {
for (const key of ['min', 'max', 'median', 'q3', 'q1']) {
target[key] = mapper(source[key]);
}
super._transformStats(target, source, mapper);
target.maxEstimate = source.maxEstimate;
for (const key of ['items', 'outliers']) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
if (Array.isArray(source.coords)) {
target.coords = source.coords.map((coord) => Object.assign({}, coord, { v: mapper(coord.v) }));
target.coords = source.coords.map((c) => Object.assign({}, c, { v: mapper(c.v) }));
}

@@ -1085,21 +1131,19 @@ }

chart_js.BarController.defaults,
baseDefaults(baseOptionKeys),
baseDefaults$1(baseOptionKeys),
{
datasets: {
points: 100,
animation: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.datasets.animation.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'], baseOptionKeys.filter((c) => !c.endsWith('Color'))),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
points: 100,
animations: {
numbers: {
type: 'number',
properties: chart_js.BarController.defaults.animations.numbers.properties.concat(['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'], baseOptionKeys.filter((c) => !c.endsWith('Color'))),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
},
dataElementType: Violin.id,
dataElementOptions: chart_js.BarController.defaults.dataElementOptions.concat(baseOptionKeys),
},
]);
ViolinController.overrides = helpers.merge({}, [chart_js.BarController.overrides, defaultOverrides()]);
class ViolinChart extends chart_js.Chart {

@@ -1118,3 +1162,3 @@ constructor(item, config) {

exports.BoxPlotController = BoxPlotController;
exports.StatsBase = StatsBase$1;
exports.StatsBase = StatsBase;
exports.Violin = Violin;

@@ -1121,0 +1165,0 @@ exports.ViolinChart = ViolinChart;

@@ -1,2 +0,2 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ChartBoxPlot={},t.Chart,t.Chart.helpers)}(this,(function(t,e,r){"use strict";function i(t,e,r){const i=e-1,o=e=>{const o=e*i,n=Math.floor(o),s=o-n,a=t[n];return 0===s?a:r(a,t[Math.min(n+1,i)],s)};return{q1:o(.25),median:o(.5),q3:o(.75)}}function o(t,e=t.length){return i(t,e,(t,e,r)=>t+r*(e-t))}function n(t,e=t.length){return i(t,e,(t,e,r)=>t+(e-t)*r)}function s(t,e=t.length){return i(t,e,t=>t)}function a(t,e=t.length){return i(t,e,(t,e)=>e)}function l(t,e=t.length){return i(t,e,(t,e,r)=>r<.5?t:e)}function u(t,e=t.length){return i(t,e,(t,e)=>.5*(t+e))}function h(t,e=t.length){const r=e,i=Math.floor((r+3)/2)/2,o=e=>.5*(t[Math.floor(e)-1]+t[Math.ceil(e)-1]);return{q1:o(i),median:o((r+1)/2),q3:o(r+1-i)}}function d(t,e=t.length){return h(t,e)}function m(t,e={}){const{quantiles:r,validAndSorted:i,coef:n,whiskersMode:s,eps:a}=Object.assign({coef:1.5,eps:.01,quantiles:o,validAndSorted:!1,whiskersMode:"nearest"},e),{missing:l,s:u,min:h,max:d,sum:m}=i?function(t){if(0===t.length)return{sum:Number.NaN,min:Number.NaN,max:Number.NaN,missing:0,s:[]};const e=t[0],r=t[t.length-1],i=(t,e)=>t+e;return{sum:(t instanceof Float32Array||Float64Array,t.reduce(i,0)),min:e,max:r,missing:0,s:t}}(t):function(t){let e=Number.POSITIVE_INFINITY,r=Number.NEGATIVE_INFINITY,i=0,o=0;const n=t.length,s=t instanceof Float64Array?new Float64Array(n):new Float32Array(n);for(let a=0;a<n;++a){const n=t[a];null==n||Number.isNaN(n)||(s[o]=n,o++,n<e&&(e=n),n>r&&(r=n),i+=n)}const a=n-o;if(0===o)return{sum:i,min:e,max:r,missing:a,s:[]};const l=o===n?s:s.subarray(0,o);return l.sort((t,e)=>t===e?0:t<e?-1:1),{sum:i,min:l[0],max:l[l.length-1],missing:a,s:l}}(t),c={min:Number.NaN,max:Number.NaN,mean:Number.NaN,missing:l,iqr:Number.NaN,count:t.length,whiskerHigh:Number.NaN,whiskerLow:Number.NaN,outlier:[],median:Number.NaN,q1:Number.NaN,q3:Number.NaN,items:[]},f=(t,e)=>Math.abs(t-e)<a,g=t.length-l;if(0===g)return c;const{median:p,q1:x,q3:y}=r(u,g),b=y-x,q="number"==typeof n&&n>0;let w=q?Math.max(h,x-n*b):h,k=q?Math.min(d,y+n*b):d;const M=[];for(let t=0;t<g;++t){const e=u[t];if(e>=w||f(e,w)){"nearest"===s&&(w=e);break}0!==M.length&&f(M[M.length-1],e)||M.push(e)}const N=[];for(let t=g-1;t>=0;--t){const e=u[t];if(e<=k||f(e,k)){"nearest"===s&&(k=e);break}0!==N.length&&f(N[N.length-1],e)||0!==M.length&&f(M[M.length-1],e)||N.push(e)}return{min:h,max:d,count:t.length,missing:l,mean:m/g,whiskerHigh:k,whiskerLow:w,outlier:M.concat(N.reverse()),median:p,q1:x,q3:y,iqr:b,items:u}}function c(t){const e=t.length;if(e<1)return Number.NaN;if(1===e)return 0;const r=function(t){return 0===t.length?Number.NaN:t.reduce((t,e,r)=>t+(e-t)/(r+1),0)}(t);return t.reduce((t,e)=>t+(e-r)*(e-r),0)/(t.length-1)}function f(t,e,r){const i=function(t,e){const r=e(t),i=(r.q3-r.q1)/1.34;return 1.06*Math.min(Math.sqrt(c(t)),i)*Math.pow(t.length,-.2)}(e,r);return t.map(t=>{const r=e.reduce((e,r)=>{return e+(o=(t-r)/i,1/Math.sqrt(2*Math.PI)*Math.exp(-.5*o*o));var o},0);return{v:t,estimate:r/i/e.length}})}const g={coef:1.5,quantiles:7};function p(t){return{coef:null==t||"number"!=typeof t.coef?g.coef:t.coef,quantiles:function(t){return"function"==typeof t?t:{hinges:d,fivenum:h,7:o,quantiles:o,linear:n,lower:s,higher:a,nearest:l,midpoint:u}[t]||o}(null==t||null==t.quantiles?o:t.quantiles)}}function x(t,e){if(t){if("number"==typeof t.median&&"number"==typeof t.q1&&"number"==typeof t.q3){if(void 0===t.whiskerMin){const{coef:r}=p(e),{whiskerMin:i,whiskerMax:o}=function(t,e,r=1.5){const i=t.q3-t.q1,o="number"==typeof r&&r>0;let n=o?Math.max(t.min,t.q1-r*i):t.min,s=o?Math.min(t.max,t.q3+r*i):t.max;if(Array.isArray(e)){for(let t=0;t<e.length;t++){const r=e[t];if(r>=n){n=r;break}}for(let t=e.length-1;t>=0;t--){const r=e[t];if(r<=s){s=r;break}}}return{whiskerMin:n,whiskerMax:s}}(t,Array.isArray(t.items)?t.items.slice().sort((t,e)=>t-e):null,r);t.whiskerMin=i,t.whiskerMax=o}return t}if(Array.isArray(t))return function(t,e){const r=m(t,p(e));return{items:Array.from(r.items),outliers:r.outlier,whiskerMax:r.whiskerHigh,whiskerMin:r.whiskerLow,max:r.max,median:r.median,mean:r.mean,min:r.min,q1:r.q1,q3:r.q3}}(t,e)}}function y(t,e){if(t){if("number"==typeof t.median&&Array.isArray(t.coords))return t;if(Array.isArray(t))return function(t,e){if(0===t.length)return;const r=t.filter(t=>"number"==typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),{quantiles:i}=p(e),o=i(r),n=r[0],s=r[r.length-1],a=[],l=(s-n)/e.points;for(let t=n;t<=s&&l>0;t+=l)a.push(t);a[a.length-1]!==s&&a.push(s);const u=f(r,a,i),h=u.reduce((t,e)=>Math.max(t,e.estimate),Number.NEGATIVE_INFINITY);return{...o,min:n,items:r,max:s,coords:u,outliers:[],maxEstimate:h}}(t,e)}}const b={number:(t,e,r)=>t===e||null==t?e:null==e?t:t+(e-t)*r};function q(t,e,r){return"number"==typeof t&&"number"==typeof e?b.number(t,e,r):Array.isArray(t)&&Array.isArray(e)?e.map((e,i)=>b.number(t[i],e,r)):e}function w(t){const e=t.formattedValue,r=this;e&&null!=r._tooltipOutlier&&t.datasetIndex===r._tooltipOutlier.datasetIndex&&(e.hoveredOutlierIndex=r._tooltipOutlier.index)}function k(t,e){if(!t.length)return!1;let r=0,i=0,o=0;for(let n=0;n<t.length;++n){const s=t[n].element;if(s&&s.hasValue()){const t=s.tooltipPosition(e,this);r+=t.x,i+=t.y,++o}}return{x:r/o,y:i/o}}function M(t){const e=["borderColor","backgroundColor"].concat(t.filter(t=>t.endsWith("Color")));return{datasets:Object.assign({animation:{numberArray:{fn:q,properties:["outliers","items"]},colors:{type:"color",properties:e},show:{colors:{type:"color",properties:e,from:"transparent"}},hide:{colors:{type:"color",properties:e,to:"transparent"}}},minStats:"min",maxStats:"max"},g),tooltips:{position:k.register().id,callbacks:{beforeLabel:w}}}}k.id="averageInstance",k.register=()=>(e.Tooltip.positioners.averageInstance=k,k);class N extends e.BarController{getMinMax(t,e){const r=t.axis,i=this._config;t.axis=i.minStats;const o=super.getMinMax(t,e).min;t.axis=i.maxStats;const n=super.getMinMax(t,e).max;return t.axis=r,{min:o,max:n}}parsePrimitiveData(t,e,r,i){const o=t.vScale,n=t.iScale,s=n.getLabels(),a=[];for(let t=0;t<i;t++){const i=t+r,l={};l[n.axis]=n.parse(s[i],i);const u=this._parseStats(null==e?null:e[i],this._config);u&&(Object.assign(l,u),l[o.axis]=u.median),a.push(l)}return a}parseArrayData(t,e,r,i){return this.parsePrimitiveData(t,e,r,i)}parseObjectData(t,e,r,i){return this.parsePrimitiveData(t,e,r,i)}getLabelAndValue(t){const e=super.getLabelAndValue(t),r=this._cachedMeta.vScale,i=this.getParsed(t);if(!r||!i||"NaN"===e.value)return e;e.value={raw:i,hoveredOutlierIndex:-1},this._transformStats(e.value,i,t=>r.getLabelForValue(t),"string");const o=this._toStringStats(e.value);return e.value.toString=function(){return this.hoveredOutlierIndex>=0?`(outlier: ${this.outliers[this.hoveredOutlierIndex]})`:o},e}updateElement(t,e,r,i){const o="reset"===i,n=this._cachedMeta.vScale,s=this.getParsed(e),a=n.getBasePixel();r._datasetIndex=this.index,r._index=e,this._transformStats(r,s,t=>o?a:n.getPixelForValue(t,e),i),super.updateElement(t,e,r,i)}}const C={borderWidth:1,outlierStyle:"circle",outlierRadius:2,outlierBorderWidth:1,itemStyle:"circle",itemRadius:0,itemBorderWidth:0,hitPadding:2,outlierHitRadius:4},_={outlierBackgroundColor:"backgroundColor",outlierBorderColor:"borderColor",itemBackgroundColor:"backgroundColor",itemBorderColor:"borderColor"},v=Object.keys(C).concat(Object.keys(_));class P extends e.Element{isVertical(){return null==this.getProps(["height"]).height}_drawItems(t){const e=this.isVertical(),i=this.getProps(["x","y","items","width","height"]),o=this.options;if(o.itemRadius<=0||!i.items||i.items.length<=0)return;t.save(),t.strokeStyle=o.itemBorderColor,t.fillStyle=o.itemBackgroundColor,t.lineWidth=o.itemBorderWidth;const n=function(t=Date.now()){let e=t;return()=>(e=(9301*e+49297)%233280,e/233280)}(1e3*this._datasetIndex+this._index),s={pointStyle:o.itemStyle,radius:o.itemRadius,borderWidth:o.itemBorderWidth};e?i.items.forEach(e=>{r.drawPoint(t,s,i.x-i.width/2+n()*i.width,e)}):i.items.forEach(e=>{r.drawPoint(t,s,e,i.y-i.height/2+n()*i.height)}),t.restore()}_drawOutliers(t){const e=this.isVertical(),i=this.getProps(["x","y","outliers"]),o=this.options;if(o.outlierRadius<=0||!i.outliers||0===i.outliers.length)return;t.save(),t.fillStyle=o.outlierBackgroundColor,t.strokeStyle=o.outlierBorderColor,t.lineWidth=o.outlierBorderWidth;const n={pointStyle:o.outlierStyle,radius:o.outlierRadius,borderWidth:o.outlierBorderWidth};e?i.outliers.forEach(e=>{r.drawPoint(t,n,i.x,e)}):i.outliers.forEach(e=>{r.drawPoint(t,n,e,i.y)}),t.restore()}_getBounds(t){return{left:0,top:0,right:0,bottom:0}}_getHitBounds(t){const e=this.options.hitPadding,r=this._getBounds(t);return{left:r.left-e,top:r.top-e,right:r.right+e,bottom:r.bottom+e}}inRange(t,e,r){return(!Number.isNaN(this.x)||!Number.isNaN(this.y))&&(this._boxInRange(t,e,r)||this._outlierIndexInRange(t,e,r)>=0)}inXRange(t,e){const r=this._getHitBounds(e);return t>=r.left&&t<=r.right}inYRange(t,e){const r=this._getHitBounds(e);return t>=r.top&&t<=r.bottom}_outlierIndexInRange(t,e,r){const i=this.getProps(["x","y"],r),o=this.options.outlierHitRadius,n=this._getOutliers(r),s=this.isVertical();if(s&&Math.abs(t-i.x)>o||!s&&Math.abs(e-i.y)>o)return-1;const a=s?e:t;for(let t=0;t<n.length;t++)if(Math.abs(n[t]-a)<=o)return t;return-1}_boxInRange(t,e,r){const i=this._getHitBounds(r);return t>=i.left&&t<=i.right&&e>=i.top&&e<=i.bottom}getCenterPoint(t){const e=this.getProps(["x","y"],t);return{x:e.x,y:e.y}}_getOutliers(t){return this.getProps(["outliers"],t).outliers||[]}tooltipPosition(t,e){if(!t||"boolean"==typeof t)return this.getCenterPoint();e&&delete e._tooltipOutlier;const r=this.getProps(["x","y"]),i=this._outlierIndexInRange(t.x,t.y);return i<0||!e?this.getCenterPoint():(e._tooltipOutlier={index:i,datasetIndex:this._datasetIndex},this.isVertical()?{x:r.x,y:this._getOutliers()[i]}:{x:this._getOutliers()[i],y:r.y})}}const B=v.concat(["medianColor","lowerBackgroundColor"]);class S extends P{draw(t){t.save(),t.fillStyle=this.options.backgroundColor,t.strokeStyle=this.options.borderColor,t.lineWidth=this.options.borderWidth,this._drawBoxPlot(t),this._drawOutliers(t),t.restore(),this._drawItems(t)}_drawBoxPlot(t){this.isVertical()?this._drawBoxPlotVertical(t):this._drawBoxPlotHorizontal(t)}_drawBoxPlotVertical(t){const e=this.options,r=this.getProps(["x","width","q1","q3","median","whiskerMin","whiskerMax"]),i=r.x,o=r.width,n=i-o/2;r.q3>r.q1?t.fillRect(n,r.q1,o,r.q3-r.q1):t.fillRect(n,r.q3,o,r.q1-r.q3),t.save(),e.medianColor&&"transparent"!==e.medianColor&&"#0000"!==e.medianColor&&(t.strokeStyle=e.medianColor),t.beginPath(),t.moveTo(n,r.median),t.lineTo(n+o,r.median),t.closePath(),t.stroke(),t.restore(),t.save(),e.lowerBackgroundColor&&"transparent"!==e.lowerBackgroundColor&&"#0000"!==e.lowerBackgroundColor&&(t.fillStyle=e.lowerBackgroundColor,r.q3>r.q1?t.fillRect(n,r.median,o,r.q3-r.median):t.fillRect(n,r.median,o,r.q1-r.median)),t.restore(),r.q3>r.q1?t.strokeRect(n,r.q1,o,r.q3-r.q1):t.strokeRect(n,r.q3,o,r.q1-r.q3),t.beginPath(),t.moveTo(n,r.whiskerMin),t.lineTo(n+o,r.whiskerMin),t.moveTo(i,r.whiskerMin),t.lineTo(i,r.q1),t.moveTo(n,r.whiskerMax),t.lineTo(n+o,r.whiskerMax),t.moveTo(i,r.whiskerMax),t.lineTo(i,r.q3),t.closePath(),t.stroke()}_drawBoxPlotHorizontal(t){const e=this.options,r=this.getProps(["y","height","q1","q3","median","whiskerMin","whiskerMax"]),i=r.y,o=r.height,n=i-o/2;r.q3>r.q1?t.fillRect(r.q1,n,r.q3-r.q1,o):t.fillRect(r.q3,n,r.q1-r.q3,o),t.save(),e.medianColor&&"transparent"!==e.medianColor&&(t.strokeStyle=e.medianColor),t.beginPath(),t.moveTo(r.median,n),t.lineTo(r.median,n+o),t.closePath(),t.stroke(),t.restore(),t.save(),e.lowerBackgroundColor&&"transparent"!==e.lowerBackgroundColor&&(t.fillStyle=e.lowerBackgroundColor,r.q3>r.q1?t.fillRect(r.median,n,r.q3-r.median,o):t.fillRect(r.median,n,r.q1-r.median,o)),t.restore(),r.q3>r.q1?t.strokeRect(r.q1,n,r.q3-r.q1,o):t.strokeRect(r.q3,n,r.q1-r.q3,o),t.beginPath(),t.moveTo(r.whiskerMin,n),t.lineTo(r.whiskerMin,n+o),t.moveTo(r.whiskerMin,i),t.lineTo(r.q1,i),t.moveTo(r.whiskerMax,n),t.lineTo(r.whiskerMax,n+o),t.moveTo(r.whiskerMax,i),t.lineTo(r.q3,i),t.closePath(),t.stroke()}_getBounds(t){const e=this.isVertical();if(null==this.x)return{left:0,top:0,right:0,bottom:0};if(e){const{x:e,width:r,whiskerMax:i,whiskerMin:o}=this.getProps(["x","width","whiskerMin","whiskerMax"],t),n=e-r/2;return{left:n,top:i,right:n+r,bottom:o}}const{y:r,height:i,whiskerMax:o,whiskerMin:n}=this.getProps(["y","height","whiskerMin","whiskerMax"],t),s=r-i/2;return{left:n,top:s,right:o,bottom:s+i}}}S.id="boxandwhiskers",S.defaults=Object.assign({},e.BarElement.defaults,C,{medianColor:"transparent",lowerBackgroundColor:"transparent"}),S.defaultRoutes=Object.assign({},e.BarElement.defaultRoutes,_);class A extends P{draw(t){t.save(),t.fillStyle=this.options.backgroundColor,t.strokeStyle=this.options.borderColor,t.lineWidth=this.options.borderWidth;const e=this.getProps(["x","y","width","height","min","max","coords","maxEstimate"]);r.drawPoint(t,{pointStyle:"rectRot",radius:5,borderWidth:this.options.borderWidth},e.x,e.y),e.coords&&e.coords.length>0&&this._drawCoords(t,e),this._drawOutliers(t),t.restore(),this._drawItems(t)}_drawCoords(t,e){if(t.beginPath(),this.isVertical()){const r=e.x,i=e.width/2/e.maxEstimate;t.moveTo(r,e.min),e.coords.forEach(e=>{t.lineTo(r-e.estimate*i,e.v)}),t.lineTo(r,e.max),t.moveTo(r,e.min),e.coords.forEach(e=>{t.lineTo(r+e.estimate*i,e.v)}),t.lineTo(r,e.max)}else{const r=e.y,i=e.height/2/e.maxEstimate;t.moveTo(e.min,r),e.coords.forEach(e=>{t.lineTo(e.v,r-e.estimate*i)}),t.lineTo(e.max,r),t.moveTo(e.min,r),e.coords.forEach(e=>{t.lineTo(e.v,r+e.estimate*i)}),t.lineTo(e.max,r)}t.closePath(),t.stroke(),t.fill()}_getBounds(t){if(this.isVertical()){const{x:e,width:r,min:i,max:o}=this.getProps(["x","width","min","max"],t),n=e-r/2;return{left:n,top:o,right:n+r,bottom:i}}const{y:e,height:r,min:i,max:o}=this.getProps(["y","height","min","max"],t),n=e-r/2;return{left:i,top:n,right:o,bottom:n+r}}}function T(t,r,i,o=[],n=[]){e.registry.addControllers(i),Array.isArray(o)?e.registry.addElements(...o):e.registry.addElements(o),Array.isArray(n)?e.registry.addScales(...n):e.registry.addScales(n);const s=r;return s.type=t,s}A.id="violin",A.defaults=Object.assign({},e.BarElement.defaults,C),A.defaultRoutes=Object.assign({},e.BarElement.defaultRoutes,_);class I extends N{_parseStats(t,e){return x(t,e)}_toStringStats(t){return`(min: ${t.min}, 25% quantile: ${t.q1}, median: ${t.median}, 75% quantile: ${t.q3}, max: ${t.max})`}_transformStats(t,e,r){for(const i of["min","max","median","q3","q1","whiskerMin","whiskerMax","mean"])t[i]=r(e[i]);for(const i of["outliers","items"])Array.isArray(e[i])&&(t[i]=e[i].map(r))}}I.id="boxplot",I.defaults=r.merge({},[e.BarController.defaults,M(B),{datasets:{animation:{numbers:{type:"number",properties:e.BarController.defaults.datasets.animation.numbers.properties.concat(["q1","q3","min","max","median","whiskerMin","whiskerMax","mean"],B.filter(t=>!t.endsWith("Color")))}}},dataElementType:S.id,dataElementOptions:e.BarController.defaults.dataElementOptions.concat(B)}]);class E extends e.Chart{constructor(t,r){super(t,T("boxplot",r,I,S,[e.LinearScale,e.CategoryScale]))}}E.id=I.id;class O extends N{_parseStats(t,e){return y(t,e)}_toStringStats(t){return`(min: ${t.min}, 25% quantile: ${t.q1}, median: ${t.median}, 75% quantile: ${t.q3}, max: ${t.max})`}_transformStats(t,e,r){for(const i of["min","max","median","q3","q1"])t[i]=r(e[i]);t.maxEstimate=e.maxEstimate;for(const i of["items","outliers"])Array.isArray(e[i])&&(t[i]=e[i].map(r));Array.isArray(e.coords)&&(t.coords=e.coords.map(t=>Object.assign({},t,{v:r(t.v)})))}}O.id="violin",O.defaults=r.merge({},[e.BarController.defaults,M(v),{datasets:{points:100,animation:{numbers:{type:"number",properties:e.BarController.defaults.datasets.animation.numbers.properties.concat(["q1","q3","min","max","median","maxEstimate"],v.filter(t=>!t.endsWith("Color")))},kdeCoords:{fn:function(t,e,r){return Array.isArray(t)&&Array.isArray(e)?e.map((e,i)=>({v:b.number(t[i]?t[i].v:null,e.v,r),estimate:b.number(t[i]?t[i].estimate:null,e.estimate,r)})):e},properties:["coords"]}}},dataElementType:A.id,dataElementOptions:e.BarController.defaults.dataElementOptions.concat(v)}]);class R extends e.Chart{constructor(t,r){super(t,T("violin",r,O,A,[e.LinearScale,e.CategoryScale]))}}R.id=O.id,e.registry.addControllers(I,O),e.registry.addElements(S,A),t.BoxAndWiskers=S,t.BoxPlotChart=E,t.BoxPlotController=I,t.StatsBase=P,t.Violin=A,t.ViolinChart=R,t.ViolinController=O,Object.defineProperty(t,"__esModule",{value:!0})}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("chart.js"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["exports","chart.js","chart.js/helpers"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ChartBoxPlot={},e.Chart,e.Chart.helpers)}(this,(function(e,t,r){"use strict";function i(e,t,r){const i=t-1,o=t=>{const o=t*i,n=Math.floor(o),s=o-n,a=e[n];return 0===s?a:r(a,e[Math.min(n+1,i)],s)};return{q1:o(.25),median:o(.5),q3:o(.75)}}function o(e,t=e.length){return i(e,t,((e,t,r)=>e+r*(t-e)))}function n(e,t=e.length){return i(e,t,((e,t,r)=>e+(t-e)*r))}function s(e,t=e.length){return i(e,t,(e=>e))}function a(e,t=e.length){return i(e,t,((e,t)=>t))}function l(e,t=e.length){return i(e,t,((e,t,r)=>r<.5?e:t))}function u(e,t=e.length){return i(e,t,((e,t)=>.5*(e+t)))}function h(e,t=e.length){const r=t,i=Math.floor((r+3)/2)/2,o=t=>.5*(e[Math.floor(t)-1]+e[Math.ceil(t)-1]);return{q1:o(i),median:o((r+1)/2),q3:o(r+1-i)}}function d(e,t=e.length){return h(e,t)}function m(e,t={}){const{quantiles:r,validAndSorted:i,coef:n,whiskersMode:s,eps:a}=Object.assign({coef:1.5,eps:.01,quantiles:o,validAndSorted:!1,whiskersMode:"nearest"},t),{missing:l,s:u,min:h,max:d,sum:m}=i?function(e){if(0===e.length)return{sum:Number.NaN,min:Number.NaN,max:Number.NaN,missing:0,s:[]};const t=e[0],r=e[e.length-1],i=(e,t)=>e+t;return{sum:(e instanceof Float32Array||Float64Array,e.reduce(i,0)),min:t,max:r,missing:0,s:e}}(e):function(e){let t=Number.POSITIVE_INFINITY,r=Number.NEGATIVE_INFINITY,i=0,o=0;const n=e.length,s=e instanceof Float64Array?new Float64Array(n):new Float32Array(n);for(let a=0;a<n;++a){const n=e[a];null==n||Number.isNaN(n)||(s[o]=n,o++,n<t&&(t=n),n>r&&(r=n),i+=n)}const a=n-o;if(0===o)return{sum:i,min:t,max:r,missing:a,s:[]};const l=o===n?s:s.subarray(0,o);return l.sort(((e,t)=>e===t?0:e<t?-1:1)),{sum:i,min:l[0],max:l[l.length-1],missing:a,s:l}}(e),c={min:Number.NaN,max:Number.NaN,mean:Number.NaN,missing:l,iqr:Number.NaN,count:e.length,whiskerHigh:Number.NaN,whiskerLow:Number.NaN,outlier:[],median:Number.NaN,q1:Number.NaN,q3:Number.NaN,items:[]},f=(e,t)=>Math.abs(e-t)<a,g=e.length-l;if(0===g)return c;const{median:p,q1:x,q3:y}=r(u,g),b=y-x,w="number"==typeof n&&n>0;let q=w?Math.max(h,x-n*b):h,k=w?Math.min(d,y+n*b):d;const N=[];for(let e=0;e<g;++e){const t=u[e];if(t>=q||f(t,q)){"nearest"===s&&(q=t);break}0!==N.length&&f(N[N.length-1],t)||N.push(t)}const C=[];for(let e=g-1;e>=0;--e){const t=u[e];if(t<=k||f(t,k)){"nearest"===s&&(k=t);break}0!==C.length&&f(C[C.length-1],t)||0!==N.length&&f(N[N.length-1],t)||C.push(t)}return{min:h,max:d,count:e.length,missing:l,mean:m/g,whiskerHigh:k,whiskerLow:q,outlier:N.concat(C.reverse()),median:p,q1:x,q3:y,iqr:b,items:u}}function c(e){const t=e.length;if(t<1)return Number.NaN;if(1===t)return 0;const r=function(e){return 0===e.length?Number.NaN:e.reduce(((e,t,r)=>e+(t-e)/(r+1)),0)}(e);return e.reduce(((e,t)=>e+(t-r)*(t-r)),0)/(e.length-1)}function f(e,t,r){const i=function(e,t){const r=t(e),i=(r.q3-r.q1)/1.34;return 1.06*Math.min(Math.sqrt(c(e)),i)*Math.pow(e.length,-.2)}(t,r);return e.map((e=>{const r=t.reduce(((t,r)=>{return t+(o=(e-r)/i,1/Math.sqrt(2*Math.PI)*Math.exp(-.5*o*o));var o}),0);return{v:e,estimate:r/i/t.length}}))}const g={coef:1.5,quantiles:7};function p(e){return{coef:null==e||"number"!=typeof e.coef?g.coef:e.coef,quantiles:function(e){return"function"==typeof e?e:{hinges:d,fivenum:h,7:o,quantiles:o,linear:n,lower:s,higher:a,nearest:l,midpoint:u}[e]||o}(null==e||null==e.quantiles?o:e.quantiles)}}function x(e,t){if(e){if("number"==typeof e.median&&"number"==typeof e.q1&&"number"==typeof e.q3){if(void 0===e.whiskerMin){const{coef:r}=p(t),{whiskerMin:i,whiskerMax:o}=function(e,t,r=1.5){const i=e.q3-e.q1,o="number"==typeof r&&r>0;let n=o?Math.max(e.min,e.q1-r*i):e.min,s=o?Math.min(e.max,e.q3+r*i):e.max;if(Array.isArray(t)){for(let e=0;e<t.length;e++){const r=t[e];if(r>=n){n=r;break}}for(let e=t.length-1;e>=0;e--){const r=t[e];if(r<=s){s=r;break}}}return{whiskerMin:n,whiskerMax:s}}(e,Array.isArray(e.items)?e.items.slice().sort(((e,t)=>e-t)):null,r);e.whiskerMin=i,e.whiskerMax=o}return e}if(Array.isArray(e))return function(e,t){const r=m(e,p(t));return{items:Array.from(r.items),outliers:r.outlier,whiskerMax:r.whiskerHigh,whiskerMin:r.whiskerLow,max:r.max,median:r.median,mean:r.mean,min:r.min,q1:r.q1,q3:r.q3}}(e,t)}}function y(e,t){if(e){if("number"==typeof e.median&&Array.isArray(e.coords))return e;if(Array.isArray(e))return function(e,t){if(0===e.length)return;const r=e.filter((e=>"number"==typeof e&&!Number.isNaN(e))).sort(((e,t)=>e-t)),i=r.reduce(((e,t)=>e+t),0)/r.length,{quantiles:o}=p(t),n=o(r),s=r[0],a=r[r.length-1],l=[],u=(a-s)/t.points;for(let e=s;e<=a&&u>0;e+=u)l.push(e);l[l.length-1]!==a&&l.push(a);const h=f(r,l,o),d=h.reduce(((e,t)=>Math.max(e,t.estimate)),Number.NEGATIVE_INFINITY);return{...n,min:s,items:r,mean:i,max:a,coords:h,outliers:[],maxEstimate:d}}(e,t)}}const b={number:(e,t,r)=>e===t||null==e?t:null==t?e:e+(t-e)*r};function w(e,t,r){return"number"==typeof e&&"number"==typeof t?b.number(e,t,r):Array.isArray(e)&&Array.isArray(t)?t.map(((t,i)=>b.number(e[i],t,r))):t}function q(e){const t=e.formattedValue,r=this;t&&null!=r._tooltipOutlier&&e.datasetIndex===r._tooltipOutlier.datasetIndex&&(t.hoveredOutlierIndex=r._tooltipOutlier.index)}function k(e,t){if(!e.length)return!1;let r=0,i=0,o=0;for(let n=0;n<e.length;++n){const s=e[n].element;if(s&&s.hasValue()){const e=s.tooltipPosition(t,this);r+=e.x,i+=e.y,++o}}return{x:r/o,y:i/o}}function N(e){const t=["borderColor","backgroundColor"].concat(e.filter((e=>e.endsWith("Color"))));return Object.assign({animations:{numberArray:{fn:w,properties:["outliers","items"]},colors:{type:"color",properties:t}},transitions:{show:{animations:{colors:{type:"color",properties:t,from:"transparent"}}},hide:{animations:{colors:{type:"color",properties:t,to:"transparent"}}}},minStats:"min",maxStats:"max"},g)}function C(){return{plugins:{tooltips:{position:k.register().id,callbacks:{beforeLabel:q}}}}}k.id="averageInstance",k.register=()=>(t.Tooltip.positioners.averageInstance=k,k);class M extends t.BarController{_transformStats(e,t,r){for(const i of["min","max","median","q3","q1","mean"]){const o=t[i];"number"==typeof o&&(e[i]=r(o))}for(const i of["outliers","items"])Array.isArray(t[i])&&(e[i]=t[i].map(r))}getMinMax(e,t){const r=e.axis,i=this.options;e.axis=i.minStats;const o=super.getMinMax(e,t).min;e.axis=i.maxStats;const n=super.getMinMax(e,t).max;return e.axis=r,{min:o,max:n}}parsePrimitiveData(e,t,r,i){const o=e.vScale,n=e.iScale,s=n.getLabels(),a=[];for(let e=0;e<i;e++){const i=e+r,l={};l[n.axis]=n.parse(s[i],i);const u=this._parseStats(null==t?null:t[i],this.options);u&&(Object.assign(l,u),l[o.axis]=u.median),a.push(l)}return a}parseArrayData(e,t,r,i){return this.parsePrimitiveData(e,t,r,i)}parseObjectData(e,t,r,i){return this.parsePrimitiveData(e,t,r,i)}getLabelAndValue(e){const t=super.getLabelAndValue(e),r=this._cachedMeta.vScale,i=this.getParsed(e);if(!r||!i||"NaN"===t.value)return t;t.value={raw:i,hoveredOutlierIndex:-1},this._transformStats(t.value,i,(e=>r.getLabelForValue(e)));const o=this._toStringStats(t.value);return t.value.toString=function(){return this.hoveredOutlierIndex>=0?`(outlier: ${this.outliers[this.hoveredOutlierIndex]})`:o},t}_toStringStats(e){const t=e=>null==e?"NaN":e.toLocaleString();return`(min: ${t(e.min)}, 25% quantile: ${t(e.q1)}, median: ${t(e.median)}, mean: ${t(e.mean)}, 75% quantile: ${t(e.q3)}, max: ${t(e.max)})`}updateElement(e,t,r,i){const o="reset"===i,n=this._cachedMeta.vScale,s=this.getParsed(t),a=n.getBasePixel();r._datasetIndex=this.index,r._index=t,this._transformStats(r,s,(e=>o?a:n.getPixelForValue(e,t))),super.updateElement(e,t,r,i)}}const v={borderWidth:1,outlierStyle:"circle",outlierRadius:2,outlierBorderWidth:1,itemStyle:"circle",itemRadius:0,itemBorderWidth:0,meanStyle:"circle",meanRadius:3,meanBorderWidth:1,hitPadding:2,outlierHitRadius:4},_={outlierBackgroundColor:"backgroundColor",outlierBorderColor:"borderColor",itemBackgroundColor:"backgroundColor",itemBorderColor:"borderColor",meanBackgroundColor:"backgroundColor",meanBorderColor:"borderColor"},B=Object.keys(v).concat(Object.keys(_));class S extends t.Element{isVertical(){return null==this.getProps(["height"]).height}_drawItems(e){const t=this.isVertical(),i=this.getProps(["x","y","items","width","height","outliers"]),o=this.options;if(o.itemRadius<=0||!i.items||i.items.length<=0)return;e.save(),e.strokeStyle=o.itemBorderColor,e.fillStyle=o.itemBackgroundColor,e.lineWidth=o.itemBorderWidth;const n=function(e=Date.now()){let t=e;return()=>(t=(9301*t+49297)%233280,t/233280)}(1e3*this._datasetIndex+this._index),s={pointStyle:o.itemStyle,radius:o.itemRadius,borderWidth:o.itemBorderWidth},a=new Set(i.outliers||[]);t?i.items.forEach((t=>{a.has(t)||r.drawPoint(e,s,i.x-i.width/2+n()*i.width,t)})):i.items.forEach((t=>{a.has(t)||r.drawPoint(e,s,t,i.y-i.height/2+n()*i.height)})),e.restore()}_drawOutliers(e){const t=this.isVertical(),i=this.getProps(["x","y","outliers"]),o=this.options;if(o.outlierRadius<=0||!i.outliers||0===i.outliers.length)return;e.save(),e.fillStyle=o.outlierBackgroundColor,e.strokeStyle=o.outlierBorderColor,e.lineWidth=o.outlierBorderWidth;const n={pointStyle:o.outlierStyle,radius:o.outlierRadius,borderWidth:o.outlierBorderWidth};t?i.outliers.forEach((t=>{r.drawPoint(e,n,i.x,t)})):i.outliers.forEach((t=>{r.drawPoint(e,n,t,i.y)})),e.restore()}_drawMeanDot(e){const t=this.isVertical(),i=this.getProps(["x","y","mean"]),o=this.options;if(o.meanRadius<=0||null==i.mean||Number.isNaN(i.mean))return;e.save(),e.fillStyle=o.meanBackgroundColor,e.strokeStyle=o.meanBorderColor,e.lineWidth=o.meanBorderWidth;const n={pointStyle:o.meanStyle,radius:o.meanRadius,borderWidth:o.meanBorderWidth};t?r.drawPoint(e,n,i.x,i.mean):r.drawPoint(e,n,i.mean,i.y),e.restore()}_getBounds(e){return{left:0,top:0,right:0,bottom:0}}_getHitBounds(e){const t=this.options.hitPadding,r=this._getBounds(e);return{left:r.left-t,top:r.top-t,right:r.right+t,bottom:r.bottom+t}}inRange(e,t,r){return(!Number.isNaN(this.x)||!Number.isNaN(this.y))&&(this._boxInRange(e,t,r)||this._outlierIndexInRange(e,t,r)>=0)}inXRange(e,t){const r=this._getHitBounds(t);return e>=r.left&&e<=r.right}inYRange(e,t){const r=this._getHitBounds(t);return e>=r.top&&e<=r.bottom}_outlierIndexInRange(e,t,r){const i=this.getProps(["x","y"],r),o=this.options.outlierHitRadius,n=this._getOutliers(r),s=this.isVertical();if(s&&Math.abs(e-i.x)>o||!s&&Math.abs(t-i.y)>o)return-1;const a=s?t:e;for(let e=0;e<n.length;e++)if(Math.abs(n[e]-a)<=o)return e;return-1}_boxInRange(e,t,r){const i=this._getHitBounds(r);return e>=i.left&&e<=i.right&&t>=i.top&&t<=i.bottom}getCenterPoint(e){const t=this.getProps(["x","y"],e);return{x:t.x,y:t.y}}_getOutliers(e){return this.getProps(["outliers"],e).outliers||[]}tooltipPosition(e,t){if(!e||"boolean"==typeof e)return this.getCenterPoint();t&&delete t._tooltipOutlier;const r=this.getProps(["x","y"]),i=this._outlierIndexInRange(e.x,e.y);return i<0||!t?this.getCenterPoint():(t._tooltipOutlier={index:i,datasetIndex:this._datasetIndex},this.isVertical()?{x:r.x,y:this._getOutliers()[i]}:{x:this._getOutliers()[i],y:r.y})}}const P=B.concat(["medianColor","lowerBackgroundColor"]);class T extends S{draw(e){e.save(),e.fillStyle=this.options.backgroundColor,e.strokeStyle=this.options.borderColor,e.lineWidth=this.options.borderWidth,this._drawBoxPlot(e),this._drawOutliers(e),this._drawMeanDot(e),e.restore(),this._drawItems(e)}_drawBoxPlot(e){this.isVertical()?this._drawBoxPlotVertical(e):this._drawBoxPlotHorizontal(e)}_drawBoxPlotVertical(e){const t=this.options,r=this.getProps(["x","width","q1","q3","median","whiskerMin","whiskerMax"]),i=r.x,o=r.width,n=i-o/2;r.q3>r.q1?e.fillRect(n,r.q1,o,r.q3-r.q1):e.fillRect(n,r.q3,o,r.q1-r.q3),e.save(),t.medianColor&&"transparent"!==t.medianColor&&"#0000"!==t.medianColor&&(e.strokeStyle=t.medianColor),e.beginPath(),e.moveTo(n,r.median),e.lineTo(n+o,r.median),e.closePath(),e.stroke(),e.restore(),e.save(),t.lowerBackgroundColor&&"transparent"!==t.lowerBackgroundColor&&"#0000"!==t.lowerBackgroundColor&&(e.fillStyle=t.lowerBackgroundColor,r.q3>r.q1?e.fillRect(n,r.median,o,r.q3-r.median):e.fillRect(n,r.median,o,r.q1-r.median)),e.restore(),r.q3>r.q1?e.strokeRect(n,r.q1,o,r.q3-r.q1):e.strokeRect(n,r.q3,o,r.q1-r.q3),e.beginPath(),e.moveTo(n,r.whiskerMin),e.lineTo(n+o,r.whiskerMin),e.moveTo(i,r.whiskerMin),e.lineTo(i,r.q1),e.moveTo(n,r.whiskerMax),e.lineTo(n+o,r.whiskerMax),e.moveTo(i,r.whiskerMax),e.lineTo(i,r.q3),e.closePath(),e.stroke()}_drawBoxPlotHorizontal(e){const t=this.options,r=this.getProps(["y","height","q1","q3","median","whiskerMin","whiskerMax"]),i=r.y,o=r.height,n=i-o/2;r.q3>r.q1?e.fillRect(r.q1,n,r.q3-r.q1,o):e.fillRect(r.q3,n,r.q1-r.q3,o),e.save(),t.medianColor&&"transparent"!==t.medianColor&&(e.strokeStyle=t.medianColor),e.beginPath(),e.moveTo(r.median,n),e.lineTo(r.median,n+o),e.closePath(),e.stroke(),e.restore(),e.save(),t.lowerBackgroundColor&&"transparent"!==t.lowerBackgroundColor&&(e.fillStyle=t.lowerBackgroundColor,r.q3>r.q1?e.fillRect(r.median,n,r.q3-r.median,o):e.fillRect(r.median,n,r.q1-r.median,o)),e.restore(),r.q3>r.q1?e.strokeRect(r.q1,n,r.q3-r.q1,o):e.strokeRect(r.q3,n,r.q1-r.q3,o),e.beginPath(),e.moveTo(r.whiskerMin,n),e.lineTo(r.whiskerMin,n+o),e.moveTo(r.whiskerMin,i),e.lineTo(r.q1,i),e.moveTo(r.whiskerMax,n),e.lineTo(r.whiskerMax,n+o),e.moveTo(r.whiskerMax,i),e.lineTo(r.q3,i),e.closePath(),e.stroke()}_getBounds(e){const t=this.isVertical();if(null==this.x)return{left:0,top:0,right:0,bottom:0};if(t){const{x:t,width:r,whiskerMax:i,whiskerMin:o}=this.getProps(["x","width","whiskerMin","whiskerMax"],e),n=t-r/2;return{left:n,top:i,right:n+r,bottom:o}}const{y:r,height:i,whiskerMax:o,whiskerMin:n}=this.getProps(["y","height","whiskerMin","whiskerMax"],e),s=r-i/2;return{left:n,top:s,right:o,bottom:s+i}}}T.id="boxandwhiskers",T.defaults=Object.assign({},t.BarElement.defaults,v,{medianColor:"transparent",lowerBackgroundColor:"transparent"}),T.defaultRoutes=Object.assign({},t.BarElement.defaultRoutes,_);class A extends S{draw(e){e.save(),e.fillStyle=this.options.backgroundColor,e.strokeStyle=this.options.borderColor,e.lineWidth=this.options.borderWidth;const t=this.getProps(["x","y","width","height","min","max","coords","maxEstimate"]);r.drawPoint(e,{pointStyle:"rectRot",radius:5,borderWidth:this.options.borderWidth},t.x,t.y),t.coords&&t.coords.length>0&&this._drawCoords(e,t),this._drawOutliers(e),this._drawMeanDot(e),e.restore(),this._drawItems(e)}_drawCoords(e,t){if(e.beginPath(),this.isVertical()){const r=t.x,i=t.width/2/t.maxEstimate;e.moveTo(r,t.min),t.coords.forEach((t=>{e.lineTo(r-t.estimate*i,t.v)})),e.lineTo(r,t.max),e.moveTo(r,t.min),t.coords.forEach((t=>{e.lineTo(r+t.estimate*i,t.v)})),e.lineTo(r,t.max)}else{const r=t.y,i=t.height/2/t.maxEstimate;e.moveTo(t.min,r),t.coords.forEach((t=>{e.lineTo(t.v,r-t.estimate*i)})),e.lineTo(t.max,r),e.moveTo(t.min,r),t.coords.forEach((t=>{e.lineTo(t.v,r+t.estimate*i)})),e.lineTo(t.max,r)}e.closePath(),e.stroke(),e.fill()}_getBounds(e){if(this.isVertical()){const{x:t,width:r,min:i,max:o}=this.getProps(["x","width","min","max"],e),n=t-r/2;return{left:n,top:o,right:n+r,bottom:i}}const{y:t,height:r,min:i,max:o}=this.getProps(["y","height","min","max"],e),n=t-r/2;return{left:i,top:n,right:o,bottom:n+r}}}function I(e,r,i,o=[],n=[]){t.registry.addControllers(i),Array.isArray(o)?t.registry.addElements(...o):t.registry.addElements(o),Array.isArray(n)?t.registry.addScales(...n):t.registry.addScales(n);const s=r;return s.type=e,s}A.id="violin",A.defaults=Object.assign({},t.BarElement.defaults,v),A.defaultRoutes=Object.assign({},t.BarElement.defaultRoutes,_);class R extends M{_parseStats(e,t){return x(e,t)}_transformStats(e,t,r){super._transformStats(e,t,r);for(const i of["whiskerMin","whiskerMax"])e[i]=r(t[i])}}R.id="boxplot",R.defaults=r.merge({},[t.BarController.defaults,N(P),{animations:{numbers:{type:"number",properties:t.BarController.defaults.animations.numbers.properties.concat(["q1","q3","min","max","median","whiskerMin","whiskerMax","mean"],P.filter((e=>!e.endsWith("Color"))))}},dataElementType:T.id}]),R.overrides=r.merge({},[t.BarController.overrides,C()]);class E extends t.Chart{constructor(e,r){super(e,I("boxplot",r,R,T,[t.LinearScale,t.CategoryScale]))}}E.id=R.id;class O extends M{_parseStats(e,t){return y(e,t)}_transformStats(e,t,r){super._transformStats(e,t,r),e.maxEstimate=t.maxEstimate,Array.isArray(t.coords)&&(e.coords=t.coords.map((e=>Object.assign({},e,{v:r(e.v)}))))}}O.id="violin",O.defaults=r.merge({},[t.BarController.defaults,N(B),{points:100,animations:{numbers:{type:"number",properties:t.BarController.defaults.animations.numbers.properties.concat(["q1","q3","min","max","median","maxEstimate"],B.filter((e=>!e.endsWith("Color"))))},kdeCoords:{fn:function(e,t,r){return Array.isArray(e)&&Array.isArray(t)?t.map(((t,i)=>({v:b.number(e[i]?e[i].v:null,t.v,r),estimate:b.number(e[i]?e[i].estimate:null,t.estimate,r)}))):t},properties:["coords"]}},dataElementType:A.id}]),O.overrides=r.merge({},[t.BarController.overrides,C()]);class W extends t.Chart{constructor(e,r){super(e,I("violin",r,O,A,[t.LinearScale,t.CategoryScale]))}}W.id=O.id,t.registry.addControllers(R,O),t.registry.addElements(T,A),e.BoxAndWiskers=T,e.BoxPlotChart=E,e.BoxPlotController=R,e.StatsBase=S,e.Violin=A,e.ViolinChart=W,e.ViolinController=O,Object.defineProperty(e,"__esModule",{value:!0})}));
//# sourceMappingURL=index.umd.min.js.map
{
"name": "@sgratzl/chartjs-chart-boxplot",
"description": "Chart.js module for charting boxplots and violin charts",
"version": "3.0.0-beta.9",
"version": "3.0.0-rc.0",
"publishConfig": {

@@ -40,4 +40,5 @@ "access": "public"

"global": "ChartBoxPlot",
"module": "build/index.esm.js",
"main": "build/index.js",
"module": "build/index.js",
"main": "build/index.cjs.js",
"browser": "build/index.umd.js",
"unpkg": "build/index.umd.min.js",

@@ -49,6 +50,7 @@ "jsdelivr": "build/index.umd.min.js",

"build",
"src/**/*.ts"
"src/**/*.ts",
"src/**/*.tsx"
],
"peerDependencies": {
"chart.js": "^3.0.0-beta"
"chart.js": "^3.0.0-rc"
},

@@ -64,19 +66,19 @@ "browserslist": [

"devDependencies": {
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.1.0",
"@rollup/plugin-replace": "^2.3.4",
"@rollup/plugin-typescript": "^8.1.0",
"@types/jest": "^26.0.20",
"@types/jest-image-snapshot": "^4.1.3",
"@types/node": "^14.14.21",
"@typescript-eslint/eslint-plugin": "^4.13.0",
"@typescript-eslint/parser": "^4.13.0",
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-node-resolve": "^11.2.0",
"@rollup/plugin-replace": "^2.4.1",
"@rollup/plugin-typescript": "^8.2.0",
"@types/jest": "^26.0.21",
"@types/jest-image-snapshot": "^4.3.0",
"@types/node": "^14.14.35",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0",
"@yarnpkg/pnpify": "^2.4.0",
"canvas": "^2.6.1",
"canvas": "^2.7.0",
"canvas-5-polyfill": "^0.1.5",
"chart.js": "^3.0.0-beta.9",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.1.0",
"chart.js": "^3.0.0-rc",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-flowtype": "^5.4.0",
"eslint-plugin-import": "^2.22.1",

@@ -88,36 +90,36 @@ "eslint-plugin-jsx-a11y": "^6.4.1",

"jest": "^26.6.3",
"jest-image-snapshot": "^4.3.0",
"jest-image-snapshot": "^4.4.0",
"prettier": "^2.2.1",
"release-it": "^14.2.2",
"release-it": "^14.4.1",
"rimraf": "^3.0.2",
"rollup": "^2.36.2",
"rollup": "^2.42.1",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-dts": "^2.0.1",
"rollup-plugin-dts": "^3.0.1",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "^26.4.4",
"ts-jest": "^26.5.4",
"tslib": "^2.1.0",
"typedoc": "^0.20.16",
"typescript": "^4.1.3"
"typedoc": "^0.20.32",
"typescript": "^4.2.3"
},
"scripts": {
"clean": "rimraf build node_modules \"*.tgz\"",
"compile": "tsc -p tsconfig.json --noEmit",
"start": "npm run watch",
"clean": "rimraf build docs node_modules \"*.tgz\" \"*.tsbuildinfo\"",
"compile": "tsc -b tsconfig.c.json",
"compile:types": "tsc -p tsconfig.c.json --emitDeclarationOnly",
"start": "yarn run watch",
"watch": "rollup -c -w",
"build": "rollup -c",
"build": "rollup -c && yarn run compile:types",
"test": "jest --passWithNoTests",
"test:watch": "jest --passWithNoTests --watch",
"test:coverage": "jest --passWithNoTests --coverage",
"lint": "npm run eslint && npm run prettier",
"fix": "npm run eslint:fix && npm run prettier:write",
"prettier:write": "prettier */** --write",
"prettier": "prettier */** --check",
"lint": "yarn run eslint && yarn run prettier",
"fix": "yarn run eslint:fix && yarn run prettier:write",
"prettier:write": "prettier \"*\" \"*/**\" --write",
"prettier": "prettier \"*\" \"*/**\" --check",
"eslint": "eslint src --ext .ts,.tsx",
"eslint:fix": "npm run eslint -- --fix",
"docs": "typedoc",
"prepare": "npm run build",
"eslint:fix": "yarn run eslint --fix",
"docs": "typedoc src/index.ts",
"prepare": "yarn run build",
"release": "release-it --disable-metrics --npm.skipChecks",
"release:pre": "release-it --disable-metrics --npm.skipChecks --preRelease=alpha --npm.tag=next",
"release:beta": "release-it --disable-metrics --npm.skipChecks --preRelease=beta --npm.tag=next"
"release:pre": "release-it --disable-metrics --npm.skipChecks --preRelease=alpha --npm.tag=next"
}
}

@@ -105,4 +105,4 @@ # Chart.js Box and Violin Plot

cat .yarnrc_patch.yml >> .yarnrc.yml
yarn
yarn pnpify --sdk
yarn install
yarn pnpify --sdk vscode
```

@@ -109,0 +109,0 @@

@@ -1,2 +0,2 @@

import { IKDEPoint } from './data';
import type { IKDEPoint } from './data';

@@ -3,0 +3,0 @@ const interpolators = {

@@ -93,2 +93,4 @@ const Months = [

max: base[shift + 4]!,
items: base,
mean: base.reduce((acc, v) => acc + v, 0) / base.length,
outliers: base.slice(0, 3).concat(base.slice(shift + 5)),

@@ -95,0 +97,0 @@ };

import { interpolateNumberArray } from '../animation';
import { outlierPositioner, patchInHoveredOutlier } from '../tooltip';
import { BarController, Element, ChartMeta, LinearScale, Scale, UpdateMode } from 'chart.js';
import { defaultStatsOptions, IBaseOptions } from '../data';
import { defaultStatsOptions, IBaseOptions, IBaseStats } from '../data';
export /*#__PURE__*/ function baseDefaults(keys: string[]) {
const colorKeys = ['borderColor', 'backgroundColor'].concat(keys.filter((c) => c.endsWith('Color')));
return {
datasets: Object.assign(
{
animation: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
show: {
return Object.assign(
{
animations: {
numberArray: {
fn: interpolateNumberArray,
properties: ['outliers', 'items'],
},
colors: {
type: 'color',
properties: colorKeys,
},
},
transitions: {
show: {
animations: {
colors: {

@@ -27,3 +29,5 @@ type: 'color',

},
hide: {
},
hide: {
animations: {
colors: {

@@ -36,11 +40,18 @@ type: 'color',

},
minStats: 'min',
maxStats: 'max',
},
defaultStatsOptions
),
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
minStats: 'min',
maxStats: 'max',
},
defaultStatsOptions
);
}
export function defaultOverrides() {
return {
plugins: {
tooltips: {
position: outlierPositioner.register().id,
callbacks: {
beforeLabel: patchInHoveredOutlier,
},
},

@@ -51,8 +62,22 @@ },

export abstract class StatsBase<S extends { median: number }, C extends Required<IBaseOptions>> extends BarController {
declare _config: C;
export abstract class StatsBase<S extends IBaseStats, C extends Required<IBaseOptions>> extends BarController {
declare options: C;
protected _transformStats<T>(target: any, source: S, mapper: (v: number) => T): void {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'mean'] as const) {
const v = source[key];
if (typeof v === 'number') {
target[key] = mapper(v);
}
}
for (const key of ['outliers', 'items'] as const) {
if (Array.isArray(source[key])) {
target[key] = source[key].map(mapper);
}
}
}
getMinMax(scale: Scale, canStack?: boolean | undefined) {
const bak = scale.axis;
const config = this._config;
const config = this.options;
scale.axis = config.minStats;

@@ -75,3 +100,3 @@ const min = super.getMinMax(scale, canStack).min;

parsed[iScale.axis] = iScale.parse(labels[index], index);
const stats = this._parseStats(data == null ? null : data[index], this._config);
const stats = this._parseStats(data == null ? null : data[index], this.options);
if (stats) {

@@ -94,3 +119,3 @@ Object.assign(parsed, stats);

protected abstract _parseStats(value: any, config: C): S;
protected abstract _parseStats(value: any, options: C): S;

@@ -100,3 +125,3 @@ getLabelAndValue(index: number) {

const vScale = this._cachedMeta.vScale;
const parsed = this.getParsed(index);
const parsed = (this.getParsed(index) as unknown) as S;
if (!vScale || !parsed || r.value === 'NaN') {

@@ -109,3 +134,3 @@ return r;

};
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v), 'string');
this._transformStats(r.value, parsed, (v) => vScale.getLabelForValue(v));
const s = this._toStringStats(r.value);

@@ -115,2 +140,3 @@ r.value.toString = function () {

if (this.hoveredOutlierIndex >= 0) {
// TODO formatter
return `(outlier: ${this.outliers[this.hoveredOutlierIndex]})`;

@@ -123,21 +149,20 @@ }

protected abstract _toStringStats(b: S): string;
protected _toStringStats(b: S) {
// TODO formatter
const f = (v: number) => (v == null ? 'NaN' : v.toLocaleString());
return `(min: ${f(b.min)}, 25% quantile: ${f(b.q1)}, median: ${f(b.median)}, mean: ${f(b.mean)}, 75% quantile: ${f(
b.q3
)}, max: ${f(b.max)})`;
}
protected abstract _transformStats<T>(
target: Record<keyof S, T | T[]>,
source: S,
mapper: (v: number) => T,
mode: UpdateMode | 'string'
): void;
updateElement(rectangle: Element, index: number, properties: any, mode: UpdateMode) {
const reset = mode === 'reset';
const scale = this._cachedMeta.vScale as LinearScale;
const parsed = this.getParsed(index);
const parsed = (this.getParsed(index) as unknown) as S;
const base = scale.getBasePixel();
properties._datasetIndex = this.index;
properties._index = index;
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)), mode);
this._transformStats(properties, parsed, (v) => (reset ? base : scale.getPixelForValue(v, index)));
super.updateElement(rectangle, index, properties, mode);
}
}

@@ -199,3 +199,3 @@ import createChart from '../__tests__/createChart';

fill: 1,
data: samples.numbers({ count: 7, max: 150 }).map((d) => ({ y: d })),
data: samples.numbers({ count: 7, max: 150 }) as any,
},

@@ -202,0 +202,0 @@ ],

@@ -13,6 +13,5 @@ import { asBoxPlotStats, IBaseStats, IBoxPlot, IBoxplotOptions } from '../data';

CartesianScaleTypeRegistry,
ScriptableContext,
} from 'chart.js';
import { merge } from 'chart.js/helpers';
import { baseDefaults, StatsBase } from './base';
import { baseDefaults, StatsBase, defaultOverrides } from './base';
import { BoxAndWiskers, IBoxAndWhiskersOptions } from '../elements';

@@ -23,21 +22,11 @@ import patchController from './patchController';

export class BoxPlotController extends StatsBase<IBoxPlot, Required<IBoxplotOptions>> {
_parseStats(value: any, config: IBoxplotOptions) {
protected _parseStats(value: any, config: IBoxplotOptions) {
return asBoxPlotStats(value, config);
}
_toStringStats(b: IBoxPlot) {
return `(min: ${b.min}, 25% quantile: ${b.q1}, median: ${b.median}, 75% quantile: ${b.q3}, max: ${b.max})`;
}
_transformStats<T>(target: any, source: IBoxPlot, mapper: (v: number) => T) {
for (const key of ['min', 'max', 'median', 'q3', 'q1', 'whiskerMin', 'whiskerMax', 'mean']) {
target[key] = mapper(
source[key as 'min' | 'max' | 'median' | 'q3' | 'q1' | 'whiskerMin' | 'whiskerMax' | 'mean']
);
protected _transformStats<T>(target: any, source: IBoxPlot, mapper: (v: number) => T) {
super._transformStats(target, source, mapper);
for (const key of ['whiskerMin', 'whiskerMax']) {
target[key] = mapper(source[key as 'whiskerMin' | 'whiskerMax']);
}
for (const key of ['outliers', 'items']) {
if (Array.isArray(source[key as keyof IBoxPlot])) {
target[key] = source[key as 'outliers' | 'items'].map(mapper);
}
}
}

@@ -50,17 +39,15 @@

{
datasets: {
animation: {
numbers: {
type: 'number',
properties: BarController.defaults.datasets.animation.numbers.properties.concat(
['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'],
boxOptionsKeys.filter((c) => !c.endsWith('Color'))
),
},
animations: {
numbers: {
type: 'number',
properties: (BarController.defaults as any).animations.numbers.properties.concat(
['q1', 'q3', 'min', 'max', 'median', 'whiskerMin', 'whiskerMax', 'mean'],
boxOptionsKeys.filter((c) => !c.endsWith('Color'))
),
},
},
dataElementType: BoxAndWiskers.id,
dataElementOptions: BarController.defaults.dataElementOptions.concat(boxOptionsKeys),
},
]);
static readonly overrides: any = /*#__PURE__*/ merge({}, [(BarController as any).overrides, defaultOverrides()]);
}

@@ -71,4 +58,4 @@

IBoxplotOptions,
ScriptableAndArrayOptions<IBoxAndWhiskersOptions, ScriptableContext>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext> {}
ScriptableAndArrayOptions<IBoxAndWhiskersOptions, 'boxplot'>,
ScriptableAndArrayOptions<CommonHoverOptions, 'boxplot'> {}

@@ -85,4 +72,5 @@ export type BoxPlotDataPoint = number[] | (Partial<IBoxPlot> & IBaseStats);

datasetOptions: BoxPlotControllerDatasetOptions;
defaultDataPoint: BoxPlotDataPoint[];
defaultDataPoint: BoxPlotDataPoint;
scales: keyof CartesianScaleTypeRegistry;
parsedDataType: IBoxPlot & ChartTypeRegistry['bar']['parsedDataType'];
};

@@ -89,0 +77,0 @@ }

@@ -13,6 +13,5 @@ import { asViolinStats, IBaseStats, IViolin, IViolinOptions } from '../data';

CartesianScaleTypeRegistry,
ScriptableContext,
} from 'chart.js';
import { merge } from 'chart.js/helpers';
import { StatsBase, baseDefaults } from './base';
import { StatsBase, baseDefaults, defaultOverrides } from './base';
import { baseOptionKeys } from '../elements/base';

@@ -24,22 +23,11 @@ import { IViolinElementOptions, Violin } from '../elements';

export class ViolinController extends StatsBase<IViolin, Required<IViolinOptions>> {
_parseStats(value: any, config: IViolinOptions) {
protected _parseStats(value: any, config: IViolinOptions) {
return asViolinStats(value, config);
}
_toStringStats(v: IViolin) {
return `(min: ${v.min}, 25% quantile: ${v.q1}, median: ${v.median}, 75% quantile: ${v.q3}, max: ${v.max})`;
}
_transformStats<T>(target: any, source: IViolin, mapper: (v: number) => T) {
for (const key of ['min', 'max', 'median', 'q3', 'q1']) {
target[key] = mapper(source[key as 'min' | 'max' | 'median' | 'q3' | 'q1']);
}
protected _transformStats<T>(target: any, source: IViolin, mapper: (v: number) => T) {
super._transformStats(target, source, mapper);
target.maxEstimate = source.maxEstimate;
for (const key of ['items', 'outliers']) {
if (Array.isArray(source[key as keyof IViolin])) {
target[key] = source[key as 'items' | 'outliers'].map(mapper);
}
}
if (Array.isArray(source.coords)) {
target.coords = source.coords.map((coord) => Object.assign({}, coord, { v: mapper(coord.v) }));
target.coords = source.coords.map((c) => Object.assign({}, c, { v: mapper(c.v) }));
}

@@ -53,23 +41,22 @@ }

{
datasets: {
points: 100,
animation: {
numbers: {
type: 'number',
properties: BarController.defaults.datasets.animation.numbers.properties.concat(
['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'],
baseOptionKeys.filter((c) => !c.endsWith('Color'))
),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
points: 100,
animations: {
numbers: {
type: 'number',
properties: (BarController.defaults as any).animations.numbers.properties.concat(
['q1', 'q3', 'min', 'max', 'median', 'maxEstimate'],
baseOptionKeys.filter((c) => !c.endsWith('Color'))
),
},
kdeCoords: {
fn: interpolateKdeCoords,
properties: ['coords'],
},
},
dataElementType: Violin.id,
dataElementOptions: BarController.defaults.dataElementOptions.concat(baseOptionKeys),
},
]);
static readonly overrides: any = /*#__PURE__*/ merge({}, [(BarController as any).overrides, defaultOverrides()]);
}
export type ViolinDataPoint = number[] | (Partial<IViolin> & IBaseStats);

@@ -79,7 +66,5 @@ export interface ViolinControllerDatasetOptions

IViolinOptions,
ScriptableAndArrayOptions<IViolinElementOptions, ScriptableContext>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext> {}
ScriptableAndArrayOptions<IViolinElementOptions, 'violin'>,
ScriptableAndArrayOptions<CommonHoverOptions, 'violin'> {}
export type ViolinDataPoint = number[] | (Partial<IViolin> & IBaseStats);
// eslint-disable-next-line @typescript-eslint/no-empty-interface

@@ -93,4 +78,5 @@ export interface IViolinChartOptions {}

datasetOptions: ViolinControllerDatasetOptions;
defaultDataPoint: ViolinDataPoint[];
defaultDataPoint: ViolinDataPoint;
scales: keyof CartesianScaleTypeRegistry;
parsedDataType: IViolin & ChartTypeRegistry['bar']['parsedDataType'];
};

@@ -97,0 +83,0 @@ }

@@ -29,10 +29,10 @@ import boxplots, {

median: number;
mean: number;
items: readonly number[];
outliers: readonly number[];
}
export interface IBoxPlot extends IBaseStats {
items: readonly number[];
outliers: readonly number[];
whiskerMax: number;
whiskerMin: number;
mean: number;
}

@@ -46,6 +46,4 @@

export interface IViolin extends IBaseStats {
items: readonly number[];
maxEstimate: number;
coords: IKDEPoint[];
outliers: readonly number[];
}

@@ -205,2 +203,4 @@

const mean = items.reduce((acc, v) => acc + v, 0) / items.length;
const { quantiles } = determineStatsOptions(options);

@@ -229,2 +229,3 @@

items,
mean,
max,

@@ -231,0 +232,0 @@ coords,

import { Element } from 'chart.js';
import { drawPoint } from 'chart.js/helpers';
import { rnd } from '../data';
import { ExtendedTooltip } from '../tooltip';
import type { ExtendedTooltip } from '../tooltip';

@@ -135,2 +135,50 @@ export interface IStatsBaseOptions {

outlierHitRadius: number;
/**
* item style used to render mean dot
* @default circle
*/
meanStyle:
| 'circle'
| 'triangle'
| 'rect'
| 'rectRounded'
| 'rectRot'
| 'cross'
| 'crossRot'
| 'star'
| 'line'
| 'dash';
/**
* radius used to mean dots
* @default 3
* @scriptable
* @indexable
*/
meanRadius: number;
/**
* background color for mean dot
* @default see rectangle.backgroundColor
* @scriptable
* @indexable
*/
meanBackgroundColor: string;
/**
* border color for mean dot
* @default see rectangle.borderColor
* @scriptable
* @indexable
*/
meanBorderColor: string;
/**
* border width for mean dot
* @default 0
* @scriptable
* @indexable
*/
meanBorderWidth: number;
}

@@ -149,2 +197,6 @@

meanStyle: 'circle',
meanRadius: 3,
meanBorderWidth: 1,
hitPadding: 2,

@@ -159,2 +211,4 @@ outlierHitRadius: 4,

itemBorderColor: 'borderColor',
meanBackgroundColor: 'backgroundColor',
meanBorderColor: 'borderColor',
};

@@ -171,2 +225,3 @@

outliers: number[];
mean: number;
}

@@ -182,5 +237,5 @@

_drawItems(ctx: CanvasRenderingContext2D) {
protected _drawItems(ctx: CanvasRenderingContext2D) {
const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'items', 'width', 'height']);
const props = this.getProps(['x', 'y', 'items', 'width', 'height', 'outliers']);
const options = this.options;

@@ -204,10 +259,15 @@

};
const outliers = new Set(props.outliers || []);
if (vert) {
props.items.forEach((v) => {
drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
if (!outliers.has(v)) {
drawPoint(ctx, pointOptions, props.x - props.width / 2 + random() * props.width, v);
}
});
} else {
props.items.forEach((v) => {
drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
if (!outliers.has(v)) {
drawPoint(ctx, pointOptions, v, props.y - props.height / 2 + random() * props.height);
}
});

@@ -218,3 +278,3 @@ }

_drawOutliers(ctx: CanvasRenderingContext2D) {
protected _drawOutliers(ctx: CanvasRenderingContext2D) {
const vert = this.isVertical();

@@ -250,2 +310,29 @@ const props = this.getProps(['x', 'y', 'outliers']);

protected _drawMeanDot(ctx: CanvasRenderingContext2D) {
const vert = this.isVertical();
const props = this.getProps(['x', 'y', 'mean']);
const options = this.options;
if (options.meanRadius <= 0 || props.mean == null || Number.isNaN(props.mean)) {
return;
}
ctx.save();
ctx.fillStyle = options.meanBackgroundColor;
ctx.strokeStyle = options.meanBorderColor;
ctx.lineWidth = options.meanBorderWidth;
const pointOptions = {
pointStyle: options.meanStyle,
radius: options.meanRadius,
borderWidth: options.meanBorderWidth,
};
if (vert) {
drawPoint(ctx, pointOptions, props.x, props.mean);
} else {
drawPoint(ctx, pointOptions, props.mean, props.y);
}
ctx.restore();
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars

@@ -293,3 +380,3 @@ _getBounds(_useFinalPosition?: boolean) {

_outlierIndexInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean) {
protected _outlierIndexInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean) {
const props = this.getProps(['x', 'y'], useFinalPosition);

@@ -313,3 +400,3 @@ const hitRadius = this.options.outlierHitRadius;

_boxInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean) {
protected _boxInRange(mouseX: number, mouseY: number, useFinalPosition?: boolean) {
const bounds = this._getHitBounds(useFinalPosition);

@@ -327,3 +414,3 @@ return mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;

_getOutliers(useFinalPosition?: boolean) {
protected _getOutliers(useFinalPosition?: boolean) {
const props = this.getProps(['outliers'], useFinalPosition);

@@ -330,0 +417,0 @@ return props.outliers || [];

@@ -43,2 +43,3 @@ import { BarElement } from 'chart.js';

this._drawOutliers(ctx);
this._drawMeanDot(ctx);

@@ -50,3 +51,3 @@ ctx.restore();

_drawBoxPlot(ctx: CanvasRenderingContext2D) {
protected _drawBoxPlot(ctx: CanvasRenderingContext2D) {
if (this.isVertical()) {

@@ -59,3 +60,3 @@ this._drawBoxPlotVertical(ctx);

_drawBoxPlotVertical(ctx: CanvasRenderingContext2D) {
protected _drawBoxPlotVertical(ctx: CanvasRenderingContext2D) {
const options = this.options;

@@ -123,3 +124,3 @@ const props = this.getProps(['x', 'width', 'q1', 'q3', 'median', 'whiskerMin', 'whiskerMax']);

_drawBoxPlotHorizontal(ctx: CanvasRenderingContext2D) {
protected _drawBoxPlotHorizontal(ctx: CanvasRenderingContext2D) {
const options = this.options;

@@ -126,0 +127,0 @@ const props = this.getProps(['y', 'height', 'q1', 'q3', 'median', 'whiskerMin', 'whiskerMax']);

import { BarElement } from 'chart.js';
import { drawPoint } from 'chart.js/helpers';
import { IKDEPoint } from '../data';
import type { IKDEPoint } from '../data';
import { StatsBase, baseDefaults, baseRoutes, IStatsBaseOptions, IStatsBaseProps } from './base';

@@ -40,2 +40,3 @@

this._drawOutliers(ctx);
this._drawMeanDot(ctx);

@@ -47,3 +48,3 @@ ctx.restore();

_drawCoords(ctx: CanvasRenderingContext2D, props: IViolinElementProps) {
protected _drawCoords(ctx: CanvasRenderingContext2D, props: IViolinElementProps) {
ctx.beginPath();

@@ -50,0 +51,0 @@ if (this.isVertical()) {

import { InteractionItem, TooltipItem, Tooltip, TooltipModel } from 'chart.js';
export interface ExtendedTooltip extends TooltipModel {
export interface ExtendedTooltip extends TooltipModel<'boxplot' | 'violin'> {
_tooltipOutlier?: {

@@ -10,3 +10,6 @@ index: number;

export function patchInHoveredOutlier(this: TooltipModel, item: TooltipItem) {
export function patchInHoveredOutlier(
this: TooltipModel<'boxplot' | 'violin'>,
item: TooltipItem<'boxplot' | 'violin'>
) {
const value = item.formattedValue as any;

@@ -21,3 +24,3 @@ const that = this as ExtendedTooltip;

export function outlierPositioner(
this: TooltipModel,
this: TooltipModel<'boxplot' | 'violin'>,
items: readonly InteractionItem[],

@@ -24,0 +27,0 @@ eventPosition: { x: number; y: number }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc