diff --git a/README.md b/README.md index 8cbb855..527ff8a 100644 --- a/README.md +++ b/README.md @@ -27,57 +27,71 @@ four new types: `boxplot`, `horizontalBoxplot`, `violin`, and `horizontalViolin` ## Config -// TODO +The config can be done on a per dataset `.data.datasets[0].minStats` or for all datasets under the controllers name. e.g., `.options.boxplot.datasets.minStats`. + +```ts +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: + | 7 + | 'quantiles' + | 'hinges' + | 'fivenum' + | 'linear' + | 'lower' + | 'higher' + | 'nearest' + | 'midpoint' + | ((sortedArr: number[]) => { min: number; q1: number; median: number; q3: number; max: number }); +} + +interface IBoxplotOptions extends IBaseOptions { + // no extra options +} + +interface IViolinOptions extends IBaseOptions { + /** + * number of points that should be samples of the KDE + * @default 100 + */ + points: number; +} -```typescript interface IChartJSOptions { boxplot: { - datasets: { - /** - * 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: - | 7 - | 'quantiles' - | 'hinges' - | 'fivenum' - | 'linear' - | 'lower' - | 'higher' - | 'nearest' - | 'midpoint' - | ((sortedArr: number[]) => { min: number; q1: number; median: number; q3: number; max: number }); - }; + datasets: {}; }; } ``` @@ -86,7 +100,7 @@ interface IChartJSOptions { The boxplot element is called `boxandwhiskers`. The basic options are from the `rectangle` element. The violin element is called `violin` also based on the `rectangle` element. -```typescript +```ts interface IBaseStyling { /** * @default see rectangle @@ -103,18 +117,27 @@ interface IBaseStyling { borderColor: string; /** - * @default null takes the current borderColor + * @default 1 * @scriptable * @indexable */ - medianColor: string; + borderWidth: number; /** - * @default 1 - * @scriptable - * @indexable + * item style used to render outliers + * @default circle */ - borderWidth: number; + outlierStyle: + | 'circle' + | 'triangle' + | 'rect' + | 'rectRounded' + | 'rectRot' + | 'cross' + | 'crossRot' + | 'star' + | 'line' + | 'dash'; /** * radius used to render outliers @@ -132,20 +155,17 @@ interface IBaseStyling { outlierBackgroundColor: string; /** - * to fill color below the median line of the box - * @default transparent + * @default see rectangle.borderColor * @scriptable * @indexable */ - lowerBackgroundColor: string; - + outlierBorderColor: string; /** - * radius used to render items - * @default 0 so disabled + * @default 1 * @scriptable * @indexable */ - itemRadius: number; + outlierBorderWidth: number; /** * item style used to render items @@ -163,9 +183,17 @@ interface IBaseStyling { | 'line' | 'dash'; + /** + * radius used to render items + * @default 0 so disabled + * @scriptable + * @indexable + */ + itemRadius: number; + /** * background color for items - * @default see rectangle backgroundColor + * @default see rectangle.backgroundColor * @scriptable * @indexable */ @@ -173,15 +201,23 @@ interface IBaseStyling { /** * border color for items - * @default see rectangle backgroundColor + * @default see rectangle.borderColor * @scriptable * @indexable */ itemBorderColor: string; + /** + * border width for items + * @default 0 + * @scriptable + * @indexable + */ + itemBorderColor: number; + /** * padding that is added around the bounding box when computing a mouse hit - * @default 1 + * @default 2 * @scriptable * @indexable */ @@ -197,15 +233,25 @@ interface IBaseStyling { } interface IBoxPlotStyling extends IBaseStyling { - // no extra styling options -} + /** + * separate color for the median line + * @default 'transparent' takes the current borderColor + * @scriptable + * @indexable + */ + medianColor: string; -interface IViolinStyling extends IBaseStyling { /** - * number of sample points of the underlying KDE for creating the violin plot - * @default 100 + * color the lower half (median-q3) of the box in a different color + * @default 'transparent' takes the current borderColor + * @scriptable + * @indexable */ - points: number; + lowerBackgroundColor: string; +} + +interface IViolinElementStyling extends IBaseStyling { + // no extras } ``` @@ -213,7 +259,7 @@ interface IViolinStyling extends IBaseStyling { Both types support that the data is given as an array of numbers `number[]`. The statistics will be automatically computed. In addition, specific summary data structures are supported: -```typescript +```ts interface IBaseItem { min: number; median: number; @@ -256,24 +302,36 @@ interface IViolinItem extends IBaseItem { ## Tooltips -In order to simplify the customization of the tooltips, -// TODO - -```js -arr = { - options: { - tooltips: { - callbacks: { - // TODO - }, - }, - }, -}; +In order to simplify the customization of the tooltips the tooltip item given to the tooltip callbacks was improved. The default `toString()` behavior should be fine in most cases. The tooltip item has the following structure: + +```ts +interface ITooltipItem { + label: string; // original + value: { + raw: IBoxPlotItem | IViolinItem; + /** + * in case an outlier is hovered this will contains its index + * @default -1 + */ + hoveredOutlierRadius: number; + /** + * toString function with a proper default implementation, which is used implicitly + */ + toString(): string; + + min: string; + median: string; + max: string; + items?: string[]; + + //... the formatted version of different attributes IBoxPlotItem or ViolinItem + }; +} ``` ### ESM and Tree Shaking -The ESM build of the library supports three shaking but having no side effects. As a consequence the chart.js library won't be automatically manipulated nor new controllers automatically registered. One has to manually import and register them. +The ESM build of the library supports tree shaking thus having no side effects. As a consequence the chart.js library won't be automatically manipulated nor new controllers automatically registered. One has to manually import and register them. ```js import Chart from 'chart.js'; @@ -282,6 +340,11 @@ import { BoxPlot } from '@sgratzl/chartjs-chart-boxplot'; // register controller in chart.js and ensure the defaults are set BoxPlot.register(); ... + +new Chart(ctx, { + type: BoxPlot.id, + data: [...], +}); ``` ## Development Environment diff --git a/src/elements/base.js b/src/elements/base.js index 3c8971b..add64f9 100644 --- a/src/elements/base.js +++ b/src/elements/base.js @@ -9,7 +9,6 @@ export const baseDefaults = Object.assign({}, defaults.elements.rectangle, { outlierBackgroundColor: defaults.elements.rectangle.backgroundColor, outlierBorderColor: defaults.elements.rectangle.borderColor, outlierBorderWidth: 1, - outlierHitRadius: 4, itemStyle: 'circle', itemRadius: 0, @@ -18,6 +17,7 @@ export const baseDefaults = Object.assign({}, defaults.elements.rectangle, { itemBorderWidth: 0, hitPadding: 2, + outlierHitRadius: 4, }); export const baseOptionKeys = [ @@ -55,7 +55,6 @@ export class StatsBase extends Element { ctx.lineWith = options.itemBorderWidth; // jitter based on random data // use the dataset index and index to initialize the random number generator - // TODO const random = rnd(this._datasetIndex * 1000 + this._index); const pointOptions = { diff --git a/src/elements/boxandwhiskers.js b/src/elements/boxandwhiskers.js index 3280b76..0c15c72 100644 --- a/src/elements/boxandwhiskers.js +++ b/src/elements/boxandwhiskers.js @@ -1,7 +1,7 @@ import { defaults } from 'chart.js'; import { StatsBase, baseDefaults, baseOptionKeys } from './base'; -export const boxOptionsKeys = baseOptionKeys.concat(['medianColor', 'lowerColor']); +export const boxOptionsKeys = baseOptionKeys.concat(['medianColor', 'lowerBackgroundColor']); export class BoxAndWiskers extends StatsBase { draw(ctx) { @@ -198,7 +198,7 @@ export class BoxAndWiskers extends StatsBase { } } -BoxAndWiskers._type = 'boxAndWhiskers'; +BoxAndWiskers._type = 'boxandwhiskers'; BoxAndWiskers.register = () => { defaults.set('elements', { [BoxAndWiskers._type]: Object.assign({}, baseDefaults, { diff --git a/src/elements/violin.js b/src/elements/violin.js index 16614de..9f0cf30 100644 --- a/src/elements/violin.js +++ b/src/elements/violin.js @@ -102,9 +102,7 @@ export class ViolinElement extends StatsBase { ViolinElement._type = 'violin'; ViolinElement.register = () => { defaults.set('elements', { - [ViolinElement._type]: Object.assign({}, baseDefaults, { - points: 100, - }), + [ViolinElement._type]: baseDefaults, }); return ViolinElement; };