Skip to content

document box #1394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1185,7 +1185,7 @@ The box mark is a composite mark consisting of four marks:

* a [rule](#rule) representing the extreme values (not including outliers)
* a [bar](#bar) representing the interquartile range (trimmed to the data)
* a [tick](#tick) represent the median value, and
* a [tick](#tick) representing the median value, and
* a [dot](#dot) representing outliers, if any

The given *options* are passed through to these underlying marks, with the exception of the following options:
Expand Down
40 changes: 40 additions & 0 deletions src/marks/box.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,50 @@ import type {DotOptions} from "./dot.js";
import type {RuleXOptions, RuleYOptions} from "./rule.js";
import type {TickXOptions, TickYOptions} from "./tick.js";

/** Options for the boxX mark. */
export type BoxXOptions = DotOptions & BarXOptions & TickXOptions & RuleXOptions;

/** Options for the boxY mark. */
export type BoxYOptions = DotOptions & BarYOptions & TickYOptions & RuleYOptions;

/**
* Returns a box mark that draws horizontal boxplots where **x** is quantitative
* or temporal and **y**, if present, is ordinal. The box mark is a compound
* mark consisting of four marks:
*
* - a rule representing the extreme values (not including outliers),
* - a bar representing the interquartile range (trimmed to the data),
* - a tick representing the median value, and
* - a dot representing outliers, if any.
*
* The given *options* are passed through to these underlying marks, with the
* exception of the following options:
*
* - **fill** - the fill color of the bar; defaults to gray
* - **fillOpacity** - the fill opacity of the bar; defaults to 1
* - **stroke** - the stroke color of the rule, tick, and dot; defaults to *currentColor*
* - **strokeOpacity** - the stroke opacity of the rule, tick, and dot; defaults to 1
* - **strokeWidth** - the stroke width of the tick; defaults to 2
*/
export function boxX(data?: Data, options?: BoxXOptions): CompoundMark;

/**
* Returns a box mark that draws vertical boxplots where **y** is quantitative
* or temporal and **x**, if present, is ordinal. The box mark is a compound
* mark consisting of four marks:
*
* - a rule representing the extreme values (not including outliers),
* - a bar representing the interquartile range (trimmed to the data),
* - a tick representing the median value, and
* - a dot representing outliers, if any.
*
* The given *options* are passed through to these underlying marks, with the
* exception of the following options:
*
* - **fill** - the fill color of the bar; defaults to gray
* - **fillOpacity** - the fill opacity of the bar; defaults to 1
* - **stroke** - the stroke color of the rule, tick, and dot; defaults to *currentColor*
* - **strokeOpacity** - the stroke opacity of the rule, tick, and dot; defaults to 1
* - **strokeWidth** - the stroke width of the tick; defaults to 2
*/
export function boxY(data?: Data, options?: BoxYOptions): CompoundMark;
23 changes: 12 additions & 11 deletions src/marks/box.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {min, max, quantile} from "d3";
import {max, min, quantile} from "d3";
import {marks} from "../mark.js";
import {identity} from "../options.js";
import {groupX, groupY, groupZ} from "../transforms/group.js";
import {map} from "../transforms/map.js";
import {barX, barY} from "./bar.js";
Expand All @@ -11,7 +12,7 @@ export function boxX(data, options = {}) {
// Returns a composite mark for producing a horizontal box plot, applying the
// necessary statistical transforms. The boxes are grouped by y, if present.
const {
x = {transform: (x) => x},
x = identity,
y = null,
fill = "#ccc",
fillOpacity,
Expand All @@ -34,7 +35,7 @@ export function boxY(data, options = {}) {
// Returns a composite mark for producing a vertical box plot, applying the
// necessary statistical transforms. The boxes are grouped by x, if present.
const {
y = {transform: (y) => y},
y = identity,
x = null,
fill = "#ccc",
fillOpacity,
Expand All @@ -60,20 +61,20 @@ function oqr(values) {
return values.map((v) => (v < r1 || v > r2 ? v : NaN));
}

function loqr1(values, value) {
const lo = quartile1(values, value) * 2.5 - quartile3(values, value) * 1.5;
function loqr1(values) {
const lo = quartile1(values) * 2.5 - quartile3(values) * 1.5;
return min(values, (d) => (d >= lo ? d : NaN));
}

function hiqr2(values, value) {
const hi = quartile3(values, value) * 2.5 - quartile1(values, value) * 1.5;
function hiqr2(values) {
const hi = quartile3(values) * 2.5 - quartile1(values) * 1.5;
return max(values, (d) => (d <= hi ? d : NaN));
}

function quartile1(values, value) {
return quantile(values, 0.25, value);
function quartile1(values) {
return quantile(values, 0.25);
}

function quartile3(values, value) {
return quantile(values, 0.75, value);
function quartile3(values) {
return quantile(values, 0.75);
}