From 5dc70d6bfa05d77a51cfdcb0a4525d427206bc4c Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Thu, 28 Jul 2022 10:57:32 -0400 Subject: [PATCH 01/59] Add script to inject JSDoc into README.md --- .github/workflows/node.js.yml | 1 + README.md | 22 +++---- package.json | 4 ++ scripts/jsdoc-to-readme.ts | 113 ++++++++++++++++++++++++++++++++++ src/options.js | 26 ++++++-- src/transforms/basic.js | 16 +++-- yarn.lock | 63 +++++++++++++++++-- 7 files changed, 219 insertions(+), 26 deletions(-) create mode 100644 scripts/jsdoc-to-readme.ts diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index f91c7ca6e7..3fa0cc8c1a 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -27,3 +27,4 @@ jobs: echo ::add-matcher::.github/eslint.json yarn run eslint . --format=compact - run: yarn test + - run: yarn readme:check diff --git a/README.md b/README.md index 9a67308a11..00d59631ea 100644 --- a/README.md +++ b/README.md @@ -2264,30 +2264,28 @@ While transform functions often produce new *data* or *facets*, they may return Plot provides a few helpers for implementing transforms. -#### Plot.valueof(*data*, *value*, *type*) + -Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *type* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: +#### Plot.valueof(*data*, *value*, *arrayType*) + +Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *arrayType* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: * a string - corresponding to the field accessor (`d => d[value]`) -* an accessor function - called as *type*.from(*data*, *value*) +* an accessor function - called as *arrayType*.from(*data*, *value*) * a number, Date, or boolean — resulting in an array uniformly filled with the *value* * an object with a transform method — called as *value*.transform(*data*) * an array of values - returning the same * null or undefined - returning the same -If *type* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). - -Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *type*, the array may be returned as-is without making a copy. - -#### Plot.transform(*options*, *transform*) +If *arrayType* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). -Given an *options* object that may specify some basic transforms (*filter*, *sort*, or *reverse*) or a custom *transform* function, composes those transforms if any with the given *transform* function, returning a new *options* object. If a custom *transform* function is present on the given *options*, any basic transforms are ignored. Any additional input *options* are passed through in the returned *options* object. This method facilitates applying the basic transforms prior to applying the given custom *transform* and is used internally by Plot’s built-in transforms. +Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *arrayType*, the array may be returned as-is without making a copy. -#### Plot.column([*source*]) + -This helper for constructing derived columns returns a [*column*, *setColumn*] array. The *column* object implements *column*.transform, returning whatever value was most recently passed to *setColumn*. If *setColumn* is not called, then *column*.transform returns undefined. If a *source* is specified, then *column*.label exposes the given *source*’s label, if any: if *source* is a string as when representing a named field of data, then *column*.label is *source*; otherwise *column*.label propagates *source*.label. This allows derived columns to propagate a human-readable axis or legend label. + -Plot.column is typically used by options transforms to define new channels; the associated columns are populated (derived) when the **transform** option function is invoked. + ## Initializers diff --git a/package.json b/package.json index dc570bb8de..02aa083694 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ "test:mocha": "mkdir -p test/output && mocha --conditions=mocha 'test/**/*-test.*' 'test/plot.js'", "test:lint": "eslint src test", "test:typecheck": "tsc --noEmit", + "readme:check": "tsx scripts/jsdoc-to-readme.ts --check", + "readme:update": "tsx scripts/jsdoc-to-readme.ts", "prepublishOnly": "rm -rf dist && rollup -c && tsc", "postpublish": "git push && git push --tags", "dev": "vite" @@ -63,7 +65,9 @@ "prettier": "^2.7.1", "rollup": "2", "rollup-plugin-terser": "7", + "ts-morph": "^15.1.0", "tslib": "^2.4.0", + "tsx": "^3.8.0", "typescript": "^4.6.4", "typescript-module-alias": "^1.0.2", "vite": "3" diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts new file mode 100644 index 0000000000..67e0b7007d --- /dev/null +++ b/scripts/jsdoc-to-readme.ts @@ -0,0 +1,113 @@ +import {readFileSync, writeFileSync} from "fs"; +import type {ExportedDeclarations, FunctionDeclaration} from "ts-morph"; +import {Project} from "ts-morph"; + +/** + * This script will find html comments in the README of the below shape and + * inject the corresponding JSDoc from that exported symbol. + * + * + * + */ + +const readmePath = "README.md"; +let indexPath = "src/index.js"; +const project = new Project({tsConfigFilePath: "tsconfig.json"}); + +let index = project.getSourceFile(indexPath); +if (!index) { + indexPath = "src/index.ts"; + index = project.getSourceFile(indexPath); + if (!index) throw new Error(`index file not found in src/`); +} + +const exported = index.getExportedDeclarations(); +function getByApiName(name: string) { + for (const [exportedName, declarations] of exported) { + if (name === exportedName) { + return declarations[0]; + } + } +} + +function injectJsDoc(readme: string) { + const lines = readme.split("\n"); + const output: string[] = []; + let insideReplacement = false; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + let replacement = ""; + let isReplacementDelimiter = false; + if (line.startsWith(" +#### Plot.transform(*options*, *transform*) + +Given an *options* object that may specify some basic transforms (*filter*, *sort*, or *reverse*) or a custom *transform* function, composes those transforms if any with the given *transform* function, returning a new *options* object. If a custom *transform* function is present on the given *options*, any basic transforms are ignored. Any additional input *options* are passed through in the returned *options* object. This method facilitates applying the basic transforms prior to applying the given custom *transform* and is used internally by Plot’s built-in transforms. + +#### Plot.column(*source*) + +This helper for constructing derived columns returns a [*column*, *setColumn*] array. The *column* object implements *column*.transform, returning whatever value was most recently passed to *setColumn*. If *setColumn* is not called, then *column*.transform returns undefined. If a *source* is specified, then *column*.label exposes the given *source*’s label, if any: if *source* is a string as when representing a named field of data, then *column*.label is *source*; otherwise *column*.label propagates *source*.label. This allows derived columns to propagate a human-readable axis or legend label. + +Plot.column is typically used by options transforms to define new channels; the associated columns are populated (derived) when the **transform** option function is invoked. + ## Initializers From d98fab496452beee6e383788a9895d6becbc840b Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Wed, 3 Aug 2022 22:42:51 -0400 Subject: [PATCH 03/59] emit declaration files to types/ --- .gitignore | 1 + src/format.ts | 4 ++-- tsconfig.json | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a4682aa5a8..8df33ba803 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store dist/ +types/ node_modules/ test/output/*-changed.svg test/output/*-changed.html diff --git a/src/format.ts b/src/format.ts index a50eb3d8e0..1be9b61482 100644 --- a/src/format.ts +++ b/src/format.ts @@ -8,11 +8,11 @@ const numberFormat = memoize1( ); const monthFormat = memoize1( (locale: string | string[] | undefined, month: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined) => - new Intl.DateTimeFormat(locale, {timeZone: "UTC", month}) + new Intl.DateTimeFormat(locale, {timeZone: "UTC", ...(month && {month})}) ); const weekdayFormat = memoize1( (locale: string | string[] | undefined, weekday: "long" | "short" | "narrow" | undefined) => - new Intl.DateTimeFormat(locale, {timeZone: "UTC", weekday}) + new Intl.DateTimeFormat(locale, {timeZone: "UTC", ...(weekday && {weekday})}) ); export function formatNumber(locale = "en-US"): (value: any) => string | undefined { diff --git a/tsconfig.json b/tsconfig.json index e3bf89a670..f7a24bffa7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,8 @@ "lib": ["esnext", "dom"], "strict": true, "stripInternal": true, + "declaration": true, + "declarationDir": "types", "outDir": "dist", "allowJs": true, "resolveJsonModule": true, From 119e0efeb16e41d2977aee82da20a32339cffbf7 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 14:03:57 -0400 Subject: [PATCH 04/59] jsdoc-to-readme - allow specifiying the title prefix --- README.md | 4 + scripts/jsdoc-to-readme.ts | 16 ++-- src/plot.js | 167 +++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ef361e0fc7..1f0fc41a4d 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ document.body.append(Plot.plot(options)); See also our [Plot + React example](https://github.com/observablehq/plot-create-react-app-example/blob/main/src/App.js). + + ## Plot.plot(*options*) Renders a new plot given the specified *options* and returns the corresponding SVG or HTML figure element. All *options* are optional. @@ -228,6 +230,8 @@ const color = plot.scale("color"); // retrieve the color scale object console.log(color.range); // inspect the color scale’s range, ["red", "blue"] ``` + + #### Plot.scale(*options*) You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts index 67e0b7007d..ef92da9056 100644 --- a/scripts/jsdoc-to-readme.ts +++ b/scripts/jsdoc-to-readme.ts @@ -46,12 +46,12 @@ function injectJsDoc(readme: string) { isReplacementDelimiter = true; insideReplacement = true; const parts = [""]; - const match = line.match(/jsdoc\s+(.+)\s/); + const match = line.match(/jsdoc\s+(#+)?\s?(.+)\s/); if (!match || match.length < 2) throw new Error(`Malformed jsdoc comment in README.md on line ${i}.`); - const [, name] = match; + const [, prefix, name] = match; const declaration = getByApiName(name); if (!declaration) throw new Error(`${name} is not exported by src/index`); - parts.push(getJsDocs(name, declaration)); + parts.push(getJsDocs(name, declaration, prefix)); parts.push(""); replacement = parts.join("\n"); } @@ -61,12 +61,12 @@ function injectJsDoc(readme: string) { return output.join("\n"); } -function getJsDocs(name: string, declaration: ExportedDeclarations) { +function getJsDocs(name: string, declaration: ExportedDeclarations, prefix = "####") { if ("getParameters" in declaration) { - return getJsDocsForFunction(name, declaration); + return getJsDocsForFunction(name, declaration, prefix); } if ("getJsDocs" in declaration) { - return `#### Plot.${name}\n${declaration + return `${prefix} Plot.${name}\n${declaration .getJsDocs() .map((doc) => doc.getDescription()) .join("\n\n")}`; @@ -74,9 +74,9 @@ function getJsDocs(name: string, declaration: ExportedDeclarations) { return `JSDoc extraction for ${declaration.getKindName()} not yet implemented.`; } -function getJsDocsForFunction(name: string, declaration: FunctionDeclaration) { +function getJsDocsForFunction(name: string, declaration: FunctionDeclaration, prefix = "####") { const parameters = declaration.getParameters(); - const title = `#### Plot.${name}(${parameters.map((param) => `*${param.getName()}*`).join(", ")})`; + const title = `${prefix} Plot.${name}(${parameters.map((param) => `*${param.getName()}*`).join(", ")})`; const parts = [title]; const docs = declaration.getJsDocs(); if (docs.length) { diff --git a/src/plot.js b/src/plot.js index a9a27a7b20..3d0411bfed 100644 --- a/src/plot.js +++ b/src/plot.js @@ -24,6 +24,173 @@ import {basic, initializer} from "./transforms/basic.js"; import {maybeInterval} from "./transforms/interval.js"; import {consumeWarnings} from "./warnings.js"; +/** + * Renders a new plot given the specified *options* and returns the corresponding SVG or HTML figure element. All *options* are optional. + * + * ### Mark options + * + * The **marks** option specifies an array of [marks](#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](#bar) or [dot](#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). + * + * ```js + * Plot.plot({ + * marks: [ + * Plot.barY(alphabet, {x: "letter", y: "frequency", fill: "steelblue"}), + * Plot.ruleY([0]) + * ] + * }) + * ``` + * + * ### Layout options + * + * These options determine the overall layout of the plot; all are specified as numbers in pixels: + * + * * **marginTop** - the top margin + * * **marginRight** - the right margin + * * **marginBottom** - the bottom margin + * * **marginLeft** - the left margin + * * **margin** - shorthand for the four margins + * * **width** - the outer width of the plot (including margins) + * * **height** - the outer height of the plot (including margins) + * + * The default **width** is 640. On Observable, the width can be set to the [standard width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to make responsive plots. The default **height** is chosen automatically based on the plot’s associated scales; for example, if *y* is linear and there is no *fy* scale, it might be 396. + * + * The default margins depend on the plot’s axes: for example, **marginTop** and **marginBottom** are at least 30 if there is a corresponding top or bottom *x* axis, and **marginLeft** and **marginRight** are at least 40 if there is a corresponding left or right *y* axis. For simplicity’s sake and for consistent layout across plots, margins are not automatically sized to make room for tick labels; instead, shorten your tick labels or increase the margins as needed. (In the future, margins may be specified indirectly via a scale property to make it easier to reorient axes without adjusting margins; see [#210](https://github.com/observablehq/plot/issues/210).) + * + * The **style** option allows custom styles to override Plot’s defaults. It may be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in the same fashion as assigning [*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as assigning [*element*.style properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). Note that unitless numbers ([quirky lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such as `{padding: 20}` may not supported by some browsers; you should instead specify a string with units such as `{padding: "20px"}`. By default, the returned plot has a white background, a max-width of 100%, and the system-ui font. Plot’s marks and axes default to [currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), meaning that they will inherit the surrounding content’s color. For example, a dark theme: + * + * ```js + * Plot.plot({ + * marks: …, + * style: { + * background: "black", + * color: "white" + * } + * }) + * ``` + * + * If a **caption** is specified, Plot.plot wraps the generated SVG element in an HTML figure element with a figcaption, returning the figure. To specify an HTML caption, consider using the [`html` tagged template literal](http://github.com/observablehq/htl); otherwise, the specified string represents text that will be escaped as needed. + * + * ```js + * Plot.plot({ + * marks: …, + * caption: html`Figure 1. This chart has a fancy caption.` + * }) + * ``` + * + * The generated SVG element has a random class name which applies a default stylesheet. Use the top-level **className** option to specify that class name. + * + * The **document** option specifies the [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to create plot elements. It defaults to window.document, but can be changed to another document, say when using a virtual DOM library for server-side rendering in Node. + * + * ### Scale options + * + * Plot passes data through [scales](https://observablehq.com/@observablehq/plot-scales) as needed before rendering marks. A scale maps abstract values such as time or temperature to visual values such as position or color. Within a given plot, marks share scales. For example, if a plot has two Plot.line marks, both share the same *x* and *y* scales for a consistent representation of data. (Plot does not currently support dual-axis charts, which are [not advised](https://blog.datawrapper.de/dualaxis/).) + * + * ```js + * Plot.plot({ + * marks: [ + * Plot.line(aapl, {x: "Date", y: "Close"}), + * Plot.line(goog, {x: "Date", y: "Close"}) + * ] + * }) + * ``` + * + * Each scale’s options are specified as a nested options object with the corresponding scale name within the top-level plot *options*: + * + * * **x** - horizontal position + * * **y** - vertical position + * * **r** - radius (size) + * * **color** - fill or stroke + * * **opacity** - fill or stroke opacity + * * **length** - linear length (for [vectors](#vector)) + * * **symbol** - categorical symbol (for [dots](#dot)) + * + * For example, to set the domain for the *x* and *y* scales: + * + * ```js + * Plot.plot({ + * x: { + * domain: [new Date("1880-01-01"), new Date("2016-11-01")] + * }, + * y: { + * domain: [-0.78, 1.35] + * } + * }) + * ``` + * + * Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](#position-options) or [color](#color-options). + * + * You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. + * + * For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: + * + * * *linear* (default) - linear transform (translate and scale) + * * *pow* - power (exponential) transform + * * *sqrt* - square-root transform (*pow* transform with exponent = 0.5) + * * *log* - logarithmic transform + * * *symlog* - bi-symmetric logarithmic transform per [Webber *et al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) + * + * The appropriate transform depends on the data’s distribution and what you wish to know. A *sqrt* transform exaggerates differences between small values at the expense of large values; it is a special case of the *pow* transform which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* transform is suitable for comparing orders of magnitude and can only be used when the domain does not include zero. The base defaults to 10 and can be specified with the *scale*.**base** option; note that this only affects the axis ticks and not the scale’s behavior. A *symlog* transform is more elaborate, but works well with wide-range values that include zero; it can be configured with the *scale*.**constant** option (default 1). + * + * For temporal data (*i.e.* dates), two variants of a *linear* scale are also supported: + * + * * *utc* (default, recommended) - UTC time + * * *time* - local time + * + * UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. + * + * For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) + * + * You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. + * + * Quantitative scales, as well as identity position scales, coerce channel values to numbers; both null and undefined are coerced to NaN. Similarly, time scales coerce channel values to dates; numbers are assumed to be milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). + * + * A scale’s domain (the extent of its inputs, abstract values) and range (the extent of its outputs, visual values) are typically inferred automatically. You can set them explicitly using these options: + * + * * *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or categorical values + * * *scale*.**range** - typically [*min*, *max*], or an array of ordinal or categorical values + * * *scale*.**unknown** - the desired output value (defaults to undefined) for invalid input values + * * *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* + * * *scale*.**interval** - an interval or time interval (for interval data; see below) + * + * For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. + * + * The default range depends on the scale: for [position scales](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. + * + * The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. + * + * For data at regular intervals, such as integer values or daily samples, the *scale*.**interval** option can be used to enforce uniformity. The specified *interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), *interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. The option can also be specified as a number, in which case it will be promoted to a numeric interval with the given step. This option sets the default *scale*.transform to the given interval’s *interval*.floor function. In addition, the default *scale*.domain is an array of uniformly-spaced values spanning the extent of the values associated with the scale. + * + * Quantitative scales can be further customized with additional options: + * + * * *scale*.**clamp** - if true, clamp input values to the scale’s domain + * * *scale*.**nice** - if true (or a tick count), extend the domain to nice round values + * * *scale*.**zero** - if true, extend the domain to include zero if needed + * * *scale*.**percent** - if true, transform proportions in [0, 1] to percentages in [0, 100] + * + * Clamping is typically used in conjunction with setting an explicit domain since if the domain is inferred, no values will be outside the domain. Clamping is useful for focusing on a subset of the data while ensuring that extreme values remain visible, but use caution: clamped values may need an annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and **zero** options are supported as shorthand for setting the respective option on all scales. + * + * The *scale*.**transform** option allows you to apply a function to all values before they are passed through the scale. This is convenient for transforming a scale’s data, say to convert to thousands or between temperature units. + * + * ```js + * Plot.plot({ + * y: { + * label: "↑ Temperature (°F)", + * transform: f => f * 9 / 5 + 32 // convert Celsius to Fahrenheit + * }, + * marks: … + * }) + * ``` + * + * #### *plot*.scale(*scaleName*) + * + * Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) function of a returned plot. The *scaleName* must be one of the known scale names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, `"symbol"`, or `"length"`. If the associated *plot* has no scale with the given *scaleName*, returns undefined. + * + * ```js + * const plot = Plot.plot(…); // render a plot + * const color = plot.scale("color"); // retrieve the color scale object + * console.log(color.range); // inspect the color scale’s range, ["red", "blue"] + * ``` + */ export function plot(options = {}) { const {facet, style, caption, ariaLabel, ariaDescription} = options; From cd18fa3159781b639399f1de8c191c1b98370c0f Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 14:12:00 -0400 Subject: [PATCH 05/59] use full urls for readme links --- README.md | 16 ++++++++-------- src/plot.js | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1f0fc41a4d..d3829e9037 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Renders a new plot given the specified *options* and returns the corresponding S ### Mark options -The **marks** option specifies an array of [marks](#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](#bar) or [dot](#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). +The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). ```js Plot.plot({ @@ -140,8 +140,8 @@ Each scale’s options are specified as a nested options object with the corresp * **r** - radius (size) * **color** - fill or stroke * **opacity** - fill or stroke opacity -* **length** - linear length (for [vectors](#vector)) -* **symbol** - categorical symbol (for [dots](#dot)) +* **length** - linear length (for [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) +* **symbol** - categorical symbol (for [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) For example, to set the domain for the *x* and *y* scales: @@ -156,9 +156,9 @@ Plot.plot({ }) ``` -Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](#position-options) or [color](#color-options). +Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) or [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). -You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. +You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: @@ -177,7 +177,7 @@ For temporal data (*i.e.* dates), two variants of a *linear* scale are also supp UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. -For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) +For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](https://github.com/observablehq/plot/blob/main/README.md#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. @@ -191,9 +191,9 @@ A scale’s domain (the extent of its inputs, abstract values) and range (the ex * *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* * *scale*.**interval** - an interval or time interval (for interval data; see below) -For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. +For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. -The default range depends on the scale: for [position scales](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. +The default range depends on the scale: for [position scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). For [color scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. diff --git a/src/plot.js b/src/plot.js index 3d0411bfed..9bcb8025d1 100644 --- a/src/plot.js +++ b/src/plot.js @@ -29,7 +29,7 @@ import {consumeWarnings} from "./warnings.js"; * * ### Mark options * - * The **marks** option specifies an array of [marks](#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](#bar) or [dot](#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). + * The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). * * ```js * Plot.plot({ @@ -101,8 +101,8 @@ import {consumeWarnings} from "./warnings.js"; * * **r** - radius (size) * * **color** - fill or stroke * * **opacity** - fill or stroke opacity - * * **length** - linear length (for [vectors](#vector)) - * * **symbol** - categorical symbol (for [dots](#dot)) + * * **length** - linear length (for [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) + * * **symbol** - categorical symbol (for [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) * * For example, to set the domain for the *x* and *y* scales: * @@ -117,9 +117,9 @@ import {consumeWarnings} from "./warnings.js"; * }) * ``` * - * Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](#position-options) or [color](#color-options). + * Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) or [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). * - * You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. + * You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. * * For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: * @@ -138,7 +138,7 @@ import {consumeWarnings} from "./warnings.js"; * * UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. * - * For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) + * For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](https://github.com/observablehq/plot/blob/main/README.md#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) * * You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. * @@ -152,9 +152,9 @@ import {consumeWarnings} from "./warnings.js"; * * *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* * * *scale*.**interval** - an interval or time interval (for interval data; see below) * - * For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. + * For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. * - * The default range depends on the scale: for [position scales](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. + * The default range depends on the scale: for [position scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). For [color scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. * * The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. * From 9668a228a61b9db21b50c419f0b3378da52d1ce5 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 14:42:00 -0400 Subject: [PATCH 06/59] JSDoc for scales --- README.md | 22 +-- src/scales.js | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 390 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d3829e9037..00361b7f3f 100644 --- a/README.md +++ b/README.md @@ -230,11 +230,11 @@ const color = plot.scale("color"); // retrieve the color scale object console.log(color.range); // inspect the color scale’s range, ["red", "blue"] ``` - + #### Plot.scale(*options*) -You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: ```js const color = Plot.scale({color: {type: "linear"}}); @@ -242,7 +242,7 @@ const color = Plot.scale({color: {type: "linear"}}); #### Scale objects -Both [*plot*.scale](#plotscalescalename) and [Plot.scale](#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. +Both [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) and [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. To reuse a scale across plots, pass the corresponding scale object into another plot specification: @@ -294,7 +294,7 @@ Plot automatically generates axes for position scales. You can configure these a * *scale*.**ticks** - the approximate number of ticks to generate * *scale*.**tickSize** - the size of each tick (in pixels; default 6) * *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) -* *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](#formats) +* *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) * *scale*.**grid** - if true, draw grid lines across the plot for each tick * *scale*.**line** - if true, draw the axis line @@ -305,7 +305,7 @@ Plot automatically generates axes for position scales. You can configure these a * *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree * *scale*.**ariaDescription** - a textual description for the axis -Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. +Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. ### Color options @@ -439,7 +439,7 @@ If an ordinal scale’s domain is not set, it defaults to natural ascending orde Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) ``` -The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](#transforms) (such as [stack](#stack) or [bin](#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](#area), [bar](#bar), and [rect](#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. +The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: @@ -447,7 +447,7 @@ Note that there may be multiple associated values in the secondary dimension for Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) ``` -Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](#group) for the list of supported reducers. +Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. For descending rather than ascending order, use the *reverse* option: @@ -455,7 +455,7 @@ For descending rather than ascending order, use the *reverse* option: Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) ``` -An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) +An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) @@ -469,7 +469,7 @@ Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. -Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](#transforms). To use both sort options and a sort transform, use [Plot.sort](#plotsortorder-options). +Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). ### Facet options @@ -478,7 +478,7 @@ The *facet* option enables [faceting](https://observablehq.com/@observablehq/plo * **fx** - the horizontal position, a *band* scale * **fy** - the vertical position, a *band* scale -Similar to [marks](#marks), faceting requires specifying data and at least one of two optional channels: +Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: * facet.**data** - the data to be faceted * facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* @@ -611,6 +611,8 @@ Continuous color legends are rendered as a ramp, and can be configured with the The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](#layout-options). + + #### Plot.legend(*options*) Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: diff --git a/src/scales.js b/src/scales.js index e64c3730e9..acda64ab30 100644 --- a/src/scales.js +++ b/src/scales.js @@ -471,6 +471,384 @@ export function coerceDate(x) { : new Date(x); } +/** + * You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: + * + * ```js + * const color = Plot.scale({color: {type: "linear"}}); + * ``` + * + * #### Scale objects + * + * Both [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) and [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. + * + * To reuse a scale across plots, pass the corresponding scale object into another plot specification: + * + * ```js + * const plot1 = Plot.plot(…); + * const plot2 = Plot.plot({…, color: plot1.scale("color")}); + * ``` + * + * For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. + * + * ### Position options + * + * The position scales (*x*, *y*, *fx*, and *fy*) support additional options: + * + * * *scale*.**inset** - inset the default range by the specified amount in pixels + * * *scale*.**round** - round the output value to the nearest integer (whole pixel) + * + * The *x* and *fx* scales support asymmetric insets for more precision. Replace inset by: + * + * * *scale*.**insetLeft** - insets the start of the default range by the specified number of pixels + * * *scale*.**insetRight** - insets the end of the default range by the specified number of pixels + * + * Similarly, the *y* and *fy* scales support asymmetric insets with: + * + * * *scale*.**insetTop** - insets the top of the default range by the specified number of pixels + * * *scale*.**insetBottom** - insets the bottom of the default range by the specified number of pixels + * + * The inset scale options can provide “breathing room” to separate marks from axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels prevents dots from overlapping with the axes. The *scale*.round option is useful for crisp edges by rounding to the nearest pixel boundary. + * + * In addition to the generic *ordinal* scale type, which requires an explicit output range value for each input domain value, Plot supports special *point* and *band* scale types for encoding ordinal data as position. These scale types accept a [*min*, *max*] range similar to quantitative scales, and divide this continuous interval into discrete points or bands based on the number of distinct values in the domain (*i.e.*, the domain’s cardinality). If the associated marks have no effective width along the ordinal dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, say for a bar, use a *band* scale. In the image below, the top *x* scale is a *point* scale while the bottom *x* scale is a *band* scale; see [Plot: Scales](https://observablehq.com/@observablehq/plot-scales) for an interactive version. + * + * point and band scales + * + * Ordinal position scales support additional options, all specified as proportions in [0, 1]: + * + * * *scale*.**padding** - how much of the range to reserve to inset first and last point or band + * * *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 = at middle, 1 = at end) + * + * For a *band* scale, you can further fine-tune padding: + * + * * *scale*.**paddingInner** - how much of the range to reserve to separate adjacent bands + * * *scale*.**paddingOuter** - how much of the range to reserve to inset first and last band + * + * Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of available space reserved for separating bands), while point scale padding defaults to 0.5 (the gap between the first point and the edge is half the distance of the gap between points, and likewise for the gap between the last point and the opposite edge). Note that rounding and mark insets (e.g., for bars and rects) also affect separation between adjacent marks. + * + * Plot automatically generates axes for position scales. You can configure these axes with the following options: + * + * * *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or *right* for *y*; null to suppress + * * *scale*.**ticks** - the approximate number of ticks to generate + * * *scale*.**tickSize** - the size of each tick (in pixels; default 6) + * * *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) + * * *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) + * * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) + * * *scale*.**grid** - if true, draw grid lines across the plot for each tick + * * *scale*.**line** - if true, draw the axis line + * * *scale*.**label** - a string to label the axis + * * *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, *left*, or *center* + * * *scale*.**labelOffset** - the label position offset (in pixels; default 0, typically for facet axes) + * * *scale*.**fontVariant** - the font-variant attribute for axis ticks; defaults to tabular-nums for quantitative axes + * * *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree + * * *scale*.**ariaDescription** - a textual description for the axis + * + * Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. + * + * ### Color options + * + * The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordinal*—can be used to encode color. In addition, Plot supports special scale types for color: + * + * * *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* scheme + * * *sequential* - equivalent to *linear* + * * *cyclical* - equivalent to *linear*, but defaults to the *rainbow* scheme + * * *threshold* - encodes based on the specified discrete thresholds; defaults to the *rdylbu* scheme + * * *quantile* - encodes based on the computed quantile thresholds; defaults to the *rdylbu* scheme + * * *quantize* - transforms a continuous domain into quantized thresholds; defaults to the *rdylbu* scheme + * * *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* scheme + * * *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults to the *rdbu* scheme + * * *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* scheme + * * *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* scheme + * * *diverging-symlog* - like *symlog*, but with a pivot; defaults to the *rdbu* scheme + * + * For a *threshold* scale, the *domain* represents *n* (typically numeric) thresholds which will produce a *range* of *n* + 1 output colors; the *i*th color of the *range* applies to values that are smaller than the *i*th element of the domain and larger or equal to the *i* - 1th element of the domain. For a *quantile* scale, the *domain* represents all input values to the scale, and the *n* option specifies how many quantiles to compute from the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output range of *n* colors. For a *quantize* scale, the domain will be transformed into approximately *n* quantized values, where *n* is an option that defaults to 5. + * + * By default, all diverging color scales are symmetric around the pivot; set *symmetric* to false if you want to cover the whole extent on both sides. + * + * Color scales support two additional options: + * + * * *scale*.**scheme** - a named color scheme in lieu of a range, such as *reds* + * * *scale*.**interpolate** - in conjunction with a range, how to interpolate colors + * + * For quantile and quantize color scales, the *scale*.scheme option is used in conjunction with *scale*.**n**, which determines how many quantiles or quantized values to compute, and thus the number of elements in the scale’s range; it defaults to 5 (for quintiles in the case of a quantile scale). + * + * The following sequential scale schemes are supported for both quantitative and ordinal data: + * + * * blues *blues* + * * greens *greens* + * * greys *greys* + * * oranges *oranges* + * * purples *purples* + * * reds *reds* + * * bugn *bugn* + * * bupu *bupu* + * * gnbu *gnbu* + * * orrd *orrd* + * * pubu *pubu* + * * pubugn *pubugn* + * * purd *purd* + * * rdpu *rdpu* + * * ylgn *ylgn* + * * ylgnbu *ylgnbu* + * * ylorbr *ylorbr* + * * ylorrd *ylorrd* + * * cividis *cividis* + * * inferno *inferno* + * * magma *magma* + * * plasma *plasma* + * * viridis *viridis* + * * cubehelix *cubehelix* + * * turbo *turbo* + * * warm *warm* + * * cool *cool* + * + * The default color scheme, *turbo*, was chosen primarily to ensure high-contrast visibility. Color schemes such as *blues* make low-value marks difficult to see against a white background, for better or for worse. To use a subset of a continuous color scheme (or any single-argument *interpolate* function), set the *scale*.range property to the corresponding subset of [0, 1]; for example, to use the first half of the *rainbow* color scheme, use a range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to encode a quantitative value without hue, consider using *opacity* rather than *color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). + * + * The following diverging scale schemes are supported: + * + * * brbg *brbg* + * * prgn *prgn* + * * piyg *piyg* + * * puor *puor* + * * rdbu *rdbu* + * * rdgy *rdgy* + * * rdylbu *rdylbu* + * * rdylgn *rdylgn* + * * spectral *spectral* + * * burd *burd* + * * buylrd *buylrd* + * + * Picking a diverging color scheme name defaults the scale type to *diverging*; set the scale type to *linear* to treat the color scheme as sequential instead. Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*). + * + * The following cylical color schemes are supported: + * + * * rainbow *rainbow* + * * sinebow *sinebow* + * + * The following categorical color schemes are supported: + * + * * accent *accent* (8 colors) + * * category10 *category10* (10 colors) + * * dark2 *dark2* (8 colors) + * * paired *paired* (12 colors) + * * pastel1 *pastel1* (9 colors) + * * pastel2 *pastel2* (8 colors) + * * set1 *set1* (9 colors) + * * set2 *set2* (8 colors) + * * set3 *set3* (12 colors) + * * tableau10 *tableau10* (10 colors) + * + * The following color interpolators are supported: + * + * * *rgb* - RGB (red, green, blue) + * * *hsl* - HSL (hue, saturation, lightness) + * * *lab* - CIELAB (*a.k.a.* “Lab”) + * * *hcl* - CIELChab (*a.k.a.* “LCh” or “HCL”) + * + * For example, to use CIELChab: + * + * ```js + * Plot.plot({ + * color: { + * range: ["red", "blue"], + * interpolate: "hcl" + * }, + * marks: … + * }) + * ``` + * + * Or to use gamma-corrected RGB (via [d3-interpolate](https://github.com/d3/d3-interpolate)): + * + * ```js + * Plot.plot({ + * color: { + * range: ["red", "blue"], + * interpolate: d3.interpolateRgb.gamma(2.2) + * }, + * marks: … + * }) + * ``` + * + * ### Sort options + * + * If an ordinal scale’s domain is not set, it defaults to natural ascending order; to order the domain by associated values in another dimension, either compute the domain manually (consider [d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) or use an associated mark’s **sort** option. For example, to sort bars by ascending frequency rather than alphabetically by letter: + * + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) + * ``` + * + * The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. + * + * Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: + * + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) + * ``` + * + * Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. + * + * For descending rather than ascending order, use the *reverse* option: + * + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) + * ``` + * + * An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) + * + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) + * ``` + * + * If different sort options are needed for different ordinal scales, the channel name can be replaced with a *value* object with additional per-scale options. + * + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse: true}}}) + * ``` + * + * If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. + * + * Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). + * + * ### Facet options + * + * The *facet* option enables [faceting](https://observablehq.com/@observablehq/plot-facets). When faceting, two additional band scales may be configured: + * + * * **fx** - the horizontal position, a *band* scale + * * **fy** - the vertical position, a *band* scale + * + * Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: + * + * * facet.**data** - the data to be faceted + * * facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* + * * facet.**y** - the vertical position; bound to the *fy* scale, which must be *band* + * + * The facet.**x** and facet.**y** channels are strictly ordinal or categorical (*i.e.*, discrete); each distinct channel value defines a facet. Quantitative data must be manually discretized for faceting, say by rounding or binning. (Automatic binning for quantitative data may be added in the future; see [#14](https://github.com/observablehq/plot/issues/14).) + * + * The following *facet* constant options are also supported: + * + * * facet.**marginTop** - the top margin + * * facet.**marginRight** - the right margin + * * facet.**marginBottom** - the bottom margin + * * facet.**marginLeft** - the left margin + * * facet.**margin** - shorthand for the four margins + * * facet.**grid** - if true, draw grid lines for each facet + * * facet.**label** - if null, disable default facet axis labels + * + * Faceting can be explicitly enabled or disabled on a mark with the *facet* option, which accepts the following values: + * + * * *auto* (default) - equivalent to *include* when mark data is strictly equal to facet data; else null + * * *include* (or true) - draw the subset of the mark’s data in the current facet + * * *exclude* - draw the subset of the mark’s data *not* in the current facet + * * null (or false) - repeat this mark’s data across all facets (i.e., no faceting) + * + * ```js + * Plot.plot({ + * facet: { + * data: penguins, + * x: "sex" + * }, + * marks: [ + * Plot.frame(), // draws an outline around each facet + * Plot.dot(penguins, {x: "culmen_length_mm", y: "culmen_depth_mm", fill: "#eee", facet: "exclude"}), // draws excluded penguins on each facet + * Plot.dot(penguins, {x: "culmen_length_mm", y: "culmen_depth_mm"}) // draws only the current facet’s subset + * ] + * }) + * ``` + * + * When the *include* or *exclude* facet mode is chosen, the mark data must be parallel to the facet data: the mark data must have the same length and order as the facet data. If the data are not parallel, then the wrong data may be shown in each facet. The default *auto* therefore requires strict equality (`===`) for safety, and using the facet data as mark data is recommended when using the *exclude* facet mode. (To construct parallel data safely, consider using [*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on the facet data.) + * + * ## Legends + * + * Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: + * + * * *scale*.**legend** - if truthy, generate a legend for the given scale + * + * If the *scale*.**legend** option is true, the default legend will be produced for the scale; otherwise, the meaning of the *legend* option depends on the scale. For quantitative color scales, it defaults to *ramp* but may be set to *swatches* for a discrete scale (most commonly for *threshold* color scales); for ordinal color scales and symbol scales, only the *swatches* value is supported. + * + * For example, this scatterplot includes a swatches legend for the ordinal color scale: + * + * ```js + * Plot.plot({ + * color: { + * legend: true + * }, + * marks: [ + * Plot.dot(athletes, {x: "weight", y: "height", stroke: "sex"}) + * ] + * }) + * ``` + * + * Whereas this scatterplot would render a ramp legend for its diverging color scale: + * + * ```js + * Plot.plot({ + * color: { + * type: "diverging", + * legend: true + * }, + * marks: [ + * Plot.dot(gistemp, {x: "Date", y: "Anomaly", stroke: "Anomaly"}) + * ] + * }) + * ``` + * + * #### *plot*.legend(*scaleName*, *options*) + * + * Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: + * + * ```js + * myplot = Plot.plot(…) + * ``` + * ```js + * mylegend = myplot.legend("color") + * ``` + * + * Or, with additional *options*: + * + * ```js + * mylegend = myplot.legend("color", {width: 320}) + * ``` + * + * If there is no scale with the given *scaleName* on the given *plot*, then *plot*.legend will return undefined. + * + * Categorical and ordinal color legends are rendered as swatches, unless *options*.**legend** is set to *ramp*. The swatches can be configured with the following options: + * + * * *options*.**tickFormat** - a format function for the labels + * * *options*.**swatchSize** - the size of the swatch (if square) + * * *options*.**swatchWidth** - the swatches’ width + * * *options*.**swatchHeight** - the swatches’ height + * * *options*.**columns** - the number of swatches per row + * * *options*.**marginLeft** - the legend’s left margin + * * *options*.**className** - a class name, that defaults to a randomly generated string scoping the styles + * * *options*.**width** - the legend’s width (in pixels) + * + * Symbol legends are rendered as swatches and support the options above in addition to the following options: + * + * * *options*.**fill** - the symbol fill color + * * *options*.**fillOpacity** - the symbol fill opacity; defaults to 1 + * * *options*.**stroke** - the symbol stroke color + * * *options*.**strokeOpacity** - the symbol stroke opacity; defaults to 1 + * * *options*.**strokeWidth** - the symbol stroke width; defaults to 1.5 + * * *options*.**r** - the symbol radius; defaults to 4.5 pixels + * + * The **fill** and **stroke** symbol legend options can be specified as “color” to apply the color scale when the symbol scale is a redundant encoding. The **fill** defaults to none. The **stroke** defaults to currentColor if the fill is none, and to none otherwise. The **fill** and **stroke** options may also be inherited from the corresponding options on an associated dot mark. + * + * Continuous color legends are rendered as a ramp, and can be configured with the following options: + * + * * *options*.**label** - the scale’s label + * * *options*.**ticks** - the desired number of ticks, or an array of tick values + * * *options*.**tickFormat** - a format function for the legend’s ticks + * * *options*.**tickSize** - the tick size + * * *options*.**round** - if true (default), round tick positions to pixels + * * *options*.**width** - the legend’s width + * * *options*.**height** - the legend’s height + * * *options*.**marginTop** - the legend’s top margin + * * *options*.**marginRight** - the legend’s right margin + * * *options*.**marginBottom** - the legend’s bottom margin + * * *options*.**marginLeft** - the legend’s left margin + * + * The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). + */ export function scale(options = {}) { let scale; for (const key in options) { From 8a60c10d1d4c88577aff922139e9994ec642a614 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 14:45:08 -0400 Subject: [PATCH 07/59] jsdoc legend --- README.md | 12 +++++++----- src/legends.js | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 00361b7f3f..9e0980873a 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,7 @@ When the *include* or *exclude* facet mode is chosen, the mark data must be para ## Legends -Plot can generate legends for *color*, *opacity*, and *symbol* [scales](#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: +Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: * *scale*.**legend** - if truthy, generate a legend for the given scale @@ -556,7 +556,7 @@ Plot.plot({ #### *plot*.legend(*scaleName*, *options*) -Given an existing *plot* returned by [Plot.plot](#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: +Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: ```js myplot = Plot.plot(…) @@ -609,13 +609,13 @@ Continuous color legends are rendered as a ramp, and can be configured with the * *options*.**marginBottom** - the legend’s bottom margin * *options*.**marginLeft** - the legend’s left margin -The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](#layout-options). +The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). - + #### Plot.legend(*options*) -Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: ```js Plot.legend({color: {type: "linear"}}) @@ -632,6 +632,8 @@ Plot.legend({ }) ``` + + ## Marks [Marks](https://observablehq.com/@observablehq/plot-marks) visualize data as geometric shapes such as bars, dots, and lines. An single mark can generate multiple shapes: for example, passing a [Plot.barY](#plotbarydata-options) to [Plot.plot](#plotplotoptions) will produce a bar for each element in the associated data. Multiple marks can be layered into [plots](#plotplotoptions). diff --git a/src/legends.js b/src/legends.js index 9de4a21ae1..306a856bfd 100644 --- a/src/legends.js +++ b/src/legends.js @@ -11,6 +11,24 @@ const legendRegistry = new Map([ ["opacity", legendOpacity] ]); +/** + * Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: + * + * ```js + * Plot.legend({color: {type: "linear"}}) + * ``` + * + * The *options* object may also include any additional legend options described in the previous section. For example, to make the above legend slightly wider: + * + * ```js + * Plot.legend({ + * width: 320, + * color: { + * type: "linear" + * } + * }) + * ``` + */ export function legend(options = {}) { for (const [key, value] of legendRegistry) { const scale = options[key]; From 1a8b1b328b9c13f4c4b3d2de9f63c413c6fbdb33 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 14:50:27 -0400 Subject: [PATCH 08/59] fix link --- src/plot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot.js b/src/plot.js index 9bcb8025d1..96c4412c36 100644 --- a/src/plot.js +++ b/src/plot.js @@ -29,7 +29,7 @@ import {consumeWarnings} from "./warnings.js"; * * ### Mark options * - * The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). + * The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). * * ```js * Plot.plot({ From 38dd8ecaa662d6eb361dc46fb7dd7c4f09027460 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:00:57 -0400 Subject: [PATCH 09/59] jsdoc-to-readme - indicate in title when a param uses dot-dot-dot notation --- scripts/jsdoc-to-readme.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts index ef92da9056..d336bec673 100644 --- a/scripts/jsdoc-to-readme.ts +++ b/scripts/jsdoc-to-readme.ts @@ -76,7 +76,9 @@ function getJsDocs(name: string, declaration: ExportedDeclarations, prefix = "## function getJsDocsForFunction(name: string, declaration: FunctionDeclaration, prefix = "####") { const parameters = declaration.getParameters(); - const title = `${prefix} Plot.${name}(${parameters.map((param) => `*${param.getName()}*`).join(", ")})`; + const title = `${prefix} Plot.${name}(${parameters + .map((param) => `${param.getDotDotDotToken() ? "..." : ""}*${param.getName()}*`) + .join(", ")})`; const parts = [title]; const docs = declaration.getJsDocs(); if (docs.length) { From 8ac36fda4b238b7f7f710fcf5396ad1664c0d74c Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:01:23 -0400 Subject: [PATCH 10/59] jsdoc marks --- README.md | 8 ++++++-- src/plot.js | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e0980873a..3ea8378e20 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Renders a new plot given the specified *options* and returns the corresponding S ### Mark options -The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](./test/data/alphabet.csv). +The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). ```js Plot.plot({ @@ -765,9 +765,13 @@ If needed, you can pass additional *options* to *mark*.plot, which is equivalent Plot.barY(alphabet, {x: "letter", y: "frequency"}).plot({width: 1024}) ``` + + #### Plot.marks(...*marks*) -A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](./src/marks/box.js) for an example. +A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](.https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. + + ### Area diff --git a/src/plot.js b/src/plot.js index 96c4412c36..ef07cb43e6 100644 --- a/src/plot.js +++ b/src/plot.js @@ -516,6 +516,9 @@ export class Mark { } } +/** + * A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](.https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. + */ export function marks(...marks) { marks.plot = Mark.prototype.plot; return marks; From 658871a12d80dd2b0a284f5e4ff21ed8dfb181e4 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:02:17 -0400 Subject: [PATCH 11/59] fix link --- README.md | 2 +- src/plot.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ea8378e20..059b37c170 100644 --- a/README.md +++ b/README.md @@ -769,7 +769,7 @@ Plot.barY(alphabet, {x: "letter", y: "frequency"}).plot({width: 1024}) #### Plot.marks(...*marks*) -A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](.https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. +A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. diff --git a/src/plot.js b/src/plot.js index ef07cb43e6..726c387c62 100644 --- a/src/plot.js +++ b/src/plot.js @@ -517,7 +517,7 @@ export class Mark { } /** - * A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](.https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. + * A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. */ export function marks(...marks) { marks.plot = Mark.prototype.plot; From 42f2e617700a306046eb37f04af23418b09a698c Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:08:09 -0400 Subject: [PATCH 12/59] jsdoc area --- README.md | 6 +++++- src/marks/area.js | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 059b37c170..b3d0ecb54f 100644 --- a/README.md +++ b/README.md @@ -800,13 +800,17 @@ Points along the baseline and topline are connected in input order. Likewise, if The area mark supports [curve options](#curves) to control interpolation between points. If any of the *x1*, *y1*, *x2*, or *y2* values are invalid (undefined, null, or NaN), the baseline and topline will be interrupted, resulting in a break that divides the area shape into multiple segments. (See [d3-shape’s *area*.defined](https://github.com/d3/d3-shape/blob/main/README.md#area_defined) for more.) If an area segment consists of only a single point, it may appear invisible unless rendered with rounded or square line caps. In addition, some curves such as *cardinal-open* only render a visible segment if it contains multiple points. + + #### Plot.area(*data*, *options*) ```js Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) ``` -Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. +Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/d3/d3-shape/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/d3/d3-shape/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. + + #### Plot.areaX(*data*, *options*) diff --git a/src/marks/area.js b/src/marks/area.js index e29bbc0021..6a260c4092 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -71,6 +71,13 @@ export class Area extends Mark { } } +/** + * ```js + * Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) + * ``` + * + * Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/d3/d3-shape/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/d3/d3-shape/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. + */ export function area(data, options) { if (options === undefined) return areaY(data, {x: first, y: second}); return new Area(data, options); From 9a43100fb98ab59f0ba0a1add73f294d0c01ee7f Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:09:49 -0400 Subject: [PATCH 13/59] jsdoc areaX --- README.md | 8 +++++--- src/marks/area.js | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b3d0ecb54f..8afe27fcd4 100644 --- a/README.md +++ b/README.md @@ -810,7 +810,7 @@ Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/d3/d3-shape/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/d3/d3-shape/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. - + #### Plot.areaX(*data*, *options*) @@ -818,9 +818,9 @@ Returns a new area with the given *data* and *options*. Plot.area is rarely used Plot.areaX(aapl, {y: "Date", x: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. -If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -828,6 +828,8 @@ Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + + #### Plot.areaY(*data*, *options*) ```js diff --git a/src/marks/area.js b/src/marks/area.js index 6a260c4092..3c7d95d799 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -83,6 +83,21 @@ export function area(data, options) { return new Area(data, options); } +/** + * ```js + * Plot.areaX(aapl, {y: "Date", x: "Close"}) + * ``` + * + * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. + * + * If the **interval** option is specified, the [binY transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * + * ```js + * Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) + * ``` + * + * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + */ export function areaX(data, options) { const {y = indexOf, ...rest} = maybeDenseIntervalY(options); return new Area(data, maybeStackX(maybeIdentityX({...rest, y1: y, y2: undefined}))); From 0cc56ffa22c2b249bcf9e281f24fe8aad6d2d5de Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:12:12 -0400 Subject: [PATCH 14/59] jsdoc areaY --- README.md | 8 +++++--- src/marks/area.js | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8afe27fcd4..ed5a9d9754 100644 --- a/README.md +++ b/README.md @@ -828,7 +828,7 @@ Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. - + #### Plot.areaY(*data*, *options*) @@ -836,9 +836,9 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.areaY(aapl, {x: "Date", y: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. -If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) @@ -846,6 +846,8 @@ Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + + ### Arrow [a scatterplot with arrows](https://observablehq.com/@observablehq/plot-arrow) diff --git a/src/marks/area.js b/src/marks/area.js index 3c7d95d799..f5b090bdcd 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -103,6 +103,21 @@ export function areaX(data, options) { return new Area(data, maybeStackX(maybeIdentityX({...rest, y1: y, y2: undefined}))); } +/** + * ```js + * Plot.areaY(aapl, {x: "Date", y: "Close"}) + * ``` + * + * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. + * + * If the **interval** option is specified, the [binX transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * + * ```js + * Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) + * ``` + * + * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + */ export function areaY(data, options) { const {x = indexOf, ...rest} = maybeDenseIntervalX(options); return new Area(data, maybeStackY(maybeIdentityY({...rest, x1: x, x2: undefined}))); From 021d1f83f2119c76a4872b1daa1fd01008f662a3 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:14:58 -0400 Subject: [PATCH 15/59] jsdoc arrow --- README.md | 4 ++++ src/marks/arrow.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed5a9d9754..acf0ce4c1a 100644 --- a/README.md +++ b/README.md @@ -874,6 +874,8 @@ The arrow mark supports the [standard mark options](#marks). The **stroke** defa The **bend** option sets the angle between the straight line between the two points and the outgoing direction of the arrow from the start point. It must be within ±90°. A positive angle will produce a clockwise curve; a negative angle will produce a counterclockwise curve; zero will produce a straight line. The **headAngle** determines how pointy the arrowhead is; it is typically between 0° and 180°. The **headLength** determines the scale of the arrowhead relative to the stroke width. Assuming the default of stroke width 1.5px, the **headLength** is the length of the arrowhead’s side in pixels. + + #### Plot.arrow(*data*, *options*) ```js @@ -882,6 +884,8 @@ Plot.arrow(inequality, {x1: "POP_1980", y1: "R90_10_1980", x2: "POP_2015", y2: " Returns a new arrow with the given *data* and *options*. + + ### Bar [a bar chart](https://observablehq.com/@observablehq/plot-bar) diff --git a/src/marks/arrow.js b/src/marks/arrow.js index 6e801df20f..79fbefa392 100644 --- a/src/marks/arrow.js +++ b/src/marks/arrow.js @@ -176,8 +176,16 @@ function circleCircleIntersect([ax, ay, ar], [bx, by, br], sign) { return [ax + (dx * x + dy * y) / d, ay + (dy * x - dx * y) / d]; } -export function arrow(data, {x, x1, x2, y, y1, y2, ...options} = {}) { +/** + * ```js + * Plot.arrow(inequality, {x1: "POP_1980", y1: "R90_10_1980", x2: "POP_2015", y2: "R90_10_2015", bend: true}) + * ``` + * + * Returns a new arrow with the given *data* and *options*. + */ +export function arrow(data, options = {}) { + let {x, x1, x2, y, y1, y2, ...remainingOptions} = options; [x1, x2] = maybeSameValue(x, x1, x2); [y1, y2] = maybeSameValue(y, y1, y2); - return new Arrow(data, {...options, x1, x2, y1, y2}); + return new Arrow(data, {...remainingOptions, x1, x2, y1, y2}); } From 1c3137e091dbc19fdf10c49c90e625b5ad094a1f Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:17:20 -0400 Subject: [PATCH 16/59] jsdoc barX --- README.md | 8 ++++++-- src/marks/bar.js | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index acf0ce4c1a..8e9af0ac60 100644 --- a/README.md +++ b/README.md @@ -894,6 +894,8 @@ Returns a new arrow with the given *data* and *options*. For the required channels, see [Plot.barX](#plotbarxdata-options) and [Plot.barY](#plotbarydata-options). The bar mark supports the [standard mark options](#marks), including insets and rounded corners. The **stroke** defaults to none. The **fill** defaults to currentColor if the stroke is none, and to none otherwise. + + #### Plot.barX(*data*, *options*) ```js @@ -905,16 +907,18 @@ Returns a new horizontal bar↔︎ with the given *data* and *options*. The foll * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. +If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. -In addition to the [standard bar channels](#bar), the following optional channels are supported: +In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). + + #### Plot.barY(*data*, *options*) ```js diff --git a/src/marks/bar.js b/src/marks/bar.js index 99a56f4685..2dbb2c5abc 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -129,6 +129,26 @@ export class BarY extends AbstractBar { } } +/** + * ```js + * Plot.barX(alphabet, {y: "letter", x: "frequency"}) + * ``` + * + * Returns a new horizontal bar↔︎ with the given *data* and *options*. The following channels are required: + * + * * **x1** - the starting horizontal position; bound to the *x* scale + * * **x2** - the ending horizontal position; bound to the *x* scale + * + * If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. + * + * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. + * + * In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: + * + * * **y** - the vertical position; bound to the *y* scale, which must be *band* + * + * If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). + */ export function barX(data, options = {y: indexOf, x2: identity}) { return new BarX(data, maybeStackX(maybeIntervalX(maybeIdentityX(options)))); } From 6a8032ed0947544e6d9b05811d16aa06d3cfd834 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:19:20 -0400 Subject: [PATCH 17/59] jsdoc barY --- README.md | 8 +++++--- src/marks/bar.js | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e9af0ac60..f17a817b42 100644 --- a/README.md +++ b/README.md @@ -917,7 +917,7 @@ In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/m If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). - + #### Plot.barY(*data*, *options*) @@ -930,16 +930,18 @@ Returns a new vertical bar↕︎ with the given *data* and *options*. The follow * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. +If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. -In addition to the [standard bar channels](#bar), the following optional channels are supported: +In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: * **x** - the horizontal position; bound to the *x* scale, which must be *band* If the **x** channel is not specified, the bar will span the full horizontal extent of the plot (or facet). + + ### Box [a boxplot of Michelson’s 1879 measurements of the speed of light](https://observablehq.com/@observablehq/plot-box) diff --git a/src/marks/bar.js b/src/marks/bar.js index 2dbb2c5abc..150a67cdfa 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -153,6 +153,26 @@ export function barX(data, options = {y: indexOf, x2: identity}) { return new BarX(data, maybeStackX(maybeIntervalX(maybeIdentityX(options)))); } +/** + * ```js + * Plot.barY(alphabet, {x: "letter", y: "frequency"}) + * ``` + * + * Returns a new vertical bar↕︎ with the given *data* and *options*. The following channels are required: + * + * * **y1** - the starting vertical position; bound to the *y* scale + * * **y2** - the ending vertical position; bound to the *y* scale + * + * If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. + * + * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. + * + * In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: + * + * * **x** - the horizontal position; bound to the *x* scale, which must be *band* + * + * If the **x** channel is not specified, the bar will span the full horizontal extent of the plot (or facet). + */ export function barY(data, options = {x: indexOf, y2: identity}) { return new BarY(data, maybeStackY(maybeIntervalY(maybeIdentityY(options)))); } From 2814bb0c0158e70a652e9b9819fbac98d70d1f28 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:25:11 -0400 Subject: [PATCH 18/59] jsdoc boxX, boxY --- README.md | 6 +++++ src/marks/box.js | 58 ++++++++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index f17a817b42..29a1887a05 100644 --- a/README.md +++ b/README.md @@ -963,6 +963,8 @@ The given *options* are passed through to these underlying marks, with the excep * **strokeOpacity** - the stroke opacity of the rule, tick, and dot; defaults to 1 * **strokeWidth** - the stroke width of the tick; defaults to 1 + + #### Plot.boxX(*data*, *options*) ```js @@ -971,6 +973,8 @@ Plot.boxX(simpsons.map(d => d.imdb_rating)) Returns a horizontal boxplot mark. If the **x** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **y** option is not specified, it defaults to null; if the **y** option is specified, it should represent an ordinal (discrete) value. + + #### Plot.boxY(*data*, *options*) ```js @@ -979,6 +983,8 @@ Plot.boxY(simpsons.map(d => d.imdb_rating)) Returns a vertical boxplot mark. If the **y** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **x** option is not specified, it defaults to null; if the **x** option is specified, it should represent an ordinal (discrete) value. + + ### Cell [a heatmap](https://observablehq.com/@observablehq/plot-cell) diff --git a/src/marks/box.js b/src/marks/box.js index b0ca5b0bc0..80f52dd67f 100644 --- a/src/marks/box.js +++ b/src/marks/box.js @@ -7,11 +7,17 @@ import {dot} from "./dot.js"; import {ruleX, ruleY} from "./rule.js"; import {tickX, tickY} from "./tick.js"; -// Returns a composite mark for producing a horizontal box plot, applying the -// necessary statistical transforms. The boxes are grouped by y, if present. -export function boxX( - data, - { +/** + * ```js + * Plot.boxX(simpsons.map(d => d.imdb_rating)) + * ``` + * + * Returns a horizontal boxplot mark. If the **x** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **y** option is not specified, it defaults to null; if the **y** option is specified, it should represent an ordinal (discrete) value. + */ +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}, y = null, fill = "#ccc", @@ -20,23 +26,28 @@ export function boxX( strokeOpacity, strokeWidth = 2, sort, - ...options - } = {} -) { + ...remainingOptions + } = options; const group = y != null ? groupY : groupZ; return marks( - ruleY(data, group({x1: loqr1, x2: hiqr2}, {x, y, stroke, strokeOpacity, ...options})), - barX(data, group({x1: "p25", x2: "p75"}, {x, y, fill, fillOpacity, ...options})), - tickX(data, group({x: "p50"}, {x, y, stroke, strokeOpacity, strokeWidth, sort, ...options})), - dot(data, map({x: oqr}, {x, y, z: y, stroke, strokeOpacity, ...options})) + ruleY(data, group({x1: loqr1, x2: hiqr2}, {x, y, stroke, strokeOpacity, ...remainingOptions})), + barX(data, group({x1: "p25", x2: "p75"}, {x, y, fill, fillOpacity, ...remainingOptions})), + tickX(data, group({x: "p50"}, {x, y, stroke, strokeOpacity, strokeWidth, sort, ...remainingOptions})), + dot(data, map({x: oqr}, {x, y, z: y, stroke, strokeOpacity, ...remainingOptions})) ); } -// Returns a composite mark for producing a vertical box plot, applying the -// necessary statistical transforms. The boxes are grouped by x, if present. -export function boxY( - data, - { +/** + * ```js + * Plot.boxY(simpsons.map(d => d.imdb_rating)) + * ``` + * + * Returns a vertical boxplot mark. If the **y** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **x** option is not specified, it defaults to null; if the **x** option is specified, it should represent an ordinal (discrete) value. + */ +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}, x = null, fill = "#ccc", @@ -45,15 +56,14 @@ export function boxY( strokeOpacity, strokeWidth = 2, sort, - ...options - } = {} -) { + ...remainingOptions + } = options; const group = x != null ? groupX : groupZ; return marks( - ruleX(data, group({y1: loqr1, y2: hiqr2}, {x, y, stroke, strokeOpacity, ...options})), - barY(data, group({y1: "p25", y2: "p75"}, {x, y, fill, fillOpacity, ...options})), - tickY(data, group({y: "p50"}, {x, y, stroke, strokeOpacity, strokeWidth, sort, ...options})), - dot(data, map({y: oqr}, {x, y, z: x, stroke, strokeOpacity, ...options})) + ruleX(data, group({y1: loqr1, y2: hiqr2}, {x, y, stroke, strokeOpacity, ...remainingOptions})), + barY(data, group({y1: "p25", y2: "p75"}, {x, y, fill, fillOpacity, ...remainingOptions})), + tickY(data, group({y: "p50"}, {x, y, stroke, strokeOpacity, strokeWidth, sort, ...remainingOptions})), + dot(data, map({y: oqr}, {x, y, z: x, stroke, strokeOpacity, ...remainingOptions})) ); } From 57d18b9c70ebb05bd217461ecc38a1250bf1ef30 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:31:47 -0400 Subject: [PATCH 19/59] jsdoc cell, cellX, cellY --- README.md | 12 ++++++++++-- src/marks/cell.js | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 29a1887a05..d298f04299 100644 --- a/README.md +++ b/README.md @@ -1000,6 +1000,8 @@ If the **x** channel is not specified, the cell will span the full horizontal ex The **stroke** defaults to none. The **fill** defaults to currentColor if the stroke is none, and to none otherwise. + + #### Plot.cell(*data*, *options*) ```js @@ -1008,13 +1010,17 @@ Plot.cell(simpsons, {x: "number_in_season", y: "season", fill: "imdb_rating"}) Returns a new cell with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + + #### Plot.cellX(*data*, *options*) ```js Plot.cellX(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + + #### Plot.cellY(*data*, *options*) @@ -1022,7 +1028,9 @@ Equivalent to [Plot.cell](#plotcelldata-options), except that if the **x** optio Plot.cellY(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + + ### Delaunay diff --git a/src/marks/cell.js b/src/marks/cell.js index 3bcdc9892f..5b49c27215 100644 --- a/src/marks/cell.js +++ b/src/marks/cell.js @@ -24,17 +24,41 @@ export class Cell extends AbstractBar { } } -export function cell(data, {x, y, ...options} = {}) { +/** + * ```js + * Plot.cell(simpsons, {x: "number_in_season", y: "season", fill: "imdb_rating"}) + * ``` + * + * Returns a new cell with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function cell(data, options = {}) { + let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); - return new Cell(data, {...options, x, y}); + return new Cell(data, {...remainingOptions, x, y}); } -export function cellX(data, {x = indexOf, fill, stroke, ...options} = {}) { +/** + * ```js + * Plot.cellX(simpsons.map(d => d.imdb_rating)) + * ``` + * + * Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + */ +export function cellX(data, options = {}) { + let {x = indexOf, fill, stroke, ...remainingOptions} = options; if (fill === undefined && maybeColorChannel(stroke)[0] === undefined) fill = identity; - return new Cell(data, {...options, x, fill, stroke}); + return new Cell(data, {...remainingOptions, x, fill, stroke}); } -export function cellY(data, {y = indexOf, fill, stroke, ...options} = {}) { +/** + * ```js + * Plot.cellY(simpsons.map(d => d.imdb_rating)) + * ``` + * + * Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + */ +export function cellY(data, options = {}) { + let {y = indexOf, fill, stroke, ...remainingOptions} = options; if (fill === undefined && maybeColorChannel(stroke)[0] === undefined) fill = identity; - return new Cell(data, {...options, y, fill, stroke}); + return new Cell(data, {...remainingOptions, y, fill, stroke}); } From 4904ab393f3a4c1db0fa79545969e9e049f9dc9d Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:42:26 -0400 Subject: [PATCH 20/59] jsdoc delaunayLink, delaunayMesh --- README.md | 28 +++++++++++++++++----------- src/marks/area.js | 10 +++++----- src/marks/bar.js | 8 ++++---- src/marks/cell.js | 4 ++-- src/marks/delaunay.js | 10 ++++++++++ 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d298f04299..2e2fd59465 100644 --- a/README.md +++ b/README.md @@ -808,7 +808,7 @@ The area mark supports [curve options](#curves) to control interpolation between Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) ``` -Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/d3/d3-shape/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/d3/d3-shape/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. +Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. @@ -818,9 +818,9 @@ Returns a new area with the given *data* and *options*. Plot.area is rarely used Plot.areaX(aapl, {y: "Date", x: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. -If the **interval** option is specified, the [binY transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -836,9 +836,9 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.areaY(aapl, {x: "Date", y: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. -If the **interval** option is specified, the [binX transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) @@ -907,11 +907,11 @@ Returns a new horizontal bar↔︎ with the given *data* and *options*. The foll * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. +If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. -In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: +In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* @@ -930,11 +930,11 @@ Returns a new vertical bar↕︎ with the given *data* and *options*. The follow * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. +If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. -In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: +In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: * **x** - the horizontal position; bound to the *x* scale, which must be *band* @@ -1018,7 +1018,7 @@ Returns a new cell with the given *data* and *options*. If neither the **x** nor Plot.cellX(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. @@ -1028,7 +1028,7 @@ Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plo Plot.cellY(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -1038,18 +1038,24 @@ Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plo [Source](./src/marks/delaunay.js) · [Examples](https://observablehq.com/@observablehq/plot-delaunay) · Plot provides a handful of marks for Delaunay and Voronoi diagrams (using [d3-delaunay](https://github.com/d3/d3-delaunay) and [Delaunator](https://github.com/mapbox/delaunator)). These marks require the **x** and **y** channels to be specified. + + #### Plot.delaunayLink(*data*, *options*) Draws links for each edge of the Delaunay triangulation of the points given by the **x** and **y** channels. Supports the same options as the [link mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived automatically from **x** and **y**. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the link inherits the corresponding channel value from one of its two endpoints arbitrarily. If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + + #### Plot.delaunayMesh(*data*, *options*) Draws a mesh of the Delaunay triangulation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + + #### Plot.hull(*data*, *options*) Draws a convex hull around the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_ and the **fill** option defaults to _none_. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the hull inherits the corresponding channel value from one of its constituent points arbitrarily. diff --git a/src/marks/area.js b/src/marks/area.js index f5b090bdcd..f0b7a31ec8 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -76,7 +76,7 @@ export class Area extends Mark { * Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/d3/d3-shape/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/d3/d3-shape/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. + * Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. */ export function area(data, options) { if (options === undefined) return areaY(data, {x: first, y: second}); @@ -88,9 +88,9 @@ export function area(data, options) { * Plot.areaX(aapl, {y: "Date", x: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. + * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. * - * If the **interval** option is specified, the [binY transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -108,9 +108,9 @@ export function areaX(data, options) { * Plot.areaY(aapl, {x: "Date", y: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. + * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. * - * If the **interval** option is specified, the [binX transform](https://github.com/d3/d3-shape/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) diff --git a/src/marks/bar.js b/src/marks/bar.js index 150a67cdfa..f4d3f17bbe 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -139,11 +139,11 @@ export class BarY extends AbstractBar { * * **x1** - the starting horizontal position; bound to the *x* scale * * **x2** - the ending horizontal position; bound to the *x* scale * - * If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. + * If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. * * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. * - * In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: + * In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: * * * **y** - the vertical position; bound to the *y* scale, which must be *band* * @@ -163,11 +163,11 @@ export function barX(data, options = {y: indexOf, x2: identity}) { * * **y1** - the starting vertical position; bound to the *y* scale * * **y2** - the ending vertical position; bound to the *y* scale * - * If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/d3/d3-shape/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. + * If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. * * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. * - * In addition to the [standard bar channels](https://github.com/d3/d3-shape/blob/main/README.md#bar), the following optional channels are supported: + * In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: * * * **x** - the horizontal position; bound to the *x* scale, which must be *band* * diff --git a/src/marks/cell.js b/src/marks/cell.js index 5b49c27215..469898f1b7 100644 --- a/src/marks/cell.js +++ b/src/marks/cell.js @@ -42,7 +42,7 @@ export function cell(data, options = {}) { * Plot.cellX(simpsons.map(d => d.imdb_rating)) * ``` * - * Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. */ export function cellX(data, options = {}) { let {x = indexOf, fill, stroke, ...remainingOptions} = options; @@ -55,7 +55,7 @@ export function cellX(data, options = {}) { * Plot.cellY(simpsons.map(d => d.imdb_rating)) * ``` * - * Equivalent to [Plot.cell](https://github.com/d3/d3-shape/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. */ export function cellY(data, options = {}) { let {y = indexOf, fill, stroke, ...remainingOptions} = options; diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index 22d43015d9..aa623234a1 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -279,10 +279,20 @@ function delaunayMark(DelaunayMark, data, {x, y, ...options} = {}) { return new DelaunayMark(data, {...options, x, y}); } +/** + * Draws links for each edge of the Delaunay triangulation of the points given by the **x** and **y** channels. Supports the same options as the [link mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived automatically from **x** and **y**. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the link inherits the corresponding channel value from one of its two endpoints arbitrarily. + * + * If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + */ export function delaunayLink(data, options) { return delaunayMark(DelaunayLink, data, options); } +/** + * Draws a mesh of the Delaunay triangulation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. + * + * If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + */ export function delaunayMesh(data, options) { return delaunayMark(DelaunayMesh, data, options); } From 2d47b19075137e7b24369be921f130cf87b388a3 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:45:50 -0400 Subject: [PATCH 21/59] jsdoc hull, voronoi, voronoiMesh --- README.md | 8 +++++++- src/marks/delaunay.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e2fd59465..4f8f7e5e3f 100644 --- a/README.md +++ b/README.md @@ -1054,7 +1054,7 @@ Draws a mesh of the Delaunay triangulation of the points given by the **x** and If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. - + #### Plot.hull(*data*, *options*) @@ -1062,18 +1062,24 @@ Draws a convex hull around the points given by the **x** and **y** channels. The If a **z** channel is specified, the input points are grouped by *z*, and separate convex hulls are constructed for each group. If the **z** channel is not specified, it defaults to either the **fill** channel, if any, or the **stroke** channel, if any. + + #### Plot.voronoi(*data*, *options*) Draws polygons for each cell of the Voronoi tesselation of the points given by the **x** and **y** channels. If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + + #### Plot.voronoiMesh(*data*, *options*) Draws a mesh for the cell boundaries of the Voronoi tesselation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + + ### Density [A scatterplot showing the relationship between the idle duration and eruption duration for Old Faithful](https://observablehq.com/@observablehq/plot-density) diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index aa623234a1..bf187eb1ce 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -297,14 +297,29 @@ export function delaunayMesh(data, options) { return delaunayMark(DelaunayMesh, data, options); } +/** + * Draws a convex hull around the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_ and the **fill** option defaults to _none_. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the hull inherits the corresponding channel value from one of its constituent points arbitrarily. + * + * If a **z** channel is specified, the input points are grouped by *z*, and separate convex hulls are constructed for each group. If the **z** channel is not specified, it defaults to either the **fill** channel, if any, or the **stroke** channel, if any. + */ export function hull(data, options) { return delaunayMark(Hull, data, options); } +/** + * Draws polygons for each cell of the Voronoi tesselation of the points given by the **x** and **y** channels. + * + * If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + */ export function voronoi(data, options) { return delaunayMark(Voronoi, data, options); } +/** + * Draws a mesh for the cell boundaries of the Voronoi tesselation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. + * + * If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + */ export function voronoiMesh(data, options) { return delaunayMark(VoronoiMesh, data, options); } From efa31a71fdf7ae5bf55fa252ab020b7be5113e67 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:49:08 -0400 Subject: [PATCH 22/59] jsdoc density --- README.md | 4 ++++ src/marks/density.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f8f7e5e3f..501716eea2 100644 --- a/README.md +++ b/README.md @@ -1086,6 +1086,8 @@ If a **z** channel is specified, the input points are grouped by *z*, and separa [Source](./src/marks/density.js) · [Examples](https://observablehq.com/@observablehq/plot-density) · Draws contours representing the density of point clouds, implementing [two-dimensional kernel density estimation](https://en.wikipedia.org/wiki/Multivariate_kernel_density_estimation). Each contour represents the area where the estimated point density is greater than or equal to a given density value. + + #### Plot.density(*data*, *options*) Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option. @@ -1094,6 +1096,8 @@ The **thresholds** option, which defaults to 20, specifies one more than the num If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour. + + ### Dot [a scatterplot](https://observablehq.com/@observablehq/plot-dot) diff --git a/src/marks/density.js b/src/marks/density.js index 0e52345552..9e5c7c78fa 100644 --- a/src/marks/density.js +++ b/src/marks/density.js @@ -65,9 +65,17 @@ export class Density extends Mark { } } -export function density(data, {x, y, ...options} = {}) { +/** + * Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option. + * + * The **thresholds** option, which defaults to 20, specifies one more than the number of contours that will be computed at uniformly-spaced intervals between 0 (exclusive) and the maximum density (exclusive). The **thresholds** option may also be specified as an array or iterable of explicit density values. The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels. + * + * If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour. + */ +export function density(data, options = {}) { + let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); - return new Density(data, {...options, x, y}); + return new Density(data, {...remainingOptions, x, y}); } const dropChannels = new Set(["x", "y", "z", "weight"]); From f1c94685683fd660a0543521d02f7775be2093a2 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:54:22 -0400 Subject: [PATCH 23/59] jsdoc dot, dotX, dotY --- README.md | 12 ++++++++++-- src/marks/dot.js | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 501716eea2..2c5f3d1e9d 100644 --- a/README.md +++ b/README.md @@ -1129,6 +1129,8 @@ The built-in **symbol** types are: *circle*, *cross*, *diamond*, *square*, *star Dots are drawn in input order, with the last data drawn on top. If sorting is needed, say to mitigate overplotting by drawing the smallest dots on top, consider a [sort and reverse transform](#transforms). + + #### Plot.dot(*data*, *options*) ```js @@ -1137,26 +1139,32 @@ Plot.dot(sales, {x: "units", y: "fruit"}) Returns a new dot with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + + #### Plot.dotX(*data*, *options*) ```js Plot.dotX(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + + #### Plot.dotY(*data*, *options*) ```js Plot.dotY(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + + #### Plot.circle(*data*, *options*) Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *circle*. diff --git a/src/marks/dot.js b/src/marks/dot.js index 168104e4d7..8bf454922f 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -113,17 +113,45 @@ export class Dot extends Mark { } } -export function dot(data, {x, y, ...options} = {}) { +/** + * ```js + * Plot.dot(sales, {x: "units", y: "fruit"}) + * ``` + * + * Returns a new dot with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function dot(data, options = {}) { + let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); - return new Dot(data, {...options, x, y}); + return new Dot(data, {...remainingOptions, x, y}); } -export function dotX(data, {x = identity, ...options} = {}) { - return new Dot(data, maybeIntervalMidY({...options, x})); +/** + * ```js + * Plot.dotX(cars.map(d => d["economy (mpg)"])) + * ``` + * + * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * + * If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + */ +export function dotX(data, options = {}) { + const {x = identity, ...remainingOptions} = options; + return new Dot(data, maybeIntervalMidY({...remainingOptions, x})); } -export function dotY(data, {y = identity, ...options} = {}) { - return new Dot(data, maybeIntervalMidX({...options, y})); +/** + * ```js + * Plot.dotY(cars.map(d => d["economy (mpg)"])) + * ``` + * + * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * + * If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + */ +export function dotY(data, options = {}) { + const {y = identity, ...remainingOptions} = options; + return new Dot(data, maybeIntervalMidX({...remainingOptions, y})); } export function circle(data, options) { From 1b2bae085c66c6bce976c262366a846eb7f04365 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:56:43 -0400 Subject: [PATCH 24/59] jsdoc circle, hexagon --- README.md | 10 +++++++--- src/marks/dot.js | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2c5f3d1e9d..c5db0c997e 100644 --- a/README.md +++ b/README.md @@ -1163,15 +1163,19 @@ Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.m If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. - + #### Plot.circle(*data*, *options*) -Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *circle*. +Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *circle*. + + #### Plot.hexagon(*data*, *options*) -Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *hexagon*. +Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *hexagon*. + + ### Hexgrid diff --git a/src/marks/dot.js b/src/marks/dot.js index 8bf454922f..3c6e83c09d 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -154,10 +154,16 @@ export function dotY(data, options = {}) { return new Dot(data, maybeIntervalMidX({...remainingOptions, y})); } +/** + * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *circle*. + */ export function circle(data, options) { return dot(data, {...options, symbol: "circle"}); } +/** + * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *hexagon*. + */ export function hexagon(data, options) { return dot(data, {...options, symbol: "hexagon"}); } From dab682a367babb7020a469fb1c29fc7e7482e163 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 15:59:17 -0400 Subject: [PATCH 25/59] jsdoc hexgrid --- README.md | 6 +++++- src/marks/hexgrid.js | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5db0c997e..5a739e4845 100644 --- a/README.md +++ b/README.md @@ -1181,10 +1181,14 @@ Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.m The hexgrid mark can be used to support marks using the [hexbin](#hexbin) layout. -#### Plot.hexgrid([*options*]) + + +#### Plot.hexgrid(*options*) The **binWidth** option specifies the distance between the centers of neighboring hexagons, in pixels (defaults to 20). The **clip** option defaults to true, clipping the mark to the frame’s dimensions. + + ### Image [a scatterplot of Presidential portraits](https://observablehq.com/@observablehq/plot-image) diff --git a/src/marks/hexgrid.js b/src/marks/hexgrid.js index 34a35fa6fa..98aa23c63d 100644 --- a/src/marks/hexgrid.js +++ b/src/marks/hexgrid.js @@ -12,6 +12,9 @@ const defaults = { strokeOpacity: 0.1 }; +/** + * The **binWidth** option specifies the distance between the centers of neighboring hexagons, in pixels (defaults to 20). The **clip** option defaults to true, clipping the mark to the frame’s dimensions. + */ export function hexgrid(options) { return new Hexgrid(options); } From bd101570beabc29ded4040965e548f0b1f4d865a Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:00:59 -0400 Subject: [PATCH 26/59] jsdoc image --- README.md | 3 +++ src/marks/image.js | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a739e4845..fa86925d54 100644 --- a/README.md +++ b/README.md @@ -1216,6 +1216,8 @@ To crop the image instead of scaling it to fit, set **preserveAspectRatio** to Images are drawn in input order, with the last data drawn on top. If sorting is needed, say to mitigate overplotting, consider a [sort and reverse transform](#transforms). + + #### Plot.image(*data*, *options*) ```js @@ -1224,6 +1226,7 @@ Plot.image(presidents, {x: "inauguration", y: "favorability", src: "portrait"}) Returns a new image with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + ### Linear regression diff --git a/src/marks/image.js b/src/marks/image.js index a09c59b15e..f4f73b5a9f 100644 --- a/src/marks/image.js +++ b/src/marks/image.js @@ -109,7 +109,15 @@ export class Image extends Mark { } } -export function image(data, {x, y, ...options} = {}) { +/** + * ```js + * Plot.image(presidents, {x: "inauguration", y: "favorability", src: "portrait"}) + * ``` + * + * Returns a new image with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function image(data, options = {}) { + let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); - return new Image(data, {...options, x, y}); + return new Image(data, {...remainingOptions, x, y}); } From 4574b7b8f51bc73694b370ec955ea328a25ac89c Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:04:00 -0400 Subject: [PATCH 27/59] jsdoc linearRegressionX, linearRegressionY --- README.md | 6 +++++ src/marks/linearRegression.js | 42 ++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index fa86925d54..a6986f9360 100644 --- a/README.md +++ b/README.md @@ -1244,6 +1244,8 @@ The given *options* are passed through to these underlying marks, with the excep Multiple regressions can be defined by specifying the *z*, *fill*, or *stroke* channel. + + #### Plot.linearRegressionX(*data*, *options*) ```js @@ -1252,6 +1254,8 @@ Plot.linearRegressionX(mtcars, {y: "wt", x: "hp"}) Returns a linear regression mark where *x* is the dependent variable and *y* is the independent variable. + + #### Plot.linearRegressionY(*data*, *options*) ```js @@ -1260,6 +1264,8 @@ Plot.linearRegressionY(mtcars, {x: "wt", y: "hp"}) Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. + + ### Line [a line chart](https://observablehq.com/@observablehq/plot-line) diff --git a/src/marks/linearRegression.js b/src/marks/linearRegression.js index 60b6ea5b72..b9d13f89ad 100644 --- a/src/marks/linearRegression.js +++ b/src/marks/linearRegression.js @@ -122,18 +122,40 @@ class LinearRegressionY extends LinearRegression { } } -export function linearRegressionX( - data, - {y = indexOf, x = identity, stroke, fill = isNoneish(stroke) ? "currentColor" : stroke, ...options} = {} -) { - return new LinearRegressionX(data, maybeDenseIntervalY({...options, x, y, fill, stroke})); +/** + * ```js + * Plot.linearRegressionX(mtcars, {y: "wt", x: "hp"}) + * ``` + * + * Returns a linear regression mark where *x* is the dependent variable and *y* is the independent variable. + */ +export function linearRegressionX(data, options = {}) { + const { + y = indexOf, + x = identity, + stroke, + fill = isNoneish(stroke) ? "currentColor" : stroke, + ...remainingOptions + } = options; + return new LinearRegressionX(data, maybeDenseIntervalY({...remainingOptions, x, y, fill, stroke})); } -export function linearRegressionY( - data, - {x = indexOf, y = identity, stroke, fill = isNoneish(stroke) ? "currentColor" : stroke, ...options} = {} -) { - return new LinearRegressionY(data, maybeDenseIntervalX({...options, x, y, fill, stroke})); +/** + * ```js + * Plot.linearRegressionY(mtcars, {x: "wt", y: "hp"}) + * ``` + * + * Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. + */ +export function linearRegressionY(data, options = {}) { + const { + x = indexOf, + y = identity, + stroke, + fill = isNoneish(stroke) ? "currentColor" : stroke, + ...remainingOptions + } = options; + return new LinearRegressionY(data, maybeDenseIntervalX({...remainingOptions, x, y, fill, stroke})); } function linearRegressionF(I, X, Y) { From 9c96d02f1ee8e4c2071f1a1150698d69df38a776 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:08:36 -0400 Subject: [PATCH 28/59] jsdoc line, lineX, lineY --- README.md | 16 +++++++++++---- src/marks/line.js | 52 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a6986f9360..f5c251a07e 100644 --- a/README.md +++ b/README.md @@ -1289,6 +1289,8 @@ Points along the line are connected in input order. Likewise, if there are multi The line mark supports [curve options](#curves) to control interpolation between points, and [marker options](#markers) to add a marker (such as a dot or an arrowhead) on each of the control points. If any of the *x* or *y* values are invalid (undefined, null, or NaN), the line will be interrupted, resulting in a break that divides the line shape into multiple segments. (See [d3-shape’s *line*.defined](https://github.com/d3/d3-shape/blob/main/README.md#line_defined) for more.) If a line segment consists of only a single point, it may appear invisible unless rendered with rounded or square line caps. In addition, some curves such as *cardinal-open* only render a visible segment if it contains multiple points. + + #### Plot.line(*data*, *options*) ```js @@ -1297,15 +1299,17 @@ Plot.line(aapl, {x: "Date", y: "Close"}) Returns a new line with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + + #### Plot.lineX(*data*, *options*) ```js Plot.lineX(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. +Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -1313,15 +1317,17 @@ Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + + #### Plot.lineY(*data*, *options*) ```js Plot.lineY(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. +Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) @@ -1329,6 +1335,8 @@ Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + + ### Link [a chart with links](https://observablehq.com/@observablehq/plot-link) diff --git a/src/marks/line.js b/src/marks/line.js index 9ed9b2cc2c..63586afe76 100644 --- a/src/marks/line.js +++ b/src/marks/line.js @@ -70,15 +70,55 @@ export class Line extends Mark { } } -export function line(data, {x, y, ...options} = {}) { +/** + * ```js + * Plot.line(aapl, {x: "Date", y: "Close"}) + * ``` + * + * Returns a new line with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function line(data, options = {}) { + let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); - return new Line(data, {...options, x, y}); + return new Line(data, {...remainingOptions, x, y}); } -export function lineX(data, {x = identity, y = indexOf, ...options} = {}) { - return new Line(data, maybeDenseIntervalY({...options, x, y})); +/** + * ```js + * Plot.lineX(aapl.map(d => d.Close)) + * ``` + * + * Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. + * + * If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * + * ```js + * Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) + * ``` + * + * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + */ +export function lineX(data, options = {}) { + const {x = identity, y = indexOf, ...remainingOptions} = options; + return new Line(data, maybeDenseIntervalY({...remainingOptions, x, y})); } -export function lineY(data, {x = indexOf, y = identity, ...options} = {}) { - return new Line(data, maybeDenseIntervalX({...options, x, y})); +/** + * ```js + * Plot.lineY(aapl.map(d => d.Close)) + * ``` + * + * Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. + * + * If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * + * ```js + * Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) + * ``` + * + * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + */ +export function lineY(data, options = {}) { + const {x = indexOf, y = identity, ...remainingOptions} = options; + return new Line(data, maybeDenseIntervalX({...remainingOptions, x, y})); } From 2a075b1ccd38f63a544d45c50cd93c2feb6755a7 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:11:00 -0400 Subject: [PATCH 29/59] jsdoc link --- README.md | 4 ++++ src/marks/link.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5c251a07e..79fc1a1cbd 100644 --- a/README.md +++ b/README.md @@ -1356,6 +1356,8 @@ The link mark supports the [standard mark options](#marks). The **stroke** defau The link mark supports [curve options](#curves) to control interpolation between points, and [marker options](#markers) to add a marker (such as a dot or an arrowhead) on each of the control points. Since a link always has two points by definition, only the following curves (or a custom curve) are recommended: *linear*, *step*, *step-after*, *step-before*, *bump-x*, or *bump-y*. Note that the *linear* curve is incapable of showing a fill since a straight line has zero area. For a curved link, you can use a bent [arrow](#arrow) (with no arrowhead, if desired). + + #### Plot.link(*data*, *options*) ```js @@ -1364,6 +1366,8 @@ Plot.link(inequality, {x1: "POP_1980", y1: "R90_10_1980", x2: "POP_2015", y2: "R Returns a new link with the given *data* and *options*. + + ### Rect [a histogram](https://observablehq.com/@observablehq/plot-rect) diff --git a/src/marks/link.js b/src/marks/link.js index d0c4813f83..b4d9529c7d 100644 --- a/src/marks/link.js +++ b/src/marks/link.js @@ -58,10 +58,18 @@ export class Link extends Mark { } } -export function link(data, {x, x1, x2, y, y1, y2, ...options} = {}) { +/** + * ```js + * Plot.link(inequality, {x1: "POP_1980", y1: "R90_10_1980", x2: "POP_2015", y2: "R90_10_2015"}) + * ``` + * + * Returns a new link with the given *data* and *options*. + */ +export function link(data, options = {}) { + let {x, x1, x2, y, y1, y2, ...remainingOptions} = options; [x1, x2] = maybeSameValue(x, x1, x2); [y1, y2] = maybeSameValue(y, y1, y2); - return new Link(data, {...options, x1, x2, y1, y2}); + return new Link(data, {...remainingOptions, x1, x2, y1, y2}); } // If x1 and x2 are specified, return them as {x1, x2}. From 9124408b65087f75891a39db76beb563f406abe9 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:14:27 -0400 Subject: [PATCH 30/59] jsdoc rect, rectX, rectY --- README.md | 12 ++++++++++-- src/marks/rect.js | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 79fc1a1cbd..a9b6a96bc7 100644 --- a/README.md +++ b/README.md @@ -1387,6 +1387,8 @@ If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be der The rect mark supports the [standard mark options](#marks), including insets and rounded corners. The **stroke** defaults to none. The **fill** defaults to currentColor if the stroke is none, and to none otherwise. + + #### Plot.rect(*data*, *options*) ```js @@ -1395,13 +1397,17 @@ Plot.rect(athletes, Plot.bin({fill: "count"}, {x: "weight", y: "height"})) Returns a new rect with the given *data* and *options*. + + #### Plot.rectX(*data*, *options*) ```js Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. +Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. + + #### Plot.rectY(*data*, *options*) @@ -1409,7 +1415,9 @@ Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **x Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. +Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. + + ### Rule diff --git a/src/marks/rect.js b/src/marks/rect.js index 037d6313c0..15cc3d8218 100644 --- a/src/marks/rect.js +++ b/src/marks/rect.js @@ -88,14 +88,35 @@ export class Rect extends Mark { } } +/** + * ```js + * Plot.rect(athletes, Plot.bin({fill: "count"}, {x: "weight", y: "height"})) + * ``` + * + * Returns a new rect with the given *data* and *options*. + */ export function rect(data, options) { return new Rect(data, maybeTrivialIntervalX(maybeTrivialIntervalY(options))); } +/** + * ```js + * Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) + * ``` + * + * Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. + */ export function rectX(data, options = {y: indexOf, interval: 1, x2: identity}) { return new Rect(data, maybeStackX(maybeTrivialIntervalY(maybeIdentityX(options)))); } +/** + * ```js + * Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) + * ``` + * + * Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. + */ export function rectY(data, options = {x: indexOf, interval: 1, y2: identity}) { return new Rect(data, maybeStackY(maybeTrivialIntervalX(maybeIdentityY(options)))); } From 9ce7efae8fcb221b154b8de5e940d5307c0ef55e Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:19:37 -0400 Subject: [PATCH 31/59] jsdoc ruleX, ruleY --- README.md | 10 ++++++++-- src/marks/rule.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9b6a96bc7..3e441e26ee 100644 --- a/README.md +++ b/README.md @@ -1427,6 +1427,8 @@ Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README. For the required channels, see [Plot.ruleX](#plotrulexdata-options) and [Plot.ruleY](#plotruleydata-options). The rule mark supports the [standard mark options](#marks), including insets along its secondary dimension. The **stroke** defaults to currentColor. + + #### Plot.ruleX(*data*, *options*) ```js @@ -1436,7 +1438,7 @@ Plot.ruleX([0]) // as annotation Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY ``` -Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: +Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: * **x** - the horizontal position; bound to the *x* scale * **y1** - the starting vertical position; bound to the *y* scale @@ -1446,6 +1448,8 @@ If the **x** option is not specified, it defaults to the identity function and a If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. + + #### Plot.ruleY(*data*, *options*) ```js @@ -1455,7 +1459,7 @@ Plot.ruleY([0]) // as annotation Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX ``` -Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: +Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: * **y** - the vertical position; bound to the *y* scale * **x1** - the starting horizontal position; bound to the *x* scale @@ -1465,6 +1469,8 @@ If the **y** option is not specified, it defaults to the identity function and a If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. + + ### Text [a bar chart with text labels](https://observablehq.com/@observablehq/plot-text) diff --git a/src/marks/rule.js b/src/marks/rule.js index da9df4d404..8db8f459ce 100644 --- a/src/marks/rule.js +++ b/src/marks/rule.js @@ -107,12 +107,48 @@ export class RuleY extends Mark { } } +/** + * ```js + * Plot.ruleX([0]) // as annotation + * ``` + * ```js + * Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY + * ``` + * + * Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: + * + * * **x** - the horizontal position; bound to the *x* scale + * * **y1** - the starting vertical position; bound to the *y* scale + * * **y2** - the ending vertical position; bound to the *y* scale + * + * If the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is specified, it is shorthand for the **y2** option with **y1** equal to zero; this is the typical configuration for a vertical lollipop chart with rules aligned at *y* = 0. If the **y1** channel is not specified, the rule will start at the top of the plot (or facet). If the **y2** channel is not specified, the rule will end at the bottom of the plot (or facet). + * + * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. + */ export function ruleX(data, options) { let {x = identity, y, y1, y2, ...rest} = maybeIntervalY(options); [y1, y2] = maybeOptionalZero(y, y1, y2); return new RuleX(data, {...rest, x, y1, y2}); } +/** + * ```js + * Plot.ruleY([0]) // as annotation + * ``` + * ```js + * Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX + * ``` + * + * Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: + * + * * **y** - the vertical position; bound to the *y* scale + * * **x1** - the starting horizontal position; bound to the *x* scale + * * **x2** - the ending horizontal position; bound to the *x* scale + * + * If the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is specified, it is shorthand for the **x2** option with **x1** equal to zero; this is the typical configuration for a horizontal lollipop chart with rules aligned at *x* = 0. If the **x1** channel is not specified, the rule will start at the left edge of the plot (or facet). If the **x2** channel is not specified, the rule will end at the right edge of the plot (or facet). + * + * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. + */ export function ruleY(data, options) { let {y = identity, x, x1, x2, ...rest} = maybeIntervalX(options); [x1, x2] = maybeOptionalZero(x, x1, x2); From 585f854232c1232b6eda758605cdc24ae425c5a9 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:23:22 -0400 Subject: [PATCH 32/59] jsdoc text, textX, textY --- README.md | 12 ++++++++++-- src/marks/text.js | 28 ++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3e441e26ee..a1088e35ae 100644 --- a/README.md +++ b/README.md @@ -1515,22 +1515,30 @@ If the **frameAnchor** option is not specified, then **textAnchor** and **lineAn The **paintOrder** option defaults to “stroke” and the **strokeWidth** option defaults to 3. By setting **fill** to the foreground color and **stroke** to the background color (such as black and white, respectively), you can surround text with a “halo” which may improve legibility against a busy background. + + #### Plot.text(*data*, *options*) Returns a new text mark with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + + #### Plot.textX(*data*, *options*) -Equivalent to [Plot.text](#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + + #### Plot.textY(*data*, *options*) -Equivalent to [Plot.text](#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + + ### Tick [a barcode plot](https://observablehq.com/@observablehq/plot-tick) diff --git a/src/marks/text.js b/src/marks/text.js index 38bfdb31bc..739535a8cc 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -157,17 +157,33 @@ function applyMultilineText(selection, {monospace, lineAnchor, lineHeight, lineW }); } -export function text(data, {x, y, ...options} = {}) { +/** + * Returns a new text mark with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function text(data, options = {}) { + let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); - return new Text(data, {...options, x, y}); + return new Text(data, {...remainingOptions, x, y}); } -export function textX(data, {x = identity, ...options} = {}) { - return new Text(data, maybeIntervalMidY({...options, x})); +/** + * Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * + * If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + */ +export function textX(data, options = {}) { + const {x = identity, ...remainingOptions} = options; + return new Text(data, maybeIntervalMidY({...remainingOptions, x})); } -export function textY(data, {y = identity, ...options} = {}) { - return new Text(data, maybeIntervalMidX({...options, y})); +/** + * Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * + * If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + */ +export function textY(data, options = {}) { + const {y = identity, ...remainingOptions} = options; + return new Text(data, maybeIntervalMidX({...remainingOptions, y})); } function applyIndirectTextStyles(selection, mark, T) { From 235da271eb2ccdaa87f108bd4d1859d8680858d3 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:26:27 -0400 Subject: [PATCH 33/59] jsdoc tickX, tickY --- README.md | 6 ++++++ src/marks/tick.js | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a1088e35ae..c864631919 100644 --- a/README.md +++ b/README.md @@ -1547,6 +1547,8 @@ If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*in For the required channels, see [Plot.tickX](#plottickxdata-options) and [Plot.tickY](#plottickydata-options). The tick mark supports the [standard mark options](#marks), including insets. The **stroke** defaults to currentColor. + + #### Plot.tickX(*data*, *options*) ```js @@ -1563,6 +1565,8 @@ The following optional channels are supported: If the **y** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + + #### Plot.tickY(*data*, *options*) ```js @@ -1579,6 +1583,8 @@ The following optional channels are supported: If the **x** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + + ### Vector [a vector field](https://observablehq.com/@observablehq/plot-vector) diff --git a/src/marks/tick.js b/src/marks/tick.js index 1cf890e152..9e553e853c 100644 --- a/src/marks/tick.js +++ b/src/marks/tick.js @@ -100,10 +100,42 @@ export class TickY extends AbstractTick { } } -export function tickX(data, {x = identity, ...options} = {}) { - return new TickX(data, {...options, x}); +/** + * ```js + * Plot.tickX(stateage, {x: "population", y: "age"}) + * ``` + * + * Returns a new tick↕︎ with the given *data* and *options*. The following channels are required: + * + * * **x** - the horizontal position; bound to the *x* scale + * + * The following optional channels are supported: + * + * * **y** - the vertical position; bound to the *y* scale, which must be *band* + * + * If the **y** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + */ +export function tickX(data, options = {}) { + const {x = identity, ...remainingOptions} = options; + return new TickX(data, {...remainingOptions, x}); } -export function tickY(data, {y = identity, ...options} = {}) { - return new TickY(data, {...options, y}); +/** + * ```js + * Plot.tickY(stateage, {y: "population", x: "age"}) + * ``` + * + * Returns a new tick↔︎ with the given *data* and *options*. The following channels are required: + * + * * **y** - the vertical position; bound to the *y* scale + * + * The following optional channels are supported: + * + * * **x** - the horizontal position; bound to the *x* scale, which must be *band* + * + * If the **x** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + */ +export function tickY(data, options = {}) { + const {y = identity, ...remainingOptions} = options; + return new TickY(data, {...remainingOptions, y}); } From 3ca814275c032ea07ff5a8bccd4c8d2b766e4a81 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:31:43 -0400 Subject: [PATCH 34/59] jsdoc vector, vectorX, vectorY --- README.md | 12 ++++++++++-- src/marks/vector.js | 28 ++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c864631919..63d2aaafb5 100644 --- a/README.md +++ b/README.md @@ -1615,6 +1615,8 @@ The **stroke** defaults to currentColor. The **strokeWidth** defaults to 1.5, an Vectors are drawn in input order, with the last data drawn on top. If sorting is needed, say to mitigate overplotting by drawing the smallest vectors on top, consider a [sort and reverse transform](#transforms). + + #### Plot.vector(*data*, *options*) ```js @@ -1623,13 +1625,19 @@ Plot.vector(wind, {x: "longitude", y: "latitude", length: "speed", rotate: "dire Returns a new vector with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + + #### Plot.vectorX(*data*, *options*) -Equivalent to Plot.vector except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + + #### Plot.vectorY(*data*, *options*) -Equivalent to Plot.vector except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + + ## Decorations diff --git a/src/marks/vector.js b/src/marks/vector.js index 10e4745ce8..06f1de91d9 100644 --- a/src/marks/vector.js +++ b/src/marks/vector.js @@ -74,15 +74,31 @@ export class Vector extends Mark { } } -export function vector(data, {x, y, ...options} = {}) { +/** + * ```js + * Plot.vector(wind, {x: "longitude", y: "latitude", length: "speed", rotate: "direction"}) + * ``` + * + * Returns a new vector with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + */ +export function vector(data, options = {}) { + let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); - return new Vector(data, {...options, x, y}); + return new Vector(data, {...remainingOptions, x, y}); } -export function vectorX(data, {x = identity, ...options} = {}) { - return new Vector(data, {...options, x}); +/** + * Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + */ +export function vectorX(data, options = {}) { + const {x = identity, ...remainingOptions} = options; + return new Vector(data, {...remainingOptions, x}); } -export function vectorY(data, {y = identity, ...options} = {}) { - return new Vector(data, {...options, y}); +/** + * Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + */ +export function vectorY(data, options = {}) { + const {y = identity, ...remainingOptions} = options; + return new Vector(data, {...remainingOptions, y}); } From 3eb3c6f5bb5926d98a186c4f2710f4372af03687 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:33:18 -0400 Subject: [PATCH 35/59] jsdoc frame --- README.md | 4 ++++ src/marks/frame.js | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/README.md b/README.md index 63d2aaafb5..cc6a47e7dd 100644 --- a/README.md +++ b/README.md @@ -1651,6 +1651,8 @@ Decorations are static marks that do not represent data. Currently this includes The frame mark supports the [standard mark options](#marks), and the **rx** and **ry** options to set the [*x* radius](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx) and [*y* radius](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/ry) for rounded corners. It does not accept any data or support channels. The default **stroke** is currentColor, and the default **fill** is none. + + #### Plot.frame(*options*) ```js @@ -1659,6 +1661,8 @@ Plot.frame({stroke: "red"}) Returns a new frame with the specified *options*. + + ## Transforms Plot’s transforms provide a convenient mechanism for transforming data as part of a plot specification. All marks support the following basic transforms: diff --git a/src/marks/frame.js b/src/marks/frame.js index 0f3edb8d20..d97e671f66 100644 --- a/src/marks/frame.js +++ b/src/marks/frame.js @@ -37,6 +37,13 @@ export class Frame extends Mark { } } +/** + * ```js + * Plot.frame({stroke: "red"}) + * ``` + * + * Returns a new frame with the specified *options*. + */ export function frame(options) { return new Frame(options); } From ccf4cd55d0d4077662247547f83b9db440dac1d1 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:41:23 -0400 Subject: [PATCH 36/59] jsdoc sort, shuffle, reverse, filter --- README.md | 12 +++++++++++- src/transforms/basic.js | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cc6a47e7dd..42d9e0116b 100644 --- a/README.md +++ b/README.md @@ -1707,13 +1707,17 @@ Plot’s option transforms, listed below, do more than populate the **transform* The *filter*, *sort* and *reverse* transforms are also available as functions, allowing the order of operations to be specified explicitly. For example, sorting before binning results in sorted data inside bins, whereas sorting after binning results affects the *z* order of rendered marks. + + #### Plot.sort(*order*, *options*) ```js Plot.sort("body_mass_g", options) // show data in ascending body mass order ``` -Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. +Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. + + #### Plot.shuffle(*options*) @@ -1723,6 +1727,8 @@ Plot.shuffle(options) // show data in random order Shuffles the data randomly. If a *seed* option is specified, a linear congruential generator with the given seed is used to generate random numbers deterministically; otherwise, Math.random is used. + + #### Plot.reverse(*options*) ```js @@ -1731,6 +1737,8 @@ Plot.reverse(options) // reverse the input order Reverses the order of the data. + + #### Plot.filter(*test*, *options*) ```js @@ -1739,6 +1747,8 @@ Plot.filter(d => d.body_mass_g > 3000, options) // show data whose body mass is Filters the data given the specified *test*. The test can be given as an accessor function (which receives the datum and index), or as a channel value definition such as a field name; truthy values are retained. + + ### Bin [a histogram of athletes by weight](https://observablehq.com/@observablehq/plot-bin) diff --git a/src/transforms/basic.js b/src/transforms/basic.js index 68882d611b..9ba97b8469 100644 --- a/src/transforms/basic.js +++ b/src/transforms/basic.js @@ -62,8 +62,15 @@ function apply(options, t) { return (options.initializer != null ? initializer : basic)(options, t); } -export function filter(value, options) { - return apply(options, filterTransform(value)); +/** + * ```js + * Plot.filter(d => d.body_mass_g > 3000, options) // show data whose body mass is greater than 3kg + * ``` + * + * Filters the data given the specified *test*. The test can be given as an accessor function (which receives the datum and index), or as a channel value definition such as a field name; truthy values are retained. + */ +export function filter(test, options) { + return apply(options, filterTransform(test)); } function filterTransform(value) { @@ -73,6 +80,13 @@ function filterTransform(value) { }; } +/** + * ```js + * Plot.reverse(options) // reverse the input order + * ``` + * + * Reverses the order of the data. + */ export function reverse(options) { return {...apply(options, reverseTransform), sort: null}; } @@ -81,13 +95,28 @@ function reverseTransform(data, facets) { return {data, facets: facets.map((I) => I.slice().reverse())}; } -export function shuffle({seed, ...options} = {}) { - return {...apply(options, sortValue(seed == null ? Math.random : randomLcg(seed))), sort: null}; +/** + * ```js + * Plot.shuffle(options) // show data in random order + * ``` + * + * Shuffles the data randomly. If a *seed* option is specified, a linear congruential generator with the given seed is used to generate random numbers deterministically; otherwise, Math.random is used. + */ +export function shuffle(options = {}) { + const {seed, ...remainingOptions} = options; + return {...apply(remainingOptions, sortValue(seed == null ? Math.random : randomLcg(seed))), sort: null}; } -export function sort(value, options) { +/** + * ```js + * Plot.sort("body_mass_g", options) // show data in ascending body mass order + * ``` + * + * Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. + */ +export function sort(order, options) { return { - ...(isOptions(value) && value.channel !== undefined ? initializer : apply)(options, sortTransform(value)), + ...(isOptions(order) && order.channel !== undefined ? initializer : apply)(options, sortTransform(order)), sort: null }; } From 2712ebb9c8fe59b0724f0ca3f6cb12b98b28d607 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:47:54 -0400 Subject: [PATCH 37/59] jsdoc bin, binX, binY --- README.md | 10 ++++++++++ src/transforms/bin.js | 31 ++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 42d9e0116b..46ee2816db 100644 --- a/README.md +++ b/README.md @@ -1869,6 +1869,8 @@ Plot.binX({y: "count"}, {x: "body_mass_g", fill: "species"}) Lastly, the bin transform changes the default [mark insets](#marks): rather than defaulting to zero, a pixel is reserved to separate adjacent bins. Plot.binX changes the defaults for **insetLeft** and **insetRight**; Plot.binY changes the defaults for **insetTop** and **insetBottom**; Plot.bin changes all four. + + #### Plot.bin(*outputs*, *options*) ```js @@ -1877,6 +1879,9 @@ Plot.rect(athletes, Plot.bin({fillOpacity: "count"}, {x: "weight", y: "height"}) Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke*, if any. + + + #### Plot.binX(*outputs*, *options*) ```js @@ -1885,6 +1890,9 @@ Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. + + + #### Plot.binY(*outputs*, *options*) ```js @@ -1893,6 +1901,8 @@ Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. + + ### Group [a histogram of penguins by species](https://observablehq.com/@observablehq/plot-group) diff --git a/src/transforms/bin.js b/src/transforms/bin.js index 60e8540747..8a3fdaa504 100644 --- a/src/transforms/bin.js +++ b/src/transforms/bin.js @@ -30,22 +30,47 @@ import { import {maybeInsetX, maybeInsetY} from "./inset.js"; import {maybeInterval} from "./interval.js"; -// Group on {z, fill, stroke}, then optionally on y, then bin x. +/** + * ```js + * Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) + * ``` + * + * Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#bin + */ export function binX(outputs = {y: "count"}, options = {}) { + // Group on {z, fill, stroke}, then optionally on y, then bin x. [outputs, options] = mergeOptions(outputs, options); const {x, y} = options; return binn(maybeBinValue(x, options, identity), null, null, y, outputs, maybeInsetX(options)); } -// Group on {z, fill, stroke}, then optionally on x, then bin y. +/** + * ```js + * Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) + * ``` + * + * Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. + */ export function binY(outputs = {x: "count"}, options = {}) { + // Group on {z, fill, stroke}, then optionally on x, then bin y. [outputs, options] = mergeOptions(outputs, options); const {x, y} = options; return binn(null, maybeBinValue(y, options, identity), x, null, outputs, maybeInsetY(options)); } -// Group on {z, fill, stroke}, then bin on x and y. +/** + * ```js + * Plot.rect(athletes, Plot.bin({fillOpacity: "count"}, {x: "weight", y: "height"})) + * ``` + * + * Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke*, if any. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#bin + */ export function bin(outputs = {fill: "count"}, options = {}) { + // Group on {z, fill, stroke}, then bin on x and y. [outputs, options] = mergeOptions(outputs, options); const {x, y} = maybeBinValueTuple(options); return binn(x, y, null, null, outputs, maybeInsetX(maybeInsetY(options))); From 400f3f13fd81a9376b9ceb99580b8bb64fbfeff2 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:52:42 -0400 Subject: [PATCH 38/59] jsdoc group, groupX, groupY, groupZ --- README.md | 14 +++++++++++++ src/transforms/group.js | 44 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 46ee2816db..c80fc3799a 100644 --- a/README.md +++ b/README.md @@ -1976,6 +1976,8 @@ Plot.groupX({y: "count", title: masses => masses.join("\n")}, {x: "species", tit If any of **z**, **fill**, or **stroke** is a channel, the first of these channels is considered the *z* dimension and will be used to subdivide groups. + + #### Plot.group(*outputs*, *options*) ```js @@ -1984,6 +1986,9 @@ Plot.group({fill: "count"}, {x: "island", y: "species"}) Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if any. + + + #### Plot.groupX(*outputs*, *options*) ```js @@ -1992,6 +1997,9 @@ Plot.groupX({y: "sum"}, {x: "species", y: "body_mass_g"}) Groups on *x* and the first channel of *z*, *fill*, or *stroke*, if any. + + + #### Plot.groupY(*outputs*, *options*) ```js @@ -2000,6 +2008,9 @@ Plot.groupY({x: "sum"}, {y: "species", x: "body_mass_g"}) Groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. + + + #### Plot.groupZ(*outputs*, *options*) ```js @@ -2008,6 +2019,9 @@ Plot.groupZ({x: "proportion"}, {fill: "species"}) Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of *z*, *fill*, or *stroke* are channels, then all data (within each facet) is placed into a single group. + + + ### Map [moving averages of daily highs and lows](https://observablehq.com/@observablehq/plot-map) diff --git a/src/transforms/group.js b/src/transforms/group.js index 39e0aea894..13efb7ecc2 100644 --- a/src/transforms/group.js +++ b/src/transforms/group.js @@ -32,27 +32,63 @@ import { } from "../options.js"; import {basic} from "./basic.js"; -// Group on {z, fill, stroke}. +/** + * ```js + * Plot.groupZ({x: "proportion"}, {fill: "species"}) + * ``` + * + * Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of *z*, *fill*, or *stroke* are channels, then all data (within each facet) is placed into a single group. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#group + */ export function groupZ(outputs, options) { + // Group on {z, fill, stroke}. return groupn(null, null, outputs, options); } -// Group on {z, fill, stroke}, then on x. +/** + * ```js + * Plot.groupX({y: "sum"}, {x: "species", y: "body_mass_g"}) + * ``` + * + * Groups on *x* and the first channel of *z*, *fill*, or *stroke*, if any. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#group + */ export function groupX(outputs = {y: "count"}, options = {}) { + // Group on {z, fill, stroke}, then on x. const {x = identity} = options; if (x == null) throw new Error("missing channel: x"); return groupn(x, null, outputs, options); } -// Group on {z, fill, stroke}, then on y. +/** + * ```js + * Plot.groupY({x: "sum"}, {y: "species", x: "body_mass_g"}) + * ``` + * + * Groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#group + */ export function groupY(outputs = {x: "count"}, options = {}) { + // Group on {z, fill, stroke}, then on y. const {y = identity} = options; if (y == null) throw new Error("missing channel: y"); return groupn(null, y, outputs, options); } -// Group on {z, fill, stroke}, then on x and y. +/** + * ```js + * Plot.group({fill: "count"}, {x: "island", y: "species"}) + * ``` + * + * Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if any. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#group + */ export function group(outputs = {fill: "count"}, options = {}) { + // Group on {z, fill, stroke}, then on x and y. let {x, y} = options; [x, y] = maybeTuple(x, y); if (x == null) throw new Error("missing channel: x"); From b162e60c91c25a4b03bab760b654b76ce9a91343 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 16:58:20 -0400 Subject: [PATCH 39/59] jsdoc map, mapX, mapY --- README.md | 11 +++++++++++ src/transforms/map.js | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c80fc3799a..1894a8711f 100644 --- a/README.md +++ b/README.md @@ -2084,6 +2084,8 @@ The following window reducers are supported: By default, **anchor** is *middle* and **reduce** is *mean*. + + #### Plot.map(*outputs*, *options*) ```js @@ -2092,6 +2094,9 @@ Plot.map({y: "cumsum"}, {y: d3.randomNormal()}) Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for each channel declared in the specified *outputs* object, applies the corresponding map method. Each channel in *outputs* must have a corresponding input channel in *options*. + + + #### Plot.mapX(*map*, *options*) ```js @@ -2100,6 +2105,9 @@ Plot.mapX("cumsum", {x: d3.randomNormal()}) Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores any of **x**, **x1**, and **x2** not present in *options*. + + + #### Plot.mapY(*map*, *options*) ```js @@ -2108,6 +2116,9 @@ Plot.mapY("cumsum", {y: d3.randomNormal()}) Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. + + + #### Plot.normalize(*basis*) ```js diff --git a/src/transforms/map.js b/src/transforms/map.js index 6794f91f9e..d4d30afc57 100644 --- a/src/transforms/map.js +++ b/src/transforms/map.js @@ -2,20 +2,47 @@ import {count, group, rank} from "d3"; import {maybeZ, take, valueof, maybeInput, column} from "../options.js"; import {basic} from "./basic.js"; -export function mapX(m, options = {}) { +/** + * ```js + * Plot.mapX("cumsum", {x: d3.randomNormal()}) + * ``` + * + * Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores any of **x**, **x1**, and **x2** not present in *options*. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#map + */ +export function mapX(map, options = {}) { return map( - Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, m])), + Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, map])), options ); } -export function mapY(m, options = {}) { +/** + * ```js + * Plot.mapY("cumsum", {y: d3.randomNormal()}) + * ``` + * + * Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#map + */ +export function mapY(map, options = {}) { return map( - Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, m])), + Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, map])), options ); } +/** + * ```js + * Plot.map({y: "cumsum"}, {y: d3.randomNormal()}) + * ``` + * + * Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for each channel declared in the specified *outputs* object, applies the corresponding map method. Each channel in *outputs* must have a corresponding input channel in *options*. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#map + */ export function map(outputs = {}, options = {}) { const z = maybeZ(options); const channels = Object.entries(outputs).map(([key, map]) => { From 3a75fca2855feafa653f405a4f1afdfd6796cf41 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:02:23 -0400 Subject: [PATCH 40/59] jsdoc normalize, normalizeX, normalizeY --- README.md | 12 +++++++++--- src/transforms/normalize.js | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1894a8711f..e883e6dc0d 100644 --- a/README.md +++ b/README.md @@ -2117,7 +2117,7 @@ Plot.mapY("cumsum", {y: d3.randomNormal()}) Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. - + #### Plot.normalize(*basis*) @@ -2127,13 +2127,17 @@ Plot.map({y: Plot.normalize("first")}, {x: "Date", y: "Close", stroke: "Symbol"} Returns a normalize map method for the given *basis*, suitable for use with Plot.map. + + #### Plot.normalizeX(*basis*, *options*) ```js Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapX](#plotmapxmap-options), but applies the normalize map method with the given *basis*. +Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), but applies the normalize map method with the given *basis*. + + #### Plot.normalizeY(*basis*, *options*) @@ -2141,7 +2145,9 @@ Like [Plot.mapX](#plotmapxmap-options), but applies the normalize map method wit Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapY](#plotmapymap-options), but applies the normalize map method with the given *basis*. +Like [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), but applies the normalize map method with the given *basis*. + + #### Plot.window(*k*) diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js index 3eb03bef0c..0d49eafba5 100644 --- a/src/transforms/normalize.js +++ b/src/transforms/normalize.js @@ -3,16 +3,37 @@ import {defined} from "../defined.js"; import {percentile, take} from "../options.js"; import {mapX, mapY} from "./map.js"; +/** + * ```js + * Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) + * ``` + * + * Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), but applies the normalize map method with the given *basis*. + */ export function normalizeX(basis, options) { if (arguments.length === 1) ({basis, ...options} = basis); return mapX(normalize(basis), options); } +/** + * ```js + * Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) + * ``` + * + * Like [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), but applies the normalize map method with the given *basis*. + */ export function normalizeY(basis, options) { if (arguments.length === 1) ({basis, ...options} = basis); return mapY(normalize(basis), options); } +/** + * ```js + * Plot.map({y: Plot.normalize("first")}, {x: "Date", y: "Close", stroke: "Symbol"}) + * ``` + * + * Returns a normalize map method for the given *basis*, suitable for use with Plot.map. + */ export function normalize(basis) { if (basis === undefined) return normalizeFirst; if (typeof basis === "function") return normalizeBasis((I, S) => basis(take(S, I))); From d8f3b91d072229296ac026aba66a5b5c6c70f0f5 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:07:44 -0400 Subject: [PATCH 41/59] jsdoc select* --- README.md | 16 +++++++++++++ src/transforms/select.js | 49 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/README.md b/README.md index e883e6dc0d..ef14ffd688 100644 --- a/README.md +++ b/README.md @@ -2181,6 +2181,8 @@ Like [Plot.mapY](#plotmapymap-options), but applies the window map method with t The select transform derives a filtered mark index; it does not affect the mark’s data or channels. It is similar to the basic [filter transform](#transforms) except that provides convenient shorthand for pulling a single value out of each series. The data are grouped into series using the *z*, *fill*, or *stroke* channel in the same fashion as the [area](#area) and [line](#line) marks. + + #### Plot.select(*selector*, *options*) Selects the points of each series selected by the *selector*, which can be specified either as a function which receives as input the index of the series, the shorthand “first” or “last”, or as a {*key*: *value*} object with exactly one *key* being the name of a channel and the *value* being a function which receives as input the index of the series and the channel values. The *value* may alternatively be specified as the shorthand “min” and “max” which respectively select the minimum and maximum points for the specified channel. @@ -2213,30 +2215,44 @@ To pick the point in each city with the highest temperature: Plot.select({fill: "max"}, {x: "date", y: "city", fill: "temperature", z: "city"}) ``` + + #### Plot.selectFirst(*options*) Selects the first point of each series according to input order. + + #### Plot.selectLast(*options*) Selects the last point of each series according to input order. + + #### Plot.selectMinX(*options*) Selects the leftmost point of each series. + + #### Plot.selectMinY(*options*) Selects the lowest point of each series. + + #### Plot.selectMaxX(*options*) Selects the rightmost point of each series. + + #### Plot.selectMaxY(*options*) Selects the highest point of each series. + + ### Stack [a stacked area chart of revenue by category](https://observablehq.com/@observablehq/plot-stack) diff --git a/src/transforms/select.js b/src/transforms/select.js index 66c9ebd854..b99cea98c5 100644 --- a/src/transforms/select.js +++ b/src/transforms/select.js @@ -2,6 +2,37 @@ import {greatest, group, least} from "d3"; import {maybeZ, valueof} from "../options.js"; import {basic} from "./basic.js"; +/** + * Selects the points of each series selected by the *selector*, which can be specified either as a function which receives as input the index of the series, the shorthand “first” or “last”, or as a {*key*: *value*} object with exactly one *key* being the name of a channel and the *value* being a function which receives as input the index of the series and the channel values. The *value* may alternatively be specified as the shorthand “min” and “max” which respectively select the minimum and maximum points for the specified channel. + * + * For example, to select the point within each series that is the closest to the median of the *y* channel: + * + * ```js + * Plot.select({ + * y: (I, V) => { + * const median = d3.median(I, i => V[i]); + * const i = d3.least(I, i => Math.abs(V[i] - median)); + * return [i]; + * } + * }, { + * x: "year", + * y: "revenue", + * fill: "format" + * }) + * ``` + * + * To pick three points at random in each series: + * + * ```js + * Plot.select(I => d3.shuffle(I.slice()).slice(0, 3), {z: "year", ...}) + * ``` + * + * To pick the point in each city with the highest temperature: + * + * ```js + * Plot.select({fill: "max"}, {x: "date", y: "city", fill: "temperature", z: "city"}) + * ``` + */ export function select(selector, options = {}) { // If specified selector is a string or function, it’s a selector without an // input channel such as first or last. @@ -39,26 +70,44 @@ function maybeSelector(selector) { throw new Error(`unknown selector: ${selector}`); } +/** + * Selects the first point of each series according to input order. + */ export function selectFirst(options) { return selectChannel(null, selectorFirst, options); } +/** + * Selects the last point of each series according to input order. + */ export function selectLast(options) { return selectChannel(null, selectorLast, options); } +/** + * Selects the leftmost point of each series. + */ export function selectMinX(options) { return selectChannel("x", selectorMin, options); } +/** + * Selects the lowest point of each series. + */ export function selectMinY(options) { return selectChannel("y", selectorMin, options); } +/** + * Selects the rightmost point of each series. + */ export function selectMaxX(options) { return selectChannel("x", selectorMax, options); } +/** + * Selects the highest point of each series. + */ export function selectMaxY(options) { return selectChannel("y", selectorMax, options); } From 1bf6098b9c404f70f0e6ce0cfb0c378a5435ccd0 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:14:46 -0400 Subject: [PATCH 42/59] jsdoc stack* --- README.md | 32 +++++++++++++++++++----- src/transforms/stack.js | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ef14ffd688..9a177e69f0 100644 --- a/README.md +++ b/README.md @@ -2297,7 +2297,9 @@ In addition to the **y1** and **y2** output channels, Plot.stackY computes a **y If two arguments are passed to the stack transform functions below, the stack-specific options (**offset**, **order**, and **reverse**) are pulled exclusively from the first *options* argument, while any channels (*e.g.*, **x**, **y**, and **z**) are pulled from second *options* argument. Options from the second argument that are not consumed by the stack transform will be passed through. Using two arguments is sometimes necessary is disambiguate the option recipient when chaining transforms. -#### Plot.stackY([*stack*, ]*options*) + + +#### Plot.stackY(*stackOptions*, *options*) ```js Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) @@ -2305,7 +2307,10 @@ Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) Creates new channels **y1** and **y2**, obtained by stacking the original **y** channel for data points that share a common **x** (and possibly **z**) value. A new **y** channel is also returned, which lazily computes the middle value of **y1** and **y2**. The input **y** channel defaults to a constant 1, resulting in a count of the data points. The stack options (*offset*, *order*, and *reverse*) may be specified as part of the *options* object, if the only argument, or as a separate *stack* options argument. -#### Plot.stackY1([*stack*, ]*options*) + + + +#### Plot.stackY1(*stackOptions*, *options*) ```js Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) @@ -2313,7 +2318,10 @@ Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. -#### Plot.stackY2([*stack*, ]*options*) + + + +#### Plot.stackX(*stackOptions*, *options*) ```js Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) @@ -2329,7 +2340,10 @@ Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) See Plot.stackY, but with *x* as the input value channel, *y* as the stack index, *x1*, *x2* and *x* as the output channels. -#### Plot.stackX1([*stack*, ]*options*) + + + +#### Plot.stackX1(*stackOptions*, *options*) ```js Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) @@ -2337,7 +2351,10 @@ Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. -#### Plot.stackX2([*stack*, ]*options*) + + + +#### Plot.stackX2(*stackOptions*, *options*) ```js Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) @@ -2345,6 +2362,9 @@ Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. + + + ### Tree [a node-link tree diagram representing a software hierarchy](https://observablehq.com/@observablehq/plot-tree) diff --git a/src/transforms/stack.js b/src/transforms/stack.js index aed8a04573..6659eb35cd 100644 --- a/src/transforms/stack.js +++ b/src/transforms/stack.js @@ -3,6 +3,15 @@ import {ascendingDefined} from "../defined.js"; import {field, column, maybeColumn, maybeZ, mid, range, valueof, maybeZero, one} from "../options.js"; import {basic} from "./basic.js"; +/** + * ```js + * Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) + * ``` + * + * See Plot.stackY, but with *x* as the input value channel, *y* as the stack index, *x1*, *x2* and *x* as the output channels. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackX(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {y1, y = y1, x, ...rest} = options; // note: consumes x! @@ -10,6 +19,15 @@ export function stackX(stackOptions = {}, options = {}) { return {...transform, y1, y: Y, x1, x2, x: mid(x1, x2)}; } +/** + * ```js + * Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) + * ``` + * + * Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackX1(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {y1, y = y1, x} = options; @@ -17,6 +35,15 @@ export function stackX1(stackOptions = {}, options = {}) { return {...transform, y1, y: Y, x: X}; } +/** + * ```js + * Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) + * ``` + * + * Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackX2(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {y1, y = y1, x} = options; @@ -24,6 +51,15 @@ export function stackX2(stackOptions = {}, options = {}) { return {...transform, y1, y: Y, x: X}; } +/** + * ```js + * Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) + * ``` + * + * Creates new channels **y1** and **y2**, obtained by stacking the original **y** channel for data points that share a common **x** (and possibly **z**) value. A new **y** channel is also returned, which lazily computes the middle value of **y1** and **y2**. The input **y** channel defaults to a constant 1, resulting in a count of the data points. The stack options (*offset*, *order*, and *reverse*) may be specified as part of the *options* object, if the only argument, or as a separate *stack* options argument. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackY(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {x1, x = x1, y, ...rest} = options; // note: consumes y! @@ -31,6 +67,15 @@ export function stackY(stackOptions = {}, options = {}) { return {...transform, x1, x: X, y1, y2, y: mid(y1, y2)}; } +/** + * ```js + * Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) + * ``` + * + * Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackY1(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {x1, x = x1, y} = options; @@ -38,6 +83,15 @@ export function stackY1(stackOptions = {}, options = {}) { return {...transform, x1, x: X, y: Y}; } +/** + * ```js + * Plot.stackY2({x: "year", y: "revenue", z: "format", fill: "group"}) + * ``` + * + * Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y2** channel is returned as the **y** channel. This can be used, for example, to draw a line at the top of each stacked area. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#stack + */ export function stackY2(stackOptions = {}, options = {}) { if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); const {x1, x = x1, y} = options; From 3d17a805572df3f962ee500754715181b15f5edb Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:25:24 -0400 Subject: [PATCH 43/59] jsdoc tree, cluster, treeNode, treeLink --- README.md | 26 ++++++++---- src/marks/tree.js | 47 +++++++++++++++++----- src/transforms/stack.js | 8 ++-- src/transforms/tree.js | 88 ++++++++++++++++++++++++++++------------- 4 files changed, 119 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 9a177e69f0..40cea528c8 100644 --- a/README.md +++ b/README.md @@ -2316,7 +2316,7 @@ Creates new channels **y1** and **y2**, obtained by stacking the original **y** Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. +Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. @@ -2349,7 +2349,7 @@ See Plot.stackY, but with *x* as the input value channel, *y* as the stack index Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. +Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. @@ -2360,7 +2360,7 @@ Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** ch Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. +Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. @@ -2419,9 +2419,11 @@ The following options control how the node-link diagram is laid out: The default **treeLayout** implements the Reingold–Tilford “tidy” algorithm based on Buchheim _et al._’s linear time approach. Use [d3.cluster](https://github.com/d3/d3-hierarchy/blob/main/README.md#cluster) instead to align leaf nodes; see also [Plot.cluster](#plotclusterdata-options). If the **treeAnchor** is *left*, the root of the tree will be aligned with the left side of the frame; if **treeAnchor** is *right*, the root of the tree will be aligned with the right side of the frame; use the **insetLeft** and **insetRight** [scale options](#scale-options) if horizontal padding is desired, say to make room for labels. If the **treeSort** option is not null, it is typically a function that is passed two nodes in the hierarchy and compares them, similar to [_array_.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort); see [d3-hierarchy’s _node_.sort](https://github.com/d3/d3-hierarchy/blob/main/README.md#node_sort) for more. The **treeSort** option can also be specified as a string, in which case it refers either to a named column in data, or if it starts with “node:”, a node value (see below). If the **treeSeparation** is not null, it is a function that is passed two nodes in the hierarchy and returns the desired (relative) amount of separation; see [d3-hierarchy’s _tree_.separation](https://github.com/d3/d3-hierarchy/blob/main/README.md#tree_separation) for more. By default, non-siblings are at least twice as far apart as siblings. + + #### Plot.treeNode(*options*) -Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](#dot), [text](#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). +Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), [text](https://github.com/observablehq/plot/blob/main/README.md#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). The treeNode transform will derive output columns for any *options* that have one of the following named node values: @@ -2433,9 +2435,11 @@ The treeNode transform will derive output columns for any *options* that have on In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each node in the tree. + + #### Plot.treeLink(*options*) -Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](#link), [arrow](#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). +Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](https://github.com/observablehq/plot/blob/main/README.md#link), [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). The treeLink transform will derive output columns for any *options* that have one of the following named link values: @@ -2451,9 +2455,11 @@ The treeLink transform will derive output columns for any *options* that have on In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each child node in the tree; likewise if any option value is specified as an object with a **link** method, a derived output column will be generated by invoking the **link** method for each link in the tree, being passed two node arguments, the child and the parent. + + #### Plot.tree(*data*, *options*) -A convenience compound mark for rendering a tree diagram, including a [link](#link) to render links from parent to child, an optional [dot](#dot) for nodes, and a [text](#text) for node labels. The link mark uses the [treeLink transform](#plottreelinkoptions), while the dot and text marks use the [treeNode transform](#plottreenodeoptions). The following options are supported: +A convenience compound mark for rendering a tree diagram, including a [link](https://github.com/observablehq/plot/blob/main/README.md#link) to render links from parent to child, an optional [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for nodes, and a [text](https://github.com/observablehq/plot/blob/main/README.md#text) for node labels. The link mark uses the [treeLink transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), while the dot and text marks use the [treeNode transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). The following options are supported: * **fill** - the dot and text fill color; defaults to *node:internal* * **stroke** - the link stroke color; inherits **fill** by default @@ -2476,9 +2482,13 @@ A convenience compound mark for rendering a tree diagram, including a [link](#li Any additional *options* are passed through to the constituent link, dot, and text marks and their corresponding treeLink or treeNode transform. + + #### Plot.cluster(*data*, *options*) -Like Plot.tree, except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. +Like [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. + + ### Custom transforms diff --git a/src/marks/tree.js b/src/marks/tree.js index 09b41971ac..257378be86 100644 --- a/src/marks/tree.js +++ b/src/marks/tree.js @@ -6,9 +6,32 @@ import {dot} from "./dot.js"; import {link} from "./link.js"; import {text} from "./text.js"; -export function tree( - data, - { +/** + * A convenience compound mark for rendering a tree diagram, including a [link](https://github.com/observablehq/plot/blob/main/README.md#link) to render links from parent to child, an optional [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for nodes, and a [text](https://github.com/observablehq/plot/blob/main/README.md#text) for node labels. The link mark uses the [treeLink transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), while the dot and text marks use the [treeNode transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). The following options are supported: + * + * * **fill** - the dot and text fill color; defaults to *node:internal* + * * **stroke** - the link stroke color; inherits **fill** by default + * * **strokeWidth** - the link stroke width + * * **strokeOpacity** - the link stroke opacity + * * **strokeLinejoin** - the link stroke linejoin + * * **strokeLinecap** - the link stroke linecap + * * **strokeMiterlimit** - the link stroke miter limit + * * **strokeDasharray** - the link stroke dash array + * * **strokeDashoffset** - the link stroke dash offset + * * **marker** - the link start and end marker + * * **markerStart** - the link start marker + * * **markerEnd** - the link end marker + * * **dot** - if true, whether to render a dot; defaults to false if no link marker + * * **title** - the text and dot title; defaults to *node:path* + * * **text** - the text label; defaults to *node:name* + * * **textStroke** - the text stroke; defaults to *white* + * * **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 if right-anchored + * * **dy** - the text vertical offset; defaults to 0 + * + * Any additional *options* are passed through to the constituent link, dot, and text marks and their corresponding treeLink or treeNode transform. + */ +export function tree(data, options = {}) { + let { fill, stroke, strokeWidth, @@ -27,10 +50,9 @@ export function tree( title = "node:path", dx, dy, - ...options - } = {} -) { - if (dx === undefined) dx = maybeTreeAnchor(options.treeAnchor).dx; + ...remainingOptions + } = options; + if (dx === undefined) dx = maybeTreeAnchor(remainingOptions.treeAnchor).dx; return marks( link( data, @@ -45,10 +67,12 @@ export function tree( strokeMiterlimit, strokeDasharray, strokeDashoffset, - ...options + ...remainingOptions }) ), - dotDot ? dot(data, treeNode({fill: fill === undefined ? "node:internal" : fill, title, ...options})) : null, + dotDot + ? dot(data, treeNode({fill: fill === undefined ? "node:internal" : fill, title, ...remainingOptions})) + : null, textText != null ? text( data, @@ -59,13 +83,16 @@ export function tree( dx, dy, title, - ...options + ...remainingOptions }) ) : null ); } +/** + * Like [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. + */ export function cluster(data, options) { return tree(data, {...options, treeLayout: Cluster}); } diff --git a/src/transforms/stack.js b/src/transforms/stack.js index 6659eb35cd..3bc41420e8 100644 --- a/src/transforms/stack.js +++ b/src/transforms/stack.js @@ -24,7 +24,7 @@ export function stackX(stackOptions = {}, options = {}) { * Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. + * Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -40,7 +40,7 @@ export function stackX1(stackOptions = {}, options = {}) { * Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. + * Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -72,7 +72,7 @@ export function stackY(stackOptions = {}, options = {}) { * Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. + * Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -88,7 +88,7 @@ export function stackY1(stackOptions = {}, options = {}) { * Plot.stackY2({x: "year", y: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y2** channel is returned as the **y** channel. This can be used, for example, to draw a line at the top of each stacked area. + * Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y2** channel is returned as the **y** channel. This can be used, for example, to draw a line at the top of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ diff --git a/src/transforms/tree.js b/src/transforms/tree.js index 9ff9871ba1..02d17338f4 100644 --- a/src/transforms/tree.js +++ b/src/transforms/tree.js @@ -3,28 +3,42 @@ import {ascendingDefined} from "../defined.js"; import {column, identity, isObject, one, valueof} from "../options.js"; import {basic} from "./basic.js"; -export function treeNode({ - path = identity, // the delimited path - delimiter, // how the path is separated - frameAnchor, - treeLayout = tree, - treeSort, - treeSeparation, - treeAnchor, - ...options -} = {}) { +/** + * Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), [text](https://github.com/observablehq/plot/blob/main/README.md#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). + * + * The treeNode transform will derive output columns for any *options* that have one of the following named node values: + * + * * *node:name* - the node’s name (the last part of its path) + * * *node:path* - the node’s full, normalized, slash-separated path + * * *node:internal* - true if the node is internal, or false for leaves + * * *node:depth* - the distance from the node to the root + * * *node:height* - the distance from the node to its deepest descendant + * + * In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each node in the tree. + */ +export function treeNode(options = {}) { + let { + path = identity, // the delimited path + delimiter, // how the path is separated + frameAnchor, + treeLayout = tree, + treeSort, + treeSeparation, + treeAnchor, + ...remainingOptions + } = options; treeAnchor = maybeTreeAnchor(treeAnchor); treeSort = maybeTreeSort(treeSort); if (frameAnchor === undefined) frameAnchor = treeAnchor.frameAnchor; const normalize = normalizer(delimiter); - const outputs = treeOutputs(options, maybeNodeValue); + const outputs = treeOutputs(remainingOptions, maybeNodeValue); const [X, setX] = column(); const [Y, setY] = column(); return { x: X, y: Y, frameAnchor, - ...basic(options, (data, facets) => { + ...basic(remainingOptions, (data, facets) => { const P = normalize(valueof(data, path)); const X = setX([]); const Y = setY([]); @@ -55,24 +69,42 @@ export function treeNode({ }; } -export function treeLink({ - path = identity, // the delimited path - delimiter, // how the path is separated - curve = "bump-x", - stroke = "#555", - strokeWidth = 1.5, - strokeOpacity = 0.5, - treeLayout = tree, - treeSort, - treeSeparation, - treeAnchor, - ...options -} = {}) { +/** + * Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](https://github.com/observablehq/plot/blob/main/README.md#link), [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). + * + * The treeLink transform will derive output columns for any *options* that have one of the following named link values: + * + * * *node:name* - the child node’s name (the last part of its path) + * * *node:path* - the child node’s full, normalized, slash-separated path + * * *node:internal* - true if the child node is internal, or false for leaves + * * *node:depth* - the distance from the child node to the root + * * *node:height* - the distance from the child node to its deepest descendant + * * *parent:name* - the parent node’s name (the last part of its path) + * * *parent:path* - the parent node’s full, normalized, slash-separated path + * * *parent:depth* - the distance from the parent node to the root + * * *parent:height* - the distance from the parent node to its deepest descendant + * + * In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each child node in the tree; likewise if any option value is specified as an object with a **link** method, a derived output column will be generated by invoking the **link** method for each link in the tree, being passed two node arguments, the child and the parent. + */ +export function treeLink(options = {}) { + let { + path = identity, // the delimited path + delimiter, // how the path is separated + curve = "bump-x", + stroke = "#555", + strokeWidth = 1.5, + strokeOpacity = 0.5, + treeLayout = tree, + treeSort, + treeSeparation, + treeAnchor, + ...remainingOptions + } = options; treeAnchor = maybeTreeAnchor(treeAnchor); treeSort = maybeTreeSort(treeSort); - options = {curve, stroke, strokeWidth, strokeOpacity, ...options}; + remainingOptions = {curve, stroke, strokeWidth, strokeOpacity, ...remainingOptions}; const normalize = normalizer(delimiter); - const outputs = treeOutputs(options, maybeLinkValue); + const outputs = treeOutputs(remainingOptions, maybeLinkValue); const [X1, setX1] = column(); const [X2, setX2] = column(); const [Y1, setY1] = column(); @@ -82,7 +114,7 @@ export function treeLink({ x2: X2, y1: Y1, y2: Y2, - ...basic(options, (data, facets) => { + ...basic(remainingOptions, (data, facets) => { const P = normalize(valueof(data, path)); const X1 = setX1([]); const X2 = setX2([]); From 28aaa70cda56eec3ecf5735bf7902dd6ccf00e11 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:29:34 -0400 Subject: [PATCH 44/59] jsdoc dodgeX, dodgeY --- README.md | 12 ++++++++++-- src/transforms/dodge.js | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 40cea528c8..328ea2c567 100644 --- a/README.md +++ b/README.md @@ -2548,7 +2548,9 @@ The **anchor** option may one of *middle*, *right*, and *left* for dodgeX, and o The dodge layout is highly dependent on the input data order: the circles placed first will be closest to the dodge anchor. When using the dodge layout with circles of varying radius, the data is sorted by descending radius by default; you can disable this behavior by setting the **sort** or **reverse** option. -#### Plot.dodgeY([*dodgeOptions*, ]*options*) + + +#### Plot.dodgeY(*dodgeOptions*, *options*) ```js Plot.dodgeY({x: "date"}) @@ -2556,7 +2558,10 @@ Plot.dodgeY({x: "date"}) Given marks arranged along the *x* axis, the dodgeY transform piles them vertically by defining a *y* position channel that avoids overlapping. The *x* position channel is unchanged. -#### Plot.dodgeX([*dodgeOptions*, ]*options*) + + + +#### Plot.dodgeX(*dodgeOptions*, *options*) ```js Plot.dodgeX({y: "value"}) @@ -2564,6 +2569,9 @@ Plot.dodgeX({y: "value"}) Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. + + + ### Hexbin [a chart showing the inverse relationship of fuel economy to engine displacement, and the positive correlation of engine displacement and weight; hexagonal bins of varying size represent the number of cars at each location, while color encodes the mean weight of nearby cars](https://observablehq.com/@observablehq/plot-hexbin) diff --git a/src/transforms/dodge.js b/src/transforms/dodge.js index a9905591c6..87f189a16a 100644 --- a/src/transforms/dodge.js +++ b/src/transforms/dodge.js @@ -15,6 +15,15 @@ function maybeAnchor(anchor) { return typeof anchor === "string" ? {anchor} : anchor; } +/** + * ```js + * Plot.dodgeX({y: "value"}) + * ``` + * + * Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#dodge + */ export function dodgeX(dodgeOptions = {}, options = {}) { if (arguments.length === 1) [dodgeOptions, options] = mergeOptions(dodgeOptions); let {anchor = "left", padding = 1} = maybeAnchor(dodgeOptions); @@ -34,6 +43,15 @@ export function dodgeX(dodgeOptions = {}, options = {}) { return dodge("x", "y", anchor, number(padding), options); } +/** + * ```js + * Plot.dodgeY({x: "date"}) + * ``` + * + * Given marks arranged along the *x* axis, the dodgeY transform piles them vertically by defining a *y* position channel that avoids overlapping. The *x* position channel is unchanged. + * + * @link https://github.com/observablehq/plot/blob/main/README.md#dodge + */ export function dodgeY(dodgeOptions = {}, options = {}) { if (arguments.length === 1) [dodgeOptions, options] = mergeOptions(dodgeOptions); let {anchor = "bottom", padding = 1} = maybeAnchor(dodgeOptions); From 9addc766ddb5d73e1cdc85b5acf9d6cb253cee06 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:33:17 -0400 Subject: [PATCH 45/59] jsdoc hexbin --- README.md | 8 +++++-- src/transforms/hexbin.js | 46 +++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 328ea2c567..030f3d4de4 100644 --- a/README.md +++ b/README.md @@ -2578,9 +2578,11 @@ Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position [Source](./src/transforms/hexbin.js) · [Examples](https://observablehq.com/@observablehq/plot-hexbin) · The hexbin transform can be applied to any mark that consumes *x* and *y*, such as the [dot](#dot), [image](#image), [text](#text), and [vector](#vector) marks. It aggregates values into hexagonal bins of the given **binWidth** (in pixels) and computes new position channels *x* and *y* as the centers of each bin. It can also create new channels by applying a specified reducer to each bin, such as the *count* of elements in the bin. + + #### Plot.hexbin(*outputs*, *options*) -Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. +Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](https://github.com/observablehq/plot/blob/main/README.md#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. The following aggregation methods are supported: @@ -2603,7 +2605,9 @@ The following aggregation methods are supported: * a function to be passed the array of values for each bin and the extent of the bin * an object with a *reduce* method -See also the [hexgrid](#hexgrid) mark. +See also the [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) mark. + + ### Custom initializers diff --git a/src/transforms/hexbin.js b/src/transforms/hexbin.js index 3e00224a4c..3fc073a3fc 100644 --- a/src/transforms/hexbin.js +++ b/src/transforms/hexbin.js @@ -12,25 +12,51 @@ import {hasOutput, maybeGroup, maybeOutputs, maybeSubgroup} from "./group.js"; export const ox = 0.5, oy = 0; -// TODO filter e.g. to show empty hexbins? -// TODO disallow x, x1, x2, y, y1, y2 reducers? -export function hexbin(outputs = {fill: "count"}, inputs = {}) { - let {binWidth, ...options} = inputs; +/** + * Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](https://github.com/observablehq/plot/blob/main/README.md#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. + * + * The following aggregation methods are supported: + * + * * *first* - the first value, in input order + * * *last* - the last value, in input order + * * *count* - the number of elements (frequency) + * * *distinct* - the number of distinct values + * * *sum* - the sum of values + * * *proportion* - the sum proportional to the overall total (weighted frequency) + * * *proportion-facet* - the sum proportional to the facet total + * * *min* - the minimum value + * * *min-index* - the zero-based index of the minimum value + * * *max* - the maximum value + * * *max-index* - the zero-based index of the maximum value + * * *mean* - the mean value (average) + * * *median* - the median value + * * *deviation* - the standard deviation + * * *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) + * * *mode* - the value with the most occurrences + * * a function to be passed the array of values for each bin and the extent of the bin + * * an object with a *reduce* method + * + * See also the [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) mark. + */ +export function hexbin(outputs = {fill: "count"}, options = {}) { + // TODO filter e.g. to show empty hexbins? + // TODO disallow x, x1, x2, y, y1, y2 reducers? + let {binWidth, ...remainingOptions} = options; binWidth = binWidth === undefined ? 20 : number(binWidth); - outputs = maybeOutputs(outputs, options); + outputs = maybeOutputs(outputs, remainingOptions); // A fill output means a fill channel, and hence the stroke should default to // none (assuming a mark that defaults to fill and no stroke, such as dot). // Note that it’s safe to mutate options here because we just created it with // the rest operator above. - const {z, fill, stroke} = options; - if (stroke === undefined && isNoneish(fill) && hasOutput(outputs, "fill")) options.stroke = "none"; + const {z, fill, stroke} = remainingOptions; + if (stroke === undefined && isNoneish(fill) && hasOutput(outputs, "fill")) remainingOptions.stroke = "none"; // Populate default values for the r and symbol options, as appropriate. - if (options.symbol === undefined) options.symbol = "hexagon"; - if (options.r === undefined && !hasOutput(outputs, "r")) options.r = binWidth / 2; + if (remainingOptions.symbol === undefined) remainingOptions.symbol = "hexagon"; + if (remainingOptions.r === undefined && !hasOutput(outputs, "r")) remainingOptions.r = binWidth / 2; - return initializer(options, (data, facets, {x: X, y: Y, z: Z, fill: F, stroke: S, symbol: Q}, scales) => { + return initializer(remainingOptions, (data, facets, {x: X, y: Y, z: Z, fill: F, stroke: S, symbol: Q}, scales) => { if (X === undefined) throw new Error("missing channel: x"); if (Y === undefined) throw new Error("missing channel: y"); From e1177b24b154ebdad93b560efca2932599c4460a Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:39:45 -0400 Subject: [PATCH 46/59] jsdoc initializer --- README.md | 4 ++++ src/transforms/basic.js | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 030f3d4de4..608e9e9dd2 100644 --- a/README.md +++ b/README.md @@ -2615,10 +2615,14 @@ You can specify a custom initializer by specifying a function as the mark **init If an initializer desires a channel that is not supported by the downstream mark, additional channels can be declared using the mark **channels** option. + + #### Plot.initializer(*options*, *initializer*) This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. + + ## Curves A curve defines how to turn a discrete representation of a line as a sequence of points [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] into a continuous path; *i.e.*, how to interpolate between points. Curves are used by the [line](#line), [area](#area), and [link](#link) mark, and are implemented by [d3-shape](https://github.com/d3/d3-shape/blob/main/README.md#curves). diff --git a/src/transforms/basic.js b/src/transforms/basic.js index 9ba97b8469..3562f12d20 100644 --- a/src/transforms/basic.js +++ b/src/transforms/basic.js @@ -23,9 +23,13 @@ export function basic(options = {}, transform) { }; } -// If both i1 and i2 are defined, returns a composite initializer that first -// applies i1 and then applies i2. -export function initializer({filter: f1, sort: s1, reverse: r1, initializer: i1, ...options} = {}, i2) { +/** + * This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. + */ +export function initializer(options = {}, initializer) { + let {filter: f1, sort: s1, reverse: r1, initializer: i1, ...remainingOptions} = options; + // If both i1 and i2 are defined, returns a composite initializer that first + // applies i1 and then applies i2. if (i1 === undefined) { // explicit initializer overrides filter, sort, and reverse if (f1 != null) i1 = filterTransform(f1); @@ -33,8 +37,8 @@ export function initializer({filter: f1, sort: s1, reverse: r1, initializer: i1, if (r1) i1 = composeInitializer(i1, reverseTransform); } return { - ...options, - initializer: composeInitializer(i1, i2) + ...remainingOptions, + initializer: composeInitializer(i1, initializer) }; } From e79a04d6de5fb224da51e1b2a3f1102eaef6ded5 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:45:27 -0400 Subject: [PATCH 47/59] jsdoc format* --- README.md | 12 ++++++++++-- src/format.ts | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 608e9e9dd2..0b37bb8ff0 100644 --- a/README.md +++ b/README.md @@ -2683,6 +2683,8 @@ The primary color of a marker is inherited from the *stroke* of the associated m These helper functions are provided for use as a *scale*.tickFormat [axis option](#position-options), as the text option for [Plot.text](#plottextdata-options), or for general use. See also [d3-format](https://github.com/d3/d3-format), [d3-time-format](https://github.com/d3/d3-time-format), and JavaScript’s built-in [date formatting](https://observablehq.com/@mbostock/date-formatting) and [number formatting](https://observablehq.com/@mbostock/number-formatting). + + #### Plot.formatIsoDate(*date*) ```js @@ -2691,7 +2693,9 @@ Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01" Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the given *date* is not valid, returns `"Invalid Date"`. -#### Plot.formatWeekday(*locale*, *format*) + + +#### Plot.formatWeekday(*locale*, *weekday*) ```js Plot.formatWeekday("es-MX", "long")(0) // "domingo" @@ -2699,7 +2703,9 @@ Plot.formatWeekday("es-MX", "long")(0) // "domingo" Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. -#### Plot.formatMonth(*locale*, *format*) + + +#### Plot.formatMonth(*locale*, *month*) ```js Plot.formatMonth("es-MX", "long")(0) // "enero" @@ -2707,6 +2713,8 @@ Plot.formatMonth("es-MX", "long")(0) // "enero" Returns a function that formats a given month number (from 0 = January to 11 = December) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [month format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to *short*. + + ## Accessibility Plot supports several [ARIA properties](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) to help build the accessibility tree. The accessibility tree is consumed by various assistive technology such as screen readers and browser add-ons to make web contents and web applications more accessible to people with disabilities. It can be inspected in the browser’s inspector. diff --git a/src/format.ts b/src/format.ts index 1be9b61482..4163cb8f66 100644 --- a/src/format.ts +++ b/src/format.ts @@ -20,6 +20,13 @@ export function formatNumber(locale = "en-US"): (value: any) => string | undefin return (i: any) => (i != null && !isNaN(i) ? format.format(i) : undefined); } +/** + * ```js + * Plot.formatMonth("es-MX", "long")(0) // "enero" + * ``` + * + * Returns a function that formats a given month number (from 0 = January to 11 = December) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [month format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to *short*. + */ export function formatMonth( locale = "en-US", month: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined = "short" @@ -29,12 +36,26 @@ export function formatMonth( i != null && !isNaN((i = +new Date(Date.UTC(2000, +i)))) ? format.format(i) : undefined; } +/** + * ```js + * Plot.formatWeekday("es-MX", "long")(0) // "domingo" + * ``` + * + * Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. + */ export function formatWeekday(locale = "en-US", weekday: "long" | "short" | "narrow" | undefined = "short") { const format = weekdayFormat(locale, weekday); return (i: Date | number | null | undefined) => i != null && !isNaN((i = +new Date(Date.UTC(2001, 0, +i)))) ? format.format(i) : undefined; } +/** + * ```js + * Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01" + * ``` + * + * Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the given *date* is not valid, returns `"Invalid Date"`. + */ export function formatIsoDate(date: Date): string { return isoFormat(date, "Invalid Date"); } From 5b3f935e90c9c399009f4172d8bc24ecb6819145 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Tue, 23 Aug 2022 17:51:25 -0400 Subject: [PATCH 48/59] fix mapX, mapY --- README.md | 4 ++-- src/transforms/map.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0b37bb8ff0..8c18ea7ce9 100644 --- a/README.md +++ b/README.md @@ -2097,7 +2097,7 @@ Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for ea -#### Plot.mapX(*map*, *options*) +#### Plot.mapX(*mapping*, *options*) ```js Plot.mapX("cumsum", {x: d3.randomNormal()}) @@ -2108,7 +2108,7 @@ Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores -#### Plot.mapY(*map*, *options*) +#### Plot.mapY(*mapping*, *options*) ```js Plot.mapY("cumsum", {y: d3.randomNormal()}) diff --git a/src/transforms/map.js b/src/transforms/map.js index d4d30afc57..a6a787b125 100644 --- a/src/transforms/map.js +++ b/src/transforms/map.js @@ -11,9 +11,9 @@ import {basic} from "./basic.js"; * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ -export function mapX(map, options = {}) { +export function mapX(mapping, options = {}) { return map( - Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, map])), + Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, mapping])), options ); } @@ -27,9 +27,9 @@ export function mapX(map, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ -export function mapY(map, options = {}) { +export function mapY(mapping, options = {}) { return map( - Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, map])), + Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, mapping])), options ); } From dac50b54515fd37ea1b97b9ba026c13cbce19b54 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Wed, 24 Aug 2022 17:01:08 -0400 Subject: [PATCH 49/59] revert d.ts related changes for another PR --- .gitignore | 1 - src/format.ts | 4 ++-- tsconfig.json | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 8df33ba803..a4682aa5a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store dist/ -types/ node_modules/ test/output/*-changed.svg test/output/*-changed.html diff --git a/src/format.ts b/src/format.ts index 4163cb8f66..7ca5e69040 100644 --- a/src/format.ts +++ b/src/format.ts @@ -8,11 +8,11 @@ const numberFormat = memoize1( ); const monthFormat = memoize1( (locale: string | string[] | undefined, month: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined) => - new Intl.DateTimeFormat(locale, {timeZone: "UTC", ...(month && {month})}) + new Intl.DateTimeFormat(locale, {timeZone: "UTC", month}) ); const weekdayFormat = memoize1( (locale: string | string[] | undefined, weekday: "long" | "short" | "narrow" | undefined) => - new Intl.DateTimeFormat(locale, {timeZone: "UTC", ...(weekday && {weekday})}) + new Intl.DateTimeFormat(locale, {timeZone: "UTC", weekday}) ); export function formatNumber(locale = "en-US"): (value: any) => string | undefined { diff --git a/tsconfig.json b/tsconfig.json index f7a24bffa7..e3bf89a670 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,8 +5,6 @@ "lib": ["esnext", "dom"], "strict": true, "stripInternal": true, - "declaration": true, - "declarationDir": "types", "outDir": "dist", "allowJs": true, "resolveJsonModule": true, From f5204ea3b36b53f11786c1dddc4658d2137ea89f Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Thu, 25 Aug 2022 00:00:44 -0400 Subject: [PATCH 50/59] Hard-wrap JSDoc comments --- README.md | 1502 +++++++++++++++++++++++++-------- src/format.ts | 18 +- src/legends.js | 10 +- src/marks/area.js | 54 +- src/marks/bar.js | 53 +- src/marks/box.js | 10 +- src/marks/cell.js | 19 +- src/marks/delaunay.js | 47 +- src/marks/density.js | 19 +- src/marks/dot.js | 33 +- src/marks/hexgrid.js | 4 +- src/marks/image.js | 5 +- src/marks/line.js | 39 +- src/marks/linearRegression.js | 6 +- src/marks/rect.js | 18 +- src/marks/rule.js | 39 +- src/marks/text.js | 25 +- src/marks/tick.js | 15 +- src/marks/tree.js | 26 +- src/marks/vector.js | 15 +- src/options.js | 33 +- src/plot.js | 290 +++++-- src/scales.js | 536 ++++++++---- src/transforms/basic.js | 26 +- src/transforms/bin.js | 9 +- src/transforms/dodge.js | 8 +- src/transforms/group.js | 7 +- src/transforms/hexbin.js | 24 +- src/transforms/map.js | 11 +- src/transforms/normalize.js | 11 +- src/transforms/select.js | 12 +- src/transforms/stack.js | 31 +- src/transforms/tree.js | 41 +- 33 files changed, 2340 insertions(+), 656 deletions(-) diff --git a/README.md b/README.md index 8c18ea7ce9..e561049282 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,24 @@ See also our [Plot + React example](https://github.com/observablehq/plot-create- ## Plot.plot(*options*) -Renders a new plot given the specified *options* and returns the corresponding SVG or HTML figure element. All *options* are optional. +Renders a new plot given the specified *options* and returns the +corresponding SVG or HTML figure element. All *options* are optional. ### Mark options -The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). +The **marks** option specifies an array of +[marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to +render. Each mark has its own data and options; see the respective mark type +(*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) +or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for +which mark options are supported. Each mark may be a nested array of marks, +allowing composition. Marks may also be a function which returns an SVG +element, if you wish to insert some arbitrary content into your plot. And +marks may be null or undefined, which produce no output; this is useful for +showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn +in *z* order, last on top. For example, here a single rule at *y* = 0 is +drawn on top of blue bars for the [*alphabet* +dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). ```js Plot.plot({ @@ -81,7 +94,8 @@ Plot.plot({ ### Layout options -These options determine the overall layout of the plot; all are specified as numbers in pixels: +These options determine the overall layout of the plot; all are specified as +numbers in pixels: * **marginTop** - the top margin * **marginRight** - the right margin @@ -91,11 +105,39 @@ These options determine the overall layout of the plot; all are specified as num * **width** - the outer width of the plot (including margins) * **height** - the outer height of the plot (including margins) -The default **width** is 640. On Observable, the width can be set to the [standard width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to make responsive plots. The default **height** is chosen automatically based on the plot’s associated scales; for example, if *y* is linear and there is no *fy* scale, it might be 396. - -The default margins depend on the plot’s axes: for example, **marginTop** and **marginBottom** are at least 30 if there is a corresponding top or bottom *x* axis, and **marginLeft** and **marginRight** are at least 40 if there is a corresponding left or right *y* axis. For simplicity’s sake and for consistent layout across plots, margins are not automatically sized to make room for tick labels; instead, shorten your tick labels or increase the margins as needed. (In the future, margins may be specified indirectly via a scale property to make it easier to reorient axes without adjusting margins; see [#210](https://github.com/observablehq/plot/issues/210).) - -The **style** option allows custom styles to override Plot’s defaults. It may be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in the same fashion as assigning [*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as assigning [*element*.style properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). Note that unitless numbers ([quirky lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such as `{padding: 20}` may not supported by some browsers; you should instead specify a string with units such as `{padding: "20px"}`. By default, the returned plot has a white background, a max-width of 100%, and the system-ui font. Plot’s marks and axes default to [currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), meaning that they will inherit the surrounding content’s color. For example, a dark theme: +The default **width** is 640. On Observable, the width can be set to the +[standard +width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to +make responsive plots. The default **height** is chosen automatically based +on the plot’s associated scales; for example, if *y* is linear and there is +no *fy* scale, it might be 396. + +The default margins depend on the plot’s axes: for example, **marginTop** and +**marginBottom** are at least 30 if there is a corresponding top or bottom +*x* axis, and **marginLeft** and **marginRight** are at least 40 if there is +a corresponding left or right *y* axis. For simplicity’s sake and for +consistent layout across plots, margins are not automatically sized to make +room for tick labels; instead, shorten your tick labels or increase the +margins as needed. (In the future, margins may be specified indirectly via a +scale property to make it easier to reorient axes without adjusting margins; +see [#210](https://github.com/observablehq/plot/issues/210).) + +The **style** option allows custom styles to override Plot’s defaults. It may +be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in +the same fashion as assigning +[*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) +or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as +assigning [*element*.style +properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). +Note that unitless numbers ([quirky +lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such +as `{padding: 20}` may not supported by some browsers; you should instead +specify a string with units such as `{padding: "20px"}`. By default, the +returned plot has a white background, a max-width of 100%, and the system-ui +font. Plot’s marks and axes default to +[currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), +meaning that they will inherit the surrounding content’s color. For example, +a dark theme: ```js Plot.plot({ @@ -107,7 +149,11 @@ Plot.plot({ }) ``` -If a **caption** is specified, Plot.plot wraps the generated SVG element in an HTML figure element with a figcaption, returning the figure. To specify an HTML caption, consider using the [`html` tagged template literal](http://github.com/observablehq/htl); otherwise, the specified string represents text that will be escaped as needed. +If a **caption** is specified, Plot.plot wraps the generated SVG element in +an HTML figure element with a figcaption, returning the figure. To specify an +HTML caption, consider using the [`html` tagged template +literal](http://github.com/observablehq/htl); otherwise, the specified string +represents text that will be escaped as needed. ```js Plot.plot({ @@ -116,13 +162,26 @@ Plot.plot({ }) ``` -The generated SVG element has a random class name which applies a default stylesheet. Use the top-level **className** option to specify that class name. +The generated SVG element has a random class name which applies a default +stylesheet. Use the top-level **className** option to specify that class +name. -The **document** option specifies the [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to create plot elements. It defaults to window.document, but can be changed to another document, say when using a virtual DOM library for server-side rendering in Node. +The **document** option specifies the +[document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to +create plot elements. It defaults to window.document, but can be changed to +another document, say when using a virtual DOM library for server-side +rendering in Node. ### Scale options -Plot passes data through [scales](https://observablehq.com/@observablehq/plot-scales) as needed before rendering marks. A scale maps abstract values such as time or temperature to visual values such as position or color. Within a given plot, marks share scales. For example, if a plot has two Plot.line marks, both share the same *x* and *y* scales for a consistent representation of data. (Plot does not currently support dual-axis charts, which are [not advised](https://blog.datawrapper.de/dualaxis/).) +Plot passes data through +[scales](https://observablehq.com/@observablehq/plot-scales) as needed before +rendering marks. A scale maps abstract values such as time or temperature to +visual values such as position or color. Within a given plot, marks share +scales. For example, if a plot has two Plot.line marks, both share the same +*x* and *y* scales for a consistent representation of data. (Plot does not +currently support dual-axis charts, which are [not +advised](https://blog.datawrapper.de/dualaxis/).) ```js Plot.plot({ @@ -133,15 +192,18 @@ Plot.plot({ }) ``` -Each scale’s options are specified as a nested options object with the corresponding scale name within the top-level plot *options*: +Each scale’s options are specified as a nested options object with the +corresponding scale name within the top-level plot *options*: * **x** - horizontal position * **y** - vertical position * **r** - radius (size) * **color** - fill or stroke * **opacity** - fill or stroke opacity -* **length** - linear length (for [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) -* **symbol** - categorical symbol (for [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) +* **length** - linear length (for + [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) +* **symbol** - categorical symbol (for + [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) For example, to set the domain for the *x* and *y* scales: @@ -156,59 +218,171 @@ Plot.plot({ }) ``` -Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) or [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). - -You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. - -For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: +Plot supports many scale types. Some scale types are for quantitative data: +values that can be added or subtracted, such as temperature or time. Other +scale types are for ordinal or categorical data: unquantifiable values that +can only be ordered, such as t-shirt sizes, or values with no inherent order +that can only be tested for equality, such as types of fruit. Some scale +types are further intended for specific visual encodings: for example, as +[position](https://github.com/observablehq/plot/blob/main/README.md#position-options) +or +[color](https://github.com/observablehq/plot/blob/main/README.md#color-options). + +You can set the scale type explicitly via the *scale*.**type** option, though +typically the scale type is inferred automatically. Some marks mandate a +particular scale type: for example, +[Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) +requires that the *x* scale is a *band* scale. Some scales have a default +type: for example, the *radius* scale defaults to *sqrt* and the *opacity* +scale defaults to *linear*. Most often, the scale type is inferred from +associated data, pulled either from the domain (if specified) or from +associated channels. A *color* scale defaults to *identity* if no range or +scheme is specified and all associated defined values are valid CSS color +strings. Otherwise, strings and booleans imply an ordinal scale; dates imply +a UTC scale; and anything else is linear. Unless they represent text, we +recommend explicitly converting strings to more specific types when loading +data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For +simplicity’s sake, Plot assumes that data is consistently typed; type +inference is based solely on the first non-null, non-undefined value. + +For quantitative data (*i.e.* numbers), a mathematical transform may be +applied to the data by changing the scale type: * *linear* (default) - linear transform (translate and scale) * *pow* - power (exponential) transform * *sqrt* - square-root transform (*pow* transform with exponent = 0.5) * *log* - logarithmic transform -* *symlog* - bi-symmetric logarithmic transform per [Webber *et al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) - -The appropriate transform depends on the data’s distribution and what you wish to know. A *sqrt* transform exaggerates differences between small values at the expense of large values; it is a special case of the *pow* transform which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* transform is suitable for comparing orders of magnitude and can only be used when the domain does not include zero. The base defaults to 10 and can be specified with the *scale*.**base** option; note that this only affects the axis ticks and not the scale’s behavior. A *symlog* transform is more elaborate, but works well with wide-range values that include zero; it can be configured with the *scale*.**constant** option (default 1). - -For temporal data (*i.e.* dates), two variants of a *linear* scale are also supported: +* *symlog* - bi-symmetric logarithmic transform per [Webber *et + al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) + +The appropriate transform depends on the data’s distribution and what you +wish to know. A *sqrt* transform exaggerates differences between small values +at the expense of large values; it is a special case of the *pow* transform +which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* +transform is suitable for comparing orders of magnitude and can only be used +when the domain does not include zero. The base defaults to 10 and can be +specified with the *scale*.**base** option; note that this only affects the +axis ticks and not the scale’s behavior. A *symlog* transform is more +elaborate, but works well with wide-range values that include zero; it can be +configured with the *scale*.**constant** option (default 1). + +For temporal data (*i.e.* dates), two variants of a *linear* scale are also +supported: * *utc* (default, recommended) - UTC time * *time* - local time -UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. - -For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](https://github.com/observablehq/plot/blob/main/README.md#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) - -You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. - -Quantitative scales, as well as identity position scales, coerce channel values to numbers; both null and undefined are coerced to NaN. Similarly, time scales coerce channel values to dates; numbers are assumed to be milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). - -A scale’s domain (the extent of its inputs, abstract values) and range (the extent of its outputs, visual values) are typically inferred automatically. You can set them explicitly using these options: - -* *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or categorical values -* *scale*.**range** - typically [*min*, *max*], or an array of ordinal or categorical values -* *scale*.**unknown** - the desired output value (defaults to undefined) for invalid input values -* *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* -* *scale*.**interval** - an interval or time interval (for interval data; see below) - -For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. - -The default range depends on the scale: for [position scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). For [color scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. - -The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. - -For data at regular intervals, such as integer values or daily samples, the *scale*.**interval** option can be used to enforce uniformity. The specified *interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), *interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. The option can also be specified as a number, in which case it will be promoted to a numeric interval with the given step. This option sets the default *scale*.transform to the given interval’s *interval*.floor function. In addition, the default *scale*.domain is an array of uniformly-spaced values spanning the extent of the values associated with the scale. +UTC is recommended over local time as charts in UTC time are guaranteed to +appear consistently to all viewers whereas charts in local time will depend +on the viewer’s time zone. Due to limitations in JavaScript’s Date class, +Plot does not yet support an explicit time zone other than UTC. + +For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the +*point* or *band* [position scale +types](https://github.com/observablehq/plot/blob/main/README.md#position-options). +The *categorical* scale type is also supported; it is equivalent to *ordinal* +except as a [color +scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), +where it provides a different default color scheme. (Since position is +inherently ordinal or even quantitative, categorical data must be assigned an +effective order when represented as position, and hence *categorical* and +*ordinal* may be considered synonymous in context.) + +You can opt-out of a scale using the *identity* scale type. This is useful if +you wish to specify literal colors or pixel positions within a mark channel +rather than relying on the scale to convert abstract values into visual +values. For position scales (*x* and *y*), an *identity* scale is still +quantitative and may produce an axis, yet unlike a *linear* scale the domain +and range are fixed based on the plot layout. + +Quantitative scales, as well as identity position scales, coerce channel +values to numbers; both null and undefined are coerced to NaN. Similarly, +time scales coerce channel values to dates; numbers are assumed to be +milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 +format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). + +A scale’s domain (the extent of its inputs, abstract values) and range (the +extent of its outputs, visual values) are typically inferred automatically. +You can set them explicitly using these options: + +* *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or + categorical values +* *scale*.**range** - typically [*min*, *max*], or an array of ordinal or + categorical values +* *scale*.**unknown** - the desired output value (defaults to undefined) for + invalid input values +* *scale*.**reverse** - reverses the domain (or in somes cases, the range), + say to flip the chart along *x* or *y* +* *scale*.**interval** - an interval or time interval (for interval data; see + below) + +For most quantitative scales, the default domain is the [*min*, *max*] of all +values associated with the scale. For the *radius* and *opacity* scales, the +default domain is [0, *max*] to ensure a meaningful value encoding. For +ordinal scales, the default domain is the set of all distinct values +associated with the scale in natural ascending order; for a different order, +set the domain explicitly or add a [sort +option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) +to an associated mark. For threshold scales, the default domain is [0] to +separate negative and non-negative values. For quantile scales, the default +domain is the set of all defined values associated with the scale. If a scale +is reversed, it is equivalent to setting the domain as [*max*, *min*] instead +of [*min*, *max*]. + +The default range depends on the scale: for [position +scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) +(*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and +margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). +For [color +scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), +there are default color schemes for quantitative, ordinal, and categorical +data. For opacity, the default range is [0, 1]. And for radius, the default +range is designed to produce dots of “reasonable” size assuming a *sqrt* +scale type for accurate area representation: zero maps to zero, the first +quartile maps to a radius of three pixels, and other values are extrapolated. +This convention for radius ensures that if the scale’s data values are all +equal, dots have the default constant radius of three pixels, while if the +data varies, dots will tend to be larger. + +The behavior of the *scale*.**unknown** option depends on the scale type. For +quantitative and temporal scales, the unknown value is used whenever the +input value is undefined, null, or NaN. For ordinal or categorical scales, +the unknown value is returned for any input value outside the domain. For +band or point scales, the unknown option has no effect; it is effectively +always equal to undefined. If the unknown option is set to undefined (the +default), or null or NaN, then the affected input values will be considered +undefined and filtered from the output. + +For data at regular intervals, such as integer values or daily samples, the +*scale*.**interval** option can be used to enforce uniformity. The specified +*interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), +*interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. +The option can also be specified as a number, in which case it will be +promoted to a numeric interval with the given step. This option sets the +default *scale*.transform to the given interval’s *interval*.floor function. +In addition, the default *scale*.domain is an array of uniformly-spaced +values spanning the extent of the values associated with the scale. Quantitative scales can be further customized with additional options: * *scale*.**clamp** - if true, clamp input values to the scale’s domain -* *scale*.**nice** - if true (or a tick count), extend the domain to nice round values +* *scale*.**nice** - if true (or a tick count), extend the domain to nice + round values * *scale*.**zero** - if true, extend the domain to include zero if needed -* *scale*.**percent** - if true, transform proportions in [0, 1] to percentages in [0, 100] +* *scale*.**percent** - if true, transform proportions in [0, 1] to + percentages in [0, 100] -Clamping is typically used in conjunction with setting an explicit domain since if the domain is inferred, no values will be outside the domain. Clamping is useful for focusing on a subset of the data while ensuring that extreme values remain visible, but use caution: clamped values may need an annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and **zero** options are supported as shorthand for setting the respective option on all scales. +Clamping is typically used in conjunction with setting an explicit domain +since if the domain is inferred, no values will be outside the domain. +Clamping is useful for focusing on a subset of the data while ensuring that +extreme values remain visible, but use caution: clamped values may need an +annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and +**zero** options are supported as shorthand for setting the respective option +on all scales. -The *scale*.**transform** option allows you to apply a function to all values before they are passed through the scale. This is convenient for transforming a scale’s data, say to convert to thousands or between temperature units. +The *scale*.**transform** option allows you to apply a function to all values +before they are passed through the scale. This is convenient for transforming +a scale’s data, say to convert to thousands or between temperature units. ```js Plot.plot({ @@ -222,7 +396,11 @@ Plot.plot({ #### *plot*.scale(*scaleName*) -Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) function of a returned plot. The *scaleName* must be one of the known scale names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, `"symbol"`, or `"length"`. If the associated *plot* has no scale with the given *scaleName*, returns undefined. +Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) +function of a returned plot. The *scaleName* must be one of the known scale +names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, +`"symbol"`, or `"length"`. If the associated *plot* has no scale with the +given *scaleName*, returns undefined. ```js const plot = Plot.plot(…); // render a plot @@ -234,7 +412,11 @@ console.log(color.range); // inspect the color scale’s range, ["red", "blue"] #### Plot.scale(*options*) -You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +You can also create a standalone scale with Plot.**scale**(*options*). The +*options* object must define at least one scale; see [Scale +options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) +for how to define a scale. For example, here is a linear color scale with the +default domain of [0, 1] and default scheme *turbo*: ```js const color = Plot.scale({color: {type: "linear"}}); @@ -242,163 +424,312 @@ const color = Plot.scale({color: {type: "linear"}}); #### Scale objects -Both [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) and [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. +Both +[*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) +and +[Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) +return scale objects. These objects represent the actual (or “materialized”) +scale options used by Plot, including the domain, range, interpolate +function, *etc.* The scale’s label, if any, is also returned; however, note +that other axis properties are not currently exposed. Point and band scales +also expose their materialized bandwidth and step. -To reuse a scale across plots, pass the corresponding scale object into another plot specification: +To reuse a scale across plots, pass the corresponding scale object into +another plot specification: ```js const plot1 = Plot.plot(…); const plot2 = Plot.plot({…, color: plot1.scale("color")}); ``` -For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. +For convenience, scale objects expose a *scale*.**apply**(*input*) method +which returns the scale’s output for the given *input* value. When +applicable, scale objects also expose a *scale*.**invert**(*output*) method +which returns the corresponding input value from the scale’s domain for the +given *output* value. ### Position options The position scales (*x*, *y*, *fx*, and *fy*) support additional options: -* *scale*.**inset** - inset the default range by the specified amount in pixels -* *scale*.**round** - round the output value to the nearest integer (whole pixel) +* *scale*.**inset** - inset the default range by the specified amount in + pixels +* *scale*.**round** - round the output value to the nearest integer (whole + pixel) -The *x* and *fx* scales support asymmetric insets for more precision. Replace inset by: +The *x* and *fx* scales support asymmetric insets for more precision. Replace +inset by: -* *scale*.**insetLeft** - insets the start of the default range by the specified number of pixels -* *scale*.**insetRight** - insets the end of the default range by the specified number of pixels +* *scale*.**insetLeft** - insets the start of the default range by the + specified number of pixels +* *scale*.**insetRight** - insets the end of the default range by the + specified number of pixels Similarly, the *y* and *fy* scales support asymmetric insets with: -* *scale*.**insetTop** - insets the top of the default range by the specified number of pixels -* *scale*.**insetBottom** - insets the bottom of the default range by the specified number of pixels - -The inset scale options can provide “breathing room” to separate marks from axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels prevents dots from overlapping with the axes. The *scale*.round option is useful for crisp edges by rounding to the nearest pixel boundary. - -In addition to the generic *ordinal* scale type, which requires an explicit output range value for each input domain value, Plot supports special *point* and *band* scale types for encoding ordinal data as position. These scale types accept a [*min*, *max*] range similar to quantitative scales, and divide this continuous interval into discrete points or bands based on the number of distinct values in the domain (*i.e.*, the domain’s cardinality). If the associated marks have no effective width along the ordinal dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, say for a bar, use a *band* scale. In the image below, the top *x* scale is a *point* scale while the bottom *x* scale is a *band* scale; see [Plot: Scales](https://observablehq.com/@observablehq/plot-scales) for an interactive version. - -point and band scales - -Ordinal position scales support additional options, all specified as proportions in [0, 1]: - -* *scale*.**padding** - how much of the range to reserve to inset first and last point or band -* *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 = at middle, 1 = at end) +* *scale*.**insetTop** - insets the top of the default range by the specified + number of pixels +* *scale*.**insetBottom** - insets the bottom of the default range by the + specified number of pixels + +The inset scale options can provide “breathing room” to separate marks from +axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with +the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels +prevents dots from overlapping with the axes. The *scale*.round option is +useful for crisp edges by rounding to the nearest pixel boundary. + +In addition to the generic *ordinal* scale type, which requires an explicit +output range value for each input domain value, Plot supports special *point* +and *band* scale types for encoding ordinal data as position. These scale +types accept a [*min*, *max*] range similar to quantitative scales, and +divide this continuous interval into discrete points or bands based on the +number of distinct values in the domain (*i.e.*, the domain’s cardinality). +If the associated marks have no effective width along the ordinal +dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, +say for a bar, use a *band* scale. In the image below, the top *x* scale is a +*point* scale while the bottom *x* scale is a *band* scale; see [Plot: +Scales](https://observablehq.com/@observablehq/plot-scales) for an +interactive version. + +point and band
+scales + +Ordinal position scales support additional options, all specified as +proportions in [0, 1]: + +* *scale*.**padding** - how much of the range to reserve to inset first and + last point or band +* *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 + = at middle, 1 = at end) For a *band* scale, you can further fine-tune padding: -* *scale*.**paddingInner** - how much of the range to reserve to separate adjacent bands -* *scale*.**paddingOuter** - how much of the range to reserve to inset first and last band +* *scale*.**paddingInner** - how much of the range to reserve to separate + adjacent bands +* *scale*.**paddingOuter** - how much of the range to reserve to inset first + and last band -Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of available space reserved for separating bands), while point scale padding defaults to 0.5 (the gap between the first point and the edge is half the distance of the gap between points, and likewise for the gap between the last point and the opposite edge). Note that rounding and mark insets (e.g., for bars and rects) also affect separation between adjacent marks. +Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of +available space reserved for separating bands), while point scale padding +defaults to 0.5 (the gap between the first point and the edge is half the +distance of the gap between points, and likewise for the gap between the last +point and the opposite edge). Note that rounding and mark insets (e.g., for +bars and rects) also affect separation between adjacent marks. -Plot automatically generates axes for position scales. You can configure these axes with the following options: +Plot automatically generates axes for position scales. You can configure +these axes with the following options: -* *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or *right* for *y*; null to suppress +* *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or + *right* for *y*; null to suppress * *scale*.**ticks** - the approximate number of ticks to generate * *scale*.**tickSize** - the size of each tick (in pixels; default 6) -* *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) -* *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) -* *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) +* *scale*.**tickPadding** - the separation between the tick and its label (in + pixels; default 3) +* *scale*.**tickFormat** - to format tick values, either a function or format + specifier string; see + [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) +* *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees + clockwise; default 0) * *scale*.**grid** - if true, draw grid lines across the plot for each tick * *scale*.**line** - if true, draw the axis line * *scale*.**label** - a string to label the axis -* *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, *left*, or *center* -* *scale*.**labelOffset** - the label position offset (in pixels; default 0, typically for facet axes) -* *scale*.**fontVariant** - the font-variant attribute for axis ticks; defaults to tabular-nums for quantitative axes -* *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree +* *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, + *left*, or *center* +* *scale*.**labelOffset** - the label position offset (in pixels; default 0, + typically for facet axes) +* *scale*.**fontVariant** - the font-variant attribute for axis ticks; + defaults to tabular-nums for quantitative axes +* *scale*.**ariaLabel** - a short label representing the axis in the + accessibility tree * *scale*.**ariaDescription** - a textual description for the axis -Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. +Top-level options are also supported as shorthand: **grid** (for *x* and *y* +only; see +[facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), +**label**, **axis**, **inset**, **round**, **align**, and **padding**. ### Color options -The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordinal*—can be used to encode color. In addition, Plot supports special scale types for color: +The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and +*ordinal*—can be used to encode color. In addition, Plot supports special +scale types for color: -* *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* scheme +* *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* + scheme * *sequential* - equivalent to *linear* * *cyclical* - equivalent to *linear*, but defaults to the *rainbow* scheme -* *threshold* - encodes based on the specified discrete thresholds; defaults to the *rdylbu* scheme -* *quantile* - encodes based on the computed quantile thresholds; defaults to the *rdylbu* scheme -* *quantize* - transforms a continuous domain into quantized thresholds; defaults to the *rdylbu* scheme -* *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* scheme -* *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults to the *rdbu* scheme -* *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* scheme -* *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* scheme -* *diverging-symlog* - like *symlog*, but with a pivot; defaults to the *rdbu* scheme - -For a *threshold* scale, the *domain* represents *n* (typically numeric) thresholds which will produce a *range* of *n* + 1 output colors; the *i*th color of the *range* applies to values that are smaller than the *i*th element of the domain and larger or equal to the *i* - 1th element of the domain. For a *quantile* scale, the *domain* represents all input values to the scale, and the *n* option specifies how many quantiles to compute from the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output range of *n* colors. For a *quantize* scale, the domain will be transformed into approximately *n* quantized values, where *n* is an option that defaults to 5. - -By default, all diverging color scales are symmetric around the pivot; set *symmetric* to false if you want to cover the whole extent on both sides. +* *threshold* - encodes based on the specified discrete thresholds; defaults + to the *rdylbu* scheme +* *quantile* - encodes based on the computed quantile thresholds; defaults to + the *rdylbu* scheme +* *quantize* - transforms a continuous domain into quantized thresholds; + defaults to the *rdylbu* scheme +* *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* + scheme +* *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults + to the *rdbu* scheme +* *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* + scheme +* *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* + scheme +* *diverging-symlog* - like *symlog*, but with a pivot; defaults to the + *rdbu* scheme + +For a *threshold* scale, the *domain* represents *n* (typically numeric) +thresholds which will produce a *range* of *n* + 1 output colors; the *i*th +color of the *range* applies to values that are smaller than the *i*th +element of the domain and larger or equal to the *i* - 1th element of the +domain. For a *quantile* scale, the *domain* represents all input values to +the scale, and the *n* option specifies how many quantiles to compute from +the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output +range of *n* colors. For a *quantize* scale, the domain will be transformed +into approximately *n* quantized values, where *n* is an option that defaults +to 5. + +By default, all diverging color scales are symmetric around the pivot; set +*symmetric* to false if you want to cover the whole extent on both sides. Color scales support two additional options: -* *scale*.**scheme** - a named color scheme in lieu of a range, such as *reds* -* *scale*.**interpolate** - in conjunction with a range, how to interpolate colors - -For quantile and quantize color scales, the *scale*.scheme option is used in conjunction with *scale*.**n**, which determines how many quantiles or quantized values to compute, and thus the number of elements in the scale’s range; it defaults to 5 (for quintiles in the case of a quantile scale). - -The following sequential scale schemes are supported for both quantitative and ordinal data: - -* blues *blues* -* greens *greens* -* greys *greys* -* oranges *oranges* -* purples *purples* -* reds *reds* -* bugn *bugn* -* bupu *bupu* -* gnbu *gnbu* -* orrd *orrd* -* pubu *pubu* -* pubugn *pubugn* -* purd *purd* -* rdpu *rdpu* -* ylgn *ylgn* -* ylgnbu *ylgnbu* -* ylorbr *ylorbr* -* ylorrd *ylorrd* -* cividis *cividis* -* inferno *inferno* -* magma *magma* -* plasma *plasma* -* viridis *viridis* -* cubehelix *cubehelix* -* turbo *turbo* -* warm *warm* -* cool *cool* - -The default color scheme, *turbo*, was chosen primarily to ensure high-contrast visibility. Color schemes such as *blues* make low-value marks difficult to see against a white background, for better or for worse. To use a subset of a continuous color scheme (or any single-argument *interpolate* function), set the *scale*.range property to the corresponding subset of [0, 1]; for example, to use the first half of the *rainbow* color scheme, use a range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to encode a quantitative value without hue, consider using *opacity* rather than *color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). +* *scale*.**scheme** - a named color scheme in lieu of a range, such as + *reds* +* *scale*.**interpolate** - in conjunction with a range, how to interpolate + colors + +For quantile and quantize color scales, the *scale*.scheme option is used in +conjunction with *scale*.**n**, which determines how many quantiles or +quantized values to compute, and thus the number of elements in the scale’s +range; it defaults to 5 (for quintiles in the case of a quantile scale). + +The following sequential scale schemes are supported for both quantitative +and ordinal data: + +* blues + *blues* +* greens + *greens* +* greys + *greys* +* oranges *oranges* +* purples *purples* +* reds + *reds* +* bugn + *bugn* +* bupu + *bupu* +* gnbu + *gnbu* +* orrd + *orrd* +* pubu + *pubu* +* pubugn + *pubugn* +* purd + *purd* +* rdpu + *rdpu* +* ylgn + *ylgn* +* ylgnbu + *ylgnbu* +* ylorbr + *ylorbr* +* ylorrd + *ylorrd* +* cividis *cividis* +* inferno *inferno* +* magma + *magma* +* plasma + *plasma* +* viridis *viridis* +* cubehelix *cubehelix* +* turbo + *turbo* +* warm + *warm* +* cool + *cool* + +The default color scheme, *turbo*, was chosen primarily to ensure +high-contrast visibility. Color schemes such as *blues* make low-value marks +difficult to see against a white background, for better or for worse. To use +a subset of a continuous color scheme (or any single-argument *interpolate* +function), set the *scale*.range property to the corresponding subset of [0, +1]; for example, to use the first half of the *rainbow* color scheme, use a +range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to +encode a quantitative value without hue, consider using *opacity* rather than +*color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). The following diverging scale schemes are supported: -* brbg *brbg* -* prgn *prgn* -* piyg *piyg* -* puor *puor* -* rdbu *rdbu* -* rdgy *rdgy* -* rdylbu *rdylbu* -* rdylgn *rdylgn* -* spectral *spectral* -* burd *burd* -* buylrd *buylrd* - -Picking a diverging color scheme name defaults the scale type to *diverging*; set the scale type to *linear* to treat the color scheme as sequential instead. Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*). +* brbg + *brbg* +* prgn + *prgn* +* piyg + *piyg* +* puor + *puor* +* rdbu + *rdbu* +* rdgy + *rdgy* +* rdylbu + *rdylbu* +* rdylgn + *rdylgn* +* spectral *spectral* +* burd + *burd* +* buylrd + *buylrd* + +Picking a diverging color scheme name defaults the scale type to *diverging*; +set the scale type to *linear* to treat the color scheme as sequential +instead. Diverging color scales support a *scale*.**pivot** option, which +defaults to zero. Values below the pivot will use the lower half of the color +scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot +will use the upper half (grays for *rdgy*). The following cylical color schemes are supported: -* rainbow *rainbow* -* sinebow *sinebow* +* rainbow *rainbow* +* sinebow *sinebow* The following categorical color schemes are supported: -* accent *accent* (8 colors) -* category10 *category10* (10 colors) -* dark2 *dark2* (8 colors) -* paired *paired* (12 colors) -* pastel1 *pastel1* (9 colors) -* pastel2 *pastel2* (8 colors) -* set1 *set1* (9 colors) -* set2 *set2* (8 colors) -* set3 *set3* (12 colors) -* tableau10 *tableau10* (10 colors) +* accent + *accent* (8 colors) +* category10 *category10* (10 colors) +* dark2 + *dark2* (8 colors) +* paired *paired* (12 colors) +* pastel1 *pastel1* (9 colors) +* pastel2 *pastel2* (8 colors) +* set1 + *set1* (9 colors) +* set2 + *set2* (8 colors) +* set3 + *set3* (12 colors) +* tableau10 *tableau10* (10 colors) The following color interpolators are supported: @@ -419,7 +750,8 @@ Plot.plot({ }) ``` -Or to use gamma-corrected RGB (via [d3-interpolate](https://github.com/d3/d3-interpolate)): +Or to use gamma-corrected RGB (via +[d3-interpolate](https://github.com/d3/d3-interpolate)): ```js Plot.plot({ @@ -433,21 +765,52 @@ Plot.plot({ ### Sort options -If an ordinal scale’s domain is not set, it defaults to natural ascending order; to order the domain by associated values in another dimension, either compute the domain manually (consider [d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) or use an associated mark’s **sort** option. For example, to sort bars by ascending frequency rather than alphabetically by letter: +If an ordinal scale’s domain is not set, it defaults to natural ascending +order; to order the domain by associated values in another dimension, either +compute the domain manually (consider +[d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) +or use an associated mark’s **sort** option. For example, to sort bars by +ascending frequency rather than alphabetically by letter: ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) ``` -The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. - -Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: +The sort option is an object whose keys are ordinal scale names, such as *x* +or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. +By specifying an existing channel rather than a new value, you avoid +repeating the order definition and can refer to channels derived by +[transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) +(such as +[stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or +[bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When +sorting on the *x*, if no such channel is defined, the *x2* channel will be +used instead if available, and similarly for *y* and *y2*; this is useful for +marks that implicitly stack such as +[area](https://github.com/observablehq/plot/blob/main/README.md#area), +[bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and +[rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort +value may also be specified as *width* or *height*, representing derived +channels |*x2* - *x1*| and |*y2* - *y1*| respectively. + +Note that there may be multiple associated values in the secondary dimension +for a given value in the primary ordinal dimension. The secondary values are +therefore grouped for each associated primary value, and each group is then +aggregated by applying a reducer. Lastly the primary values are sorted based +on the associated reduced value in natural ascending order to produce the +domain. The default reducer is *max*, but may be changed by specifying the +*reduce* option. The above code is shorthand for: ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) ``` -Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. +Generally speaking, a reducer only needs to be specified when there are +multiple secondary values for a given primary value. TODO An example of +assigning categorical colors in a scatterplot by descending count to maximize +discriminability. See the [group +transform](https://github.com/observablehq/plot/blob/main/README.md#group) +for the list of supported reducers. For descending rather than ascending order, use the *reverse* option: @@ -455,36 +818,62 @@ For descending rather than ascending order, use the *reverse* option: Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) ``` -An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) +An additional *limit* option truncates the domain to the first *n* values +after sorting. If *limit* is negative, the last *n* values are used instead. +Hence, a positive *limit* with *reverse* = true will return the top *n* +values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th +values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic +filter +transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), +limiting the *x* domain here does not affect the computation of the *y* +domain, which is computed independently without respect to filtering.) ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) ``` -If different sort options are needed for different ordinal scales, the channel name can be replaced with a *value* object with additional per-scale options. +If different sort options are needed for different ordinal scales, the +channel name can be replaced with a *value* object with additional per-scale +options. ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse: true}}}) ``` -If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. +If the input channel is *data*, then the reducer is passed groups of the +mark’s data; this is typically used in conjunction with a custom reducer +function, as when the built-in single-channel reducers are insufficient. -Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). +Note: when the value of the sort option is a string or a function, it is +interpreted as a [basic sort +transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). +To use both sort options and a sort transform, use +[Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). ### Facet options -The *facet* option enables [faceting](https://observablehq.com/@observablehq/plot-facets). When faceting, two additional band scales may be configured: +The *facet* option enables +[faceting](https://observablehq.com/@observablehq/plot-facets). When +faceting, two additional band scales may be configured: * **fx** - the horizontal position, a *band* scale * **fy** - the vertical position, a *band* scale -Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: +Similar to +[marks](https://github.com/observablehq/plot/blob/main/README.md#marks), +faceting requires specifying data and at least one of two optional channels: * facet.**data** - the data to be faceted -* facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* -* facet.**y** - the vertical position; bound to the *fy* scale, which must be *band* +* facet.**x** - the horizontal position; bound to the *fx* scale, which must + be *band* +* facet.**y** - the vertical position; bound to the *fy* scale, which must be + *band* -The facet.**x** and facet.**y** channels are strictly ordinal or categorical (*i.e.*, discrete); each distinct channel value defines a facet. Quantitative data must be manually discretized for faceting, say by rounding or binning. (Automatic binning for quantitative data may be added in the future; see [#14](https://github.com/observablehq/plot/issues/14).) +The facet.**x** and facet.**y** channels are strictly ordinal or categorical +(*i.e.*, discrete); each distinct channel value defines a facet. Quantitative +data must be manually discretized for faceting, say by rounding or binning. +(Automatic binning for quantitative data may be added in the future; see +[#14](https://github.com/observablehq/plot/issues/14).) The following *facet* constant options are also supported: @@ -496,12 +885,16 @@ The following *facet* constant options are also supported: * facet.**grid** - if true, draw grid lines for each facet * facet.**label** - if null, disable default facet axis labels -Faceting can be explicitly enabled or disabled on a mark with the *facet* option, which accepts the following values: +Faceting can be explicitly enabled or disabled on a mark with the *facet* +option, which accepts the following values: -* *auto* (default) - equivalent to *include* when mark data is strictly equal to facet data; else null -* *include* (or true) - draw the subset of the mark’s data in the current facet +* *auto* (default) - equivalent to *include* when mark data is strictly equal + to facet data; else null +* *include* (or true) - draw the subset of the mark’s data in the current + facet * *exclude* - draw the subset of the mark’s data *not* in the current facet -* null (or false) - repeat this mark’s data across all facets (i.e., no faceting) +* null (or false) - repeat this mark’s data across all facets (i.e., no + faceting) ```js Plot.plot({ @@ -517,17 +910,34 @@ Plot.plot({ }) ``` -When the *include* or *exclude* facet mode is chosen, the mark data must be parallel to the facet data: the mark data must have the same length and order as the facet data. If the data are not parallel, then the wrong data may be shown in each facet. The default *auto* therefore requires strict equality (`===`) for safety, and using the facet data as mark data is recommended when using the *exclude* facet mode. (To construct parallel data safely, consider using [*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on the facet data.) +When the *include* or *exclude* facet mode is chosen, the mark data must be +parallel to the facet data: the mark data must have the same length and order +as the facet data. If the data are not parallel, then the wrong data may be +shown in each facet. The default *auto* therefore requires strict equality +(`===`) for safety, and using the facet data as mark data is recommended when +using the *exclude* facet mode. (To construct parallel data safely, consider +using +[*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) +on the facet data.) ## Legends -Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: +Plot can generate legends for *color*, *opacity*, and *symbol* +[scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). +(An opacity scale is treated as a color scale with varying transparency.) For +an inline legend, use the *scale*.**legend** option: * *scale*.**legend** - if truthy, generate a legend for the given scale -If the *scale*.**legend** option is true, the default legend will be produced for the scale; otherwise, the meaning of the *legend* option depends on the scale. For quantitative color scales, it defaults to *ramp* but may be set to *swatches* for a discrete scale (most commonly for *threshold* color scales); for ordinal color scales and symbol scales, only the *swatches* value is supported. +If the *scale*.**legend** option is true, the default legend will be produced +for the scale; otherwise, the meaning of the *legend* option depends on the +scale. For quantitative color scales, it defaults to *ramp* but may be set to +*swatches* for a discrete scale (most commonly for *threshold* color scales); +for ordinal color scales and symbol scales, only the *swatches* value is +supported. -For example, this scatterplot includes a swatches legend for the ordinal color scale: +For example, this scatterplot includes a swatches legend for the ordinal +color scale: ```js Plot.plot({ @@ -540,7 +950,8 @@ Plot.plot({ }) ``` -Whereas this scatterplot would render a ramp legend for its diverging color scale: +Whereas this scatterplot would render a ramp legend for its diverging color +scale: ```js Plot.plot({ @@ -556,7 +967,11 @@ Plot.plot({ #### *plot*.legend(*scaleName*, *options*) -Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: +Given an existing *plot* returned by +[Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), +returns a detached legend for the *plot*’s scale with the given *scaleName*. +The *scaleName* must refer to a scale that supports legends: either +`"color"`, `"opacity"`, or `"symbol"`. For example: ```js myplot = Plot.plot(…) @@ -571,9 +986,12 @@ Or, with additional *options*: mylegend = myplot.legend("color", {width: 320}) ``` -If there is no scale with the given *scaleName* on the given *plot*, then *plot*.legend will return undefined. +If there is no scale with the given *scaleName* on the given *plot*, then +*plot*.legend will return undefined. -Categorical and ordinal color legends are rendered as swatches, unless *options*.**legend** is set to *ramp*. The swatches can be configured with the following options: +Categorical and ordinal color legends are rendered as swatches, unless +*options*.**legend** is set to *ramp*. The swatches can be configured with +the following options: * *options*.**tickFormat** - a format function for the labels * *options*.**swatchSize** - the size of the swatch (if square) @@ -581,10 +999,12 @@ Categorical and ordinal color legends are rendered as swatches, unless *options* * *options*.**swatchHeight** - the swatches’ height * *options*.**columns** - the number of swatches per row * *options*.**marginLeft** - the legend’s left margin -* *options*.**className** - a class name, that defaults to a randomly generated string scoping the styles +* *options*.**className** - a class name, that defaults to a randomly + generated string scoping the styles * *options*.**width** - the legend’s width (in pixels) -Symbol legends are rendered as swatches and support the options above in addition to the following options: +Symbol legends are rendered as swatches and support the options above in +addition to the following options: * *options*.**fill** - the symbol fill color * *options*.**fillOpacity** - the symbol fill opacity; defaults to 1 @@ -593,12 +1013,18 @@ Symbol legends are rendered as swatches and support the options above in additio * *options*.**strokeWidth** - the symbol stroke width; defaults to 1.5 * *options*.**r** - the symbol radius; defaults to 4.5 pixels -The **fill** and **stroke** symbol legend options can be specified as “color” to apply the color scale when the symbol scale is a redundant encoding. The **fill** defaults to none. The **stroke** defaults to currentColor if the fill is none, and to none otherwise. The **fill** and **stroke** options may also be inherited from the corresponding options on an associated dot mark. +The **fill** and **stroke** symbol legend options can be specified as “color” +to apply the color scale when the symbol scale is a redundant encoding. The +**fill** defaults to none. The **stroke** defaults to currentColor if the +fill is none, and to none otherwise. The **fill** and **stroke** options may +also be inherited from the corresponding options on an associated dot mark. -Continuous color legends are rendered as a ramp, and can be configured with the following options: +Continuous color legends are rendered as a ramp, and can be configured with +the following options: * *options*.**label** - the scale’s label -* *options*.**ticks** - the desired number of ticks, or an array of tick values +* *options*.**ticks** - the desired number of ticks, or an array of tick + values * *options*.**tickFormat** - a format function for the legend’s ticks * *options*.**tickSize** - the tick size * *options*.**round** - if true (default), round tick positions to pixels @@ -609,19 +1035,27 @@ Continuous color legends are rendered as a ramp, and can be configured with the * *options*.**marginBottom** - the legend’s bottom margin * *options*.**marginLeft** - the legend’s left margin -The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). +The **style** legend option allows custom styles to override Plot’s defaults; +it has the same behavior as in Plot’s top-level [layout +options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). #### Plot.legend(*options*) -Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +Returns a standalone legend for the scale defined by the given *options* +object. The *options* object must define at least one scale; see [Scale +options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) +for how to define a scale. For example, here is a ramp legend of a linear +color scale with the default domain of [0, 1] and default scheme *turbo*: ```js Plot.legend({color: {type: "linear"}}) ``` -The *options* object may also include any additional legend options described in the previous section. For example, to make the above legend slightly wider: +The *options* object may also include any additional legend options described +in the previous section. For example, to make the above legend slightly +wider: ```js Plot.legend({ @@ -769,7 +1203,11 @@ Plot.barY(alphabet, {x: "letter", y: "frequency"}).plot({width: 1024}) #### Plot.marks(...*marks*) -A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. +A convenience method for composing a mark from a series of other marks. +Returns an array of marks that implements the *mark*.plot function. See the +[box mark +implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) +for an example. @@ -808,7 +1246,15 @@ The area mark supports [curve options](#curves) to control interpolation between Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) ``` -Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. +Returns a new area with the given *data* and *options*. Plot.area is rarely +used directly; it is only needed when the baseline and topline have neither +common *x* nor *y* values. +[Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) +is used in the common horizontal orientation where the baseline and topline +share *x* values, while +[Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) +is used in the vertical orientation where the baseline and topline share *y* +values. @@ -818,15 +1264,31 @@ Returns a new area with the given *data* and *options*. Plot.area is rarely used Plot.areaX(aapl, {y: "Date", x: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is +used when the baseline and topline share *y* values, as in a time-series area +chart where time goes up↑. If neither the **x1** nor **x2** option is +specified, the **x** option may be specified as shorthand to apply an +implicit [stackX +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); +this is the typical configuration for an area chart with a baseline at *x* = +0. If the **x** option is not specified, it defaults to the identity +function. The **y** option specifies the **y1** channel; and the **y1** and +**y2** options are ignored. -If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY +transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is +implicitly applied to the specified *options*. The reducer of the output *x* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -836,15 +1298,31 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.areaY(aapl, {x: "Date", y: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is +used when the baseline and topline share *x* values, as in a time-series area +chart where time goes right→. If neither the **y1** nor **y2** option is +specified, the **y** option may be specified as shorthand to apply an +implicit [stackY +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); +this is the typical configuration for an area chart with a baseline at *y* = +0. If the **y** option is not specified, it defaults to the identity +function. The **x** option specifies the **x1** channel; and the **x1** and +**x2** options are ignored. -If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX +transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is +implicitly applied to the specified *options*. The reducer of the output *y* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -902,20 +1380,35 @@ For the required channels, see [Plot.barX](#plotbarxdata-options) and [Plot.barY Plot.barX(alphabet, {y: "letter", x: "frequency"}) ``` -Returns a new horizontal bar↔︎ with the given *data* and *options*. The following channels are required: +Returns a new horizontal bar↔︎ with the given *data* and *options*. The +following channels are required: * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. - -If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. - -In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: +If neither the **x1** nor **x2** option is specified, the **x** option may be +specified as shorthand to apply an implicit [stackX +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); +this is the typical configuration for a horizontal bar chart with bars +aligned at *x* = 0. If the **x** option is not specified, it defaults to the +identity function. If *options* is undefined, then it defaults to **x2** as +the identity function and **y** as the index of data; this allows an array of +numbers to be passed to Plot.barX to make a quick sequential bar chart. + +If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be +derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce +*x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. +If the interval is specified as a number *n*, *x1* and *x2* are taken as the +two consecutive multiples of *n* that bracket *x*. + +In addition to the [standard bar +channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the +following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* -If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). +If the **y** channel is not specified, the bar will span the full vertical +extent of the plot (or facet). @@ -925,20 +1418,36 @@ If the **y** channel is not specified, the bar will span the full vertical exten Plot.barY(alphabet, {x: "letter", y: "frequency"}) ``` -Returns a new vertical bar↕︎ with the given *data* and *options*. The following channels are required: +Returns a new vertical bar↕︎ with the given *data* and *options*. The +following channels are required: * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. +If neither the **y1** nor **y2** option is specified, the **y** option may be +specified as shorthand to apply an implicit [stackY +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); +this is the typical configuration for a vertical bar chart with bars aligned +at *y* = 0. If the **y** option is not specified, it defaults to the identity +function. If *options* is undefined, then it defaults to **y2** as the +identity function and **x** as the index of data; this allows an array of +numbers to be passed to Plot.barY to make a quick sequential bar chart. -If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be +derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce +*y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. +If the interval is specified as a number *n*, *y1* and *y2* are taken as the +two consecutive multiples of *n* that bracket *y*. -In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: +In addition to the [standard bar +channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the +following optional channels are supported: -* **x** - the horizontal position; bound to the *x* scale, which must be *band* +* **x** - the horizontal position; bound to the *x* scale, which must be + *band* -If the **x** channel is not specified, the bar will span the full horizontal extent of the plot (or facet). +If the **x** channel is not specified, the bar will span the full horizontal +extent of the plot (or facet). @@ -971,7 +1480,10 @@ The given *options* are passed through to these underlying marks, with the excep Plot.boxX(simpsons.map(d => d.imdb_rating)) ``` -Returns a horizontal boxplot mark. If the **x** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **y** option is not specified, it defaults to null; if the **y** option is specified, it should represent an ordinal (discrete) value. +Returns a horizontal boxplot mark. If the **x** option is not specified, it +defaults to the identity function, as when *data* is an array of numbers. If +the **y** option is not specified, it defaults to null; if the **y** option +is specified, it should represent an ordinal (discrete) value. @@ -981,7 +1493,10 @@ Returns a horizontal boxplot mark. If the **x** option is not specified, it defa Plot.boxY(simpsons.map(d => d.imdb_rating)) ``` -Returns a vertical boxplot mark. If the **y** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **x** option is not specified, it defaults to null; if the **x** option is specified, it should represent an ordinal (discrete) value. +Returns a vertical boxplot mark. If the **y** option is not specified, it +defaults to the identity function, as when *data* is an array of numbers. If +the **x** option is not specified, it defaults to null; if the **x** option +is specified, it should represent an ordinal (discrete) value. @@ -1008,7 +1523,10 @@ The **stroke** defaults to none. The **fill** defaults to currentColor if the st Plot.cell(simpsons, {x: "number_in_season", y: "season", fill: "imdb_rating"}) ``` -Returns a new cell with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new cell with the given *data* and *options*. If neither the **x** +nor **y** options are specified, *data* is assumed to be an array of pairs +[[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, +*x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1018,7 +1536,12 @@ Returns a new cell with the given *data* and *options*. If neither the **x** nor Plot.cellX(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), +except that if the **x** option is not specified, it defaults to [0, 1, 2, +…], and if the **fill** option is not specified and **stroke** is not a +channel, the fill defaults to the identity function and assumes that *data* = +[*x₀*, *x₁*, *x₂*, …]. @@ -1028,7 +1551,12 @@ Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README. Plot.cellY(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), +except that if the **y** option is not specified, it defaults to [0, 1, 2, +…], and if the **fill** option is not specified and **stroke** is not a +channel, the fill defaults to the identity function and assumes that *data* = +[*y₀*, *y₁*, *y₂*, …]. @@ -1042,41 +1570,68 @@ Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README. #### Plot.delaunayLink(*data*, *options*) -Draws links for each edge of the Delaunay triangulation of the points given by the **x** and **y** channels. Supports the same options as the [link mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived automatically from **x** and **y**. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the link inherits the corresponding channel value from one of its two endpoints arbitrarily. +Draws links for each edge of the Delaunay triangulation of the points given +by the **x** and **y** channels. Supports the same options as the [link +mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived +automatically from **x** and **y**. When an aesthetic channel is specified +(such as **stroke** or **strokeWidth**), the link inherits the corresponding +channel value from one of its two endpoints arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Delaunay triangulations are constructed for each group. #### Plot.delaunayMesh(*data*, *options*) -Draws a mesh of the Delaunay triangulation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a mesh of the Delaunay triangulation of the points given by the **x** +and **y** channels. The **stroke** option defaults to _currentColor_, and the +**strokeOpacity** defaults to 0.2. The **fill** option is not supported. When +an aesthetic channel is specified (such as **stroke** or **strokeWidth**), +the mesh inherits the corresponding channel value from one of its constituent +points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Delaunay triangulations are constructed for each group. #### Plot.hull(*data*, *options*) -Draws a convex hull around the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_ and the **fill** option defaults to _none_. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the hull inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a convex hull around the points given by the **x** and **y** channels. +The **stroke** option defaults to _currentColor_ and the **fill** option +defaults to _none_. When an aesthetic channel is specified (such as +**stroke** or **strokeWidth**), the hull inherits the corresponding channel +value from one of its constituent points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate convex hulls are constructed for each group. If the **z** channel is not specified, it defaults to either the **fill** channel, if any, or the **stroke** channel, if any. +If a **z** channel is specified, the input points are grouped by *z*, and +separate convex hulls are constructed for each group. If the **z** channel is +not specified, it defaults to either the **fill** channel, if any, or the +**stroke** channel, if any. #### Plot.voronoi(*data*, *options*) -Draws polygons for each cell of the Voronoi tesselation of the points given by the **x** and **y** channels. +Draws polygons for each cell of the Voronoi tesselation of the points given +by the **x** and **y** channels. -If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Voronoi tesselations are constructed for each group. #### Plot.voronoiMesh(*data*, *options*) -Draws a mesh for the cell boundaries of the Voronoi tesselation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a mesh for the cell boundaries of the Voronoi tesselation of the points +given by the **x** and **y** channels. The **stroke** option defaults to +_currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** +option is not supported. When an aesthetic channel is specified (such as +**stroke** or **strokeWidth**), the mesh inherits the corresponding channel +value from one of its constituent points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Voronoi tesselations are constructed for each group. @@ -1090,11 +1645,24 @@ If a **z** channel is specified, the input points are grouped by *z*, and separa #### Plot.density(*data*, *options*) -Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option. - -The **thresholds** option, which defaults to 20, specifies one more than the number of contours that will be computed at uniformly-spaced intervals between 0 (exclusive) and the maximum density (exclusive). The **thresholds** option may also be specified as an array or iterable of explicit density values. The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels. - -If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour. +Draws contours representing the estimated density of the two-dimensional +points given by the **x** and **y** channels, and possibly weighted by the +**weight** channel. If either of the **x** or **y** channels are not +specified, the corresponding position is controlled by the **frameAnchor** +option. + +The **thresholds** option, which defaults to 20, specifies one more than the +number of contours that will be computed at uniformly-spaced intervals +between 0 (exclusive) and the maximum density (exclusive). The **thresholds** +option may also be specified as an array or iterable of explicit density +values. The **bandwidth** option, which defaults to 20, specifies the +standard deviation of the Gaussian kernel used for estimation in pixels. + +If a **z**, **stroke** or **fill** channel is specified, the input points are +grouped by series, and separate sets of contours are generated for each +series. If the **stroke** or **fill** is specified as *density*, a color +channel is constructed with values representing the density threshold value +of each contour. @@ -1137,7 +1705,10 @@ Dots are drawn in input order, with the last data drawn on top. If sorting is ne Plot.dot(sales, {x: "units", y: "fruit"}) ``` -Returns a new dot with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new dot with the given *data* and *options*. If neither the **x** +nor **y** nor **frameAnchor** options are specified, *data* is assumed to be +an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that +**x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1147,9 +1718,15 @@ Returns a new dot with the given *data* and *options*. If neither the **x** nor Plot.dotX(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. -If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y** is transformed to +(*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If +the interval is specified as a number *n*, *y* will be the midpoint of two +consecutive multiples of *n* that bracket *y*. @@ -1159,21 +1736,31 @@ If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*in Plot.dotY(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. -If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x** is transformed to +(*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If +the interval is specified as a number *n*, *x* will be the midpoint of two +consecutive multiples of *n* that bracket *x*. #### Plot.circle(*data*, *options*) -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *circle*. +Equivalent to +[Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) +except that the **symbol** option is set to *circle*. #### Plot.hexagon(*data*, *options*) -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *hexagon*. +Equivalent to +[Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) +except that the **symbol** option is set to *hexagon*. @@ -1185,7 +1772,9 @@ The hexgrid mark can be used to support marks using the [hexbin](#hexbin) layout #### Plot.hexgrid(*options*) -The **binWidth** option specifies the distance between the centers of neighboring hexagons, in pixels (defaults to 20). The **clip** option defaults to true, clipping the mark to the frame’s dimensions. +The **binWidth** option specifies the distance between the centers of +neighboring hexagons, in pixels (defaults to 20). The **clip** option +defaults to true, clipping the mark to the frame’s dimensions. @@ -1224,7 +1813,10 @@ Images are drawn in input order, with the last data drawn on top. If sorting is Plot.image(presidents, {x: "inauguration", y: "favorability", src: "portrait"}) ``` -Returns a new image with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new image with the given *data* and *options*. If neither the **x** +nor **y** nor **frameAnchor** options are specified, *data* is assumed to be +an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that +**x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1252,7 +1844,8 @@ Multiple regressions can be defined by specifying the *z*, *fill*, or *stroke* c Plot.linearRegressionX(mtcars, {y: "wt", x: "hp"}) ``` -Returns a linear regression mark where *x* is the dependent variable and *y* is the independent variable. +Returns a linear regression mark where *x* is the dependent variable and *y* +is the independent variable. @@ -1262,7 +1855,8 @@ Returns a linear regression mark where *x* is the dependent variable and *y* is Plot.linearRegressionY(mtcars, {x: "wt", y: "hp"}) ``` -Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. +Returns a linear regression mark where *y* is the dependent variable and *x* +is the independent variable. @@ -1297,7 +1891,10 @@ The line mark supports [curve options](#curves) to control interpolation between Plot.line(aapl, {x: "Date", y: "Close"}) ``` -Returns a new line with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new line with the given *data* and *options*. If neither the **x** +nor **y** options are specified, *data* is assumed to be an array of pairs +[[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, +*x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1307,15 +1904,26 @@ Returns a new line with the given *data* and *options*. If neither the **x** nor Plot.lineX(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. +Similar to +[Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option +is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY +transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is +implicitly applied to the specified *options*. The reducer of the output *x* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -1325,15 +1933,26 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.lineY(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. +Similar to +[Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option +is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX +transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is +implicitly applied to the specified *options*. The reducer of the output *y* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -1405,7 +2024,14 @@ Returns a new rect with the given *data* and *options*. Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. +Equivalent to +[Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), +except that if neither the **x1** nor **x2** option is specified, the **x** +option may be specified as shorthand to apply an implicit [stackX +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); +this is the typical configuration for a histogram with rects aligned at *x* = +0. If the **x** option is not specified, it defaults to the identity +function. @@ -1415,7 +2041,14 @@ Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README. Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. +Equivalent to +[Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), +except that if neither the **y1** nor **y2** option is specified, the **y** +option may be specified as shorthand to apply an implicit [stackY +transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); +this is the typical configuration for a histogram with rects aligned at *y* = +0. If the **y** option is not specified, it defaults to the identity +function. @@ -1438,15 +2071,28 @@ Plot.ruleX([0]) // as annotation Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY ``` -Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: +Returns a new rule↕︎ with the given *data* and *options*. In addition to the +[standard mark +options](https://github.com/observablehq/plot/blob/main/README.md#marks), the +following channels are optional: * **x** - the horizontal position; bound to the *x* scale * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is specified, it is shorthand for the **y2** option with **y1** equal to zero; this is the typical configuration for a vertical lollipop chart with rules aligned at *y* = 0. If the **y1** channel is not specified, the rule will start at the top of the plot (or facet). If the **y2** channel is not specified, the rule will end at the bottom of the plot (or facet). +If the **x** option is not specified, it defaults to the identity function +and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is +specified, it is shorthand for the **y2** option with **y1** equal to zero; +this is the typical configuration for a vertical lollipop chart with rules +aligned at *y* = 0. If the **y1** channel is not specified, the rule will +start at the top of the plot (or facet). If the **y2** channel is not +specified, the rule will end at the bottom of the plot (or facet). -If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be +derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce +*y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. +If the interval is specified as a number *n*, *y1* and *y2* are taken as the +two consecutive multiples of *n* that bracket *y*. @@ -1455,19 +2101,33 @@ If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be der ```js Plot.ruleY([0]) // as annotation ``` + ```js Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX ``` -Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: +Returns a new rule↔︎ with the given *data* and *options*. In addition to the +[standard mark +options](https://github.com/observablehq/plot/blob/main/README.md#marks), the +following channels are optional: * **y** - the vertical position; bound to the *y* scale * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is specified, it is shorthand for the **x2** option with **x1** equal to zero; this is the typical configuration for a horizontal lollipop chart with rules aligned at *x* = 0. If the **x1** channel is not specified, the rule will start at the left edge of the plot (or facet). If the **x2** channel is not specified, the rule will end at the right edge of the plot (or facet). +If the **y** option is not specified, it defaults to the identity function +and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is +specified, it is shorthand for the **x2** option with **x1** equal to zero; +this is the typical configuration for a horizontal lollipop chart with rules +aligned at *x* = 0. If the **x1** channel is not specified, the rule will +start at the left edge of the plot (or facet). If the **x2** channel is not +specified, the rule will end at the right edge of the plot (or facet). -If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be +derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce +*x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. +If the interval is specified as a number *n*, *x1* and *x2* are taken as the +two consecutive multiples of *n* that bracket *x*. @@ -1519,23 +2179,38 @@ The **paintOrder** option defaults to “stroke” and the **strokeWidth** optio #### Plot.text(*data*, *options*) -Returns a new text mark with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new text mark with the given *data* and *options*. If neither the +**x** nor **y** nor **frameAnchor** options are specified, *data* is assumed +to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such +that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. #### Plot.textX(*data*, *options*) -Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), +except **x** defaults to the identity function and assumes that *data* = +[*x₀*, *x₁*, *x₂*, …]. -If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y** is transformed to +(*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If +the interval is specified as a number *n*, *y* will be the midpoint of two +consecutive multiples of *n* that bracket *y*. #### Plot.textY(*data*, *options*) -Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), +except **y** defaults to the identity function and assumes that *data* = +[*y₀*, *y₁*, *y₂*, …]. -If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x** is transformed to +(*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If +the interval is specified as a number *n*, *x* will be the midpoint of two +consecutive multiples of *n* that bracket *x*. @@ -1555,7 +2230,8 @@ For the required channels, see [Plot.tickX](#plottickxdata-options) and [Plot.ti Plot.tickX(stateage, {x: "population", y: "age"}) ``` -Returns a new tick↕︎ with the given *data* and *options*. The following channels are required: +Returns a new tick↕︎ with the given *data* and *options*. The following +channels are required: * **x** - the horizontal position; bound to the *x* scale @@ -1563,7 +2239,8 @@ The following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* -If the **y** channel is not specified, the tick will span the full vertical extent of the plot (or facet). +If the **y** channel is not specified, the tick will span the full vertical +extent of the plot (or facet). @@ -1573,15 +2250,18 @@ If the **y** channel is not specified, the tick will span the full vertical exte Plot.tickY(stateage, {y: "population", x: "age"}) ``` -Returns a new tick↔︎ with the given *data* and *options*. The following channels are required: +Returns a new tick↔︎ with the given *data* and *options*. The following +channels are required: * **y** - the vertical position; bound to the *y* scale The following optional channels are supported: -* **x** - the horizontal position; bound to the *x* scale, which must be *band* +* **x** - the horizontal position; bound to the *x* scale, which must be + *band* -If the **x** channel is not specified, the tick will span the full vertical extent of the plot (or facet). +If the **x** channel is not specified, the tick will span the full vertical +extent of the plot (or facet). @@ -1623,19 +2303,28 @@ Vectors are drawn in input order, with the last data drawn on top. If sorting is Plot.vector(wind, {x: "longitude", y: "latitude", length: "speed", rotate: "direction"}) ``` -Returns a new vector with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new vector with the given *data* and *options*. If neither the +**x** nor **y** options are specified, *data* is assumed to be an array of +pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, +*x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. #### Plot.vectorX(*data*, *options*) -Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. #### Plot.vectorY(*data*, *options*) -Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -1715,7 +2404,11 @@ The *filter*, *sort* and *reverse* transforms are also available as functions, a Plot.sort("body_mass_g", options) // show data in ascending body mass order ``` -Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. +Sorts the data by the specified *order*, which can be an accessor function, a +comparator function, or a channel value definition such as a field name. See +also [index +sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), +which allows marks to be sorted by a named channel, such as *r* for radius. @@ -1725,7 +2418,9 @@ Sorts the data by the specified *order*, which can be an accessor function, a co Plot.shuffle(options) // show data in random order ``` -Shuffles the data randomly. If a *seed* option is specified, a linear congruential generator with the given seed is used to generate random numbers deterministically; otherwise, Math.random is used. +Shuffles the data randomly. If a *seed* option is specified, a linear +congruential generator with the given seed is used to generate random numbers +deterministically; otherwise, Math.random is used. @@ -1745,7 +2440,9 @@ Reverses the order of the data. Plot.filter(d => d.body_mass_g > 3000, options) // show data whose body mass is greater than 3kg ``` -Filters the data given the specified *test*. The test can be given as an accessor function (which receives the datum and index), or as a channel value definition such as a field name; truthy values are retained. +Filters the data given the specified *test*. The test can be given as an +accessor function (which receives the datum and index), or as a channel value +definition such as a field name; truthy values are retained. @@ -1877,7 +2574,8 @@ Lastly, the bin transform changes the default [mark insets](#marks): rather than Plot.rect(athletes, Plot.bin({fillOpacity: "count"}, {x: "weight", y: "height"})) ``` -Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke*, if any. +Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or +*stroke*, if any. @@ -1888,7 +2586,8 @@ Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. +Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or +*stroke*, if any. @@ -1899,7 +2598,8 @@ Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. +Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or +*stroke*, if any. @@ -1984,7 +2684,8 @@ If any of **z**, **fill**, or **stroke** is a channel, the first of these channe Plot.group({fill: "count"}, {x: "island", y: "species"}) ``` -Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if any. +Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if +any. @@ -2017,7 +2718,9 @@ Groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. Plot.groupZ({x: "proportion"}, {fill: "species"}) ``` -Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of *z*, *fill*, or *stroke* are channels, then all data (within each facet) is placed into a single group. +Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of +*z*, *fill*, or *stroke* are channels, then all data (within each facet) is +placed into a single group. @@ -2092,7 +2795,10 @@ By default, **anchor** is *middle* and **reduce** is *mean*. Plot.map({y: "cumsum"}, {y: d3.randomNormal()}) ``` -Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for each channel declared in the specified *outputs* object, applies the corresponding map method. Each channel in *outputs* must have a corresponding input channel in *options*. +Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for +each channel declared in the specified *outputs* object, applies the +corresponding map method. Each channel in *outputs* must have a corresponding +input channel in *options*. @@ -2103,7 +2809,8 @@ Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for ea Plot.mapX("cumsum", {x: d3.randomNormal()}) ``` -Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores any of **x**, **x1**, and **x2** not present in *options*. +Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but +ignores any of **x**, **x1**, and **x2** not present in *options*. @@ -2114,7 +2821,8 @@ Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores Plot.mapY("cumsum", {y: d3.randomNormal()}) ``` -Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. +Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but +ignores any of **y**, **y1**, and **y2** not present in *options*. @@ -2125,7 +2833,8 @@ Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores Plot.map({y: Plot.normalize("first")}, {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Returns a normalize map method for the given *basis*, suitable for use with Plot.map. +Returns a normalize map method for the given *basis*, suitable for use with +Plot.map. @@ -2135,7 +2844,9 @@ Returns a normalize map method for the given *basis*, suitable for use with Plot Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), but applies the normalize map method with the given *basis*. +Like +[Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), +but applies the normalize map method with the given *basis*. @@ -2145,7 +2856,9 @@ Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotma Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), but applies the normalize map method with the given *basis*. +Like +[Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), +but applies the normalize map method with the given *basis*. @@ -2185,9 +2898,17 @@ The select transform derives a filtered mark index; it does not affect the mark #### Plot.select(*selector*, *options*) -Selects the points of each series selected by the *selector*, which can be specified either as a function which receives as input the index of the series, the shorthand “first” or “last”, or as a {*key*: *value*} object with exactly one *key* being the name of a channel and the *value* being a function which receives as input the index of the series and the channel values. The *value* may alternatively be specified as the shorthand “min” and “max” which respectively select the minimum and maximum points for the specified channel. +Selects the points of each series selected by the *selector*, which can be +specified either as a function which receives as input the index of the +series, the shorthand “first” or “last”, or as a {*key*: *value*} object with +exactly one *key* being the name of a channel and the *value* being a +function which receives as input the index of the series and the channel +values. The *value* may alternatively be specified as the shorthand “min” and +“max” which respectively select the minimum and maximum points for the +specified channel. -For example, to select the point within each series that is the closest to the median of the *y* channel: +For example, to select the point within each series that is the closest to +the median of the *y* channel: ```js Plot.select({ @@ -2305,7 +3026,13 @@ If two arguments are passed to the stack transform functions below, the stack-sp Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Creates new channels **y1** and **y2**, obtained by stacking the original **y** channel for data points that share a common **x** (and possibly **z**) value. A new **y** channel is also returned, which lazily computes the middle value of **y1** and **y2**. The input **y** channel defaults to a constant 1, resulting in a count of the data points. The stack options (*offset*, *order*, and *reverse*) may be specified as part of the *options* object, if the only argument, or as a separate *stack* options argument. +Creates new channels **y1** and **y2**, obtained by stacking the original +**y** channel for data points that share a common **x** (and possibly **z**) +value. A new **y** channel is also returned, which lazily computes the middle +value of **y1** and **y2**. The input **y** channel defaults to a constant 1, +resulting in a count of the data points. The stack options (*offset*, +*order*, and *reverse*) may be specified as part of the *options* object, if +the only argument, or as a separate *stack* options argument. @@ -2316,7 +3043,10 @@ Creates new channels **y1** and **y2**, obtained by stacking the original **y** Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. +Equivalent to +[Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), +except that the **y1** channel is returned as the **y** channel. This can be +used, for example, to draw a line at the bottom of each stacked area. @@ -2338,7 +3071,8 @@ Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/READM Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -See Plot.stackY, but with *x* as the input value channel, *y* as the stack index, *x1*, *x2* and *x* as the output channels. +See Plot.stackY, but with *x* as the input value channel, *y* as the stack +index, *x1*, *x2* and *x* as the output channels. @@ -2349,7 +3083,10 @@ See Plot.stackY, but with *x* as the input value channel, *y* as the stack index Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. +Equivalent to +[Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), +except that the **x1** channel is returned as the **x** channel. This can be +used, for example, to draw a line at the left edge of each stacked area. @@ -2360,7 +3097,10 @@ Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/READM Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. +Equivalent to +[Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), +except that the **x2** channel is returned as the **x** channel. This can be +used, for example, to draw a line at the right edge of each stacked area. @@ -2423,9 +3163,18 @@ The default **treeLayout** implements the Reingold–Tilford “tidy” algorith #### Plot.treeNode(*options*) -Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), [text](https://github.com/observablehq/plot/blob/main/README.md#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). +Based on the tree options described above, populates the **x** and **y** +channels with the positions for each node. The following defaults are also +applied: the default **frameAnchor** inherits the **treeAnchor**. This +transform is intended to be used with +[dot](https://github.com/observablehq/plot/blob/main/README.md#dot), +[text](https://github.com/observablehq/plot/blob/main/README.md#text), and +other point-based marks. This transform is rarely used directly; see the +[Plot.tree compound +mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). -The treeNode transform will derive output columns for any *options* that have one of the following named node values: +The treeNode transform will derive output columns for any *options* that have +one of the following named node values: * *node:name* - the node’s name (the last part of its path) * *node:path* - the node’s full, normalized, slash-separated path @@ -2433,15 +3182,27 @@ The treeNode transform will derive output columns for any *options* that have on * *node:depth* - the distance from the node to the root * *node:height* - the distance from the node to its deepest descendant -In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each node in the tree. +In addition, if any option value is specified as an object with a **node** +method, a derived output column will be generated by invoking the **node** +method for each node in the tree. #### Plot.treeLink(*options*) -Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](https://github.com/observablehq/plot/blob/main/README.md#link), [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). +Based on the tree options described above, populates the **x1**, **y1**, +**x2**, and **y2** channels. The following defaults are also applied: the +default **curve** is *bump-x*, the default **stroke** is #555, the default +**strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This +transform is intended to be used with +[link](https://github.com/observablehq/plot/blob/main/README.md#link), +[arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and +other two-point-based marks. This transform is rarely used directly; see the +[Plot.tree compound +mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). -The treeLink transform will derive output columns for any *options* that have one of the following named link values: +The treeLink transform will derive output columns for any *options* that have +one of the following named link values: * *node:name* - the child node’s name (the last part of its path) * *node:path* - the child node’s full, normalized, slash-separated path @@ -2451,15 +3212,31 @@ The treeLink transform will derive output columns for any *options* that have on * *parent:name* - the parent node’s name (the last part of its path) * *parent:path* - the parent node’s full, normalized, slash-separated path * *parent:depth* - the distance from the parent node to the root -* *parent:height* - the distance from the parent node to its deepest descendant +* *parent:height* - the distance from the parent node to its deepest + descendant -In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each child node in the tree; likewise if any option value is specified as an object with a **link** method, a derived output column will be generated by invoking the **link** method for each link in the tree, being passed two node arguments, the child and the parent. +In addition, if any option value is specified as an object with a **node** +method, a derived output column will be generated by invoking the **node** +method for each child node in the tree; likewise if any option value is +specified as an object with a **link** method, a derived output column will +be generated by invoking the **link** method for each link in the tree, being +passed two node arguments, the child and the parent. #### Plot.tree(*data*, *options*) -A convenience compound mark for rendering a tree diagram, including a [link](https://github.com/observablehq/plot/blob/main/README.md#link) to render links from parent to child, an optional [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for nodes, and a [text](https://github.com/observablehq/plot/blob/main/README.md#text) for node labels. The link mark uses the [treeLink transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), while the dot and text marks use the [treeNode transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). The following options are supported: +A convenience compound mark for rendering a tree diagram, including a +[link](https://github.com/observablehq/plot/blob/main/README.md#link) to +render links from parent to child, an optional +[dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for +nodes, and a +[text](https://github.com/observablehq/plot/blob/main/README.md#text) for +node labels. The link mark uses the [treeLink +transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), +while the dot and text marks use the [treeNode +transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). +The following options are supported: * **fill** - the dot and text fill color; defaults to *node:internal* * **stroke** - the link stroke color; inherits **fill** by default @@ -2473,20 +3250,26 @@ A convenience compound mark for rendering a tree diagram, including a [link](htt * **marker** - the link start and end marker * **markerStart** - the link start marker * **markerEnd** - the link end marker -* **dot** - if true, whether to render a dot; defaults to false if no link marker +* **dot** - if true, whether to render a dot; defaults to false if no link + marker * **title** - the text and dot title; defaults to *node:path* * **text** - the text label; defaults to *node:name* * **textStroke** - the text stroke; defaults to *white* -* **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 if right-anchored +* **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 + if right-anchored * **dy** - the text vertical offset; defaults to 0 -Any additional *options* are passed through to the constituent link, dot, and text marks and their corresponding treeLink or treeNode transform. +Any additional *options* are passed through to the constituent link, dot, and +text marks and their corresponding treeLink or treeNode transform. #### Plot.cluster(*data*, *options*) -Like [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. +Like +[Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), +except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, +which aligns leaf nodes. @@ -2502,32 +3285,60 @@ Plot provides a few helpers for implementing transforms. #### Plot.valueof(*data*, *value*, *arrayType*) -Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *arrayType* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: +Given an iterable *data* and some *value* accessor, returns an array (a +column) of the specified *arrayType* with the corresponding value of each +element of the data. The *value* accessor may be one of the following types: * a string - corresponding to the field accessor (`d => d[value]`) * an accessor function - called as *arrayType*.from(*data*, *value*) -* a number, Date, or boolean — resulting in an array uniformly filled with the *value* +* a number, Date, or boolean — resulting in an array uniformly filled with + the *value* * an object with a transform method — called as *value*.transform(*data*) * an array of values - returning the same * null or undefined - returning the same -If *arrayType* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). +If *arrayType* is specified, it must be Array or a similar class that +implements the +[Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) +interface such as a typed array. When *type* is Array or a typed array class, +the return value of valueof will be an instance of the same (or null or +undefined). If *type* is not specified, valueof may return either an array or +a typed array (or null or undefined). -Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *arrayType*, the array may be returned as-is without making a copy. +Plot.valueof is not guaranteed to return a new array. When a transform method +is used, or when the given *value* is an array that is compatible with the +requested *arrayType*, the array may be returned as-is without making a copy. #### Plot.transform(*options*, *transform*) -Given an *options* object that may specify some basic transforms (*filter*, *sort*, or *reverse*) or a custom *transform* function, composes those transforms if any with the given *transform* function, returning a new *options* object. If a custom *transform* function is present on the given *options*, any basic transforms are ignored. Any additional input *options* are passed through in the returned *options* object. This method facilitates applying the basic transforms prior to applying the given custom *transform* and is used internally by Plot’s built-in transforms. +Given an *options* object that may specify some basic transforms (*filter*, +*sort*, or *reverse*) or a custom *transform* function, composes those +transforms if any with the given *transform* function, returning a new +*options* object. If a custom *transform* function is present on the given +*options*, any basic transforms are ignored. Any additional input *options* +are passed through in the returned *options* object. This method facilitates +applying the basic transforms prior to applying the given custom *transform* +and is used internally by Plot’s built-in transforms. #### Plot.column(*source*) -This helper for constructing derived columns returns a [*column*, *setColumn*] array. The *column* object implements *column*.transform, returning whatever value was most recently passed to *setColumn*. If *setColumn* is not called, then *column*.transform returns undefined. If a *source* is specified, then *column*.label exposes the given *source*’s label, if any: if *source* is a string as when representing a named field of data, then *column*.label is *source*; otherwise *column*.label propagates *source*.label. This allows derived columns to propagate a human-readable axis or legend label. +This helper for constructing derived columns returns a [*column*, +*setColumn*] array. The *column* object implements *column*.transform, +returning whatever value was most recently passed to *setColumn*. If +*setColumn* is not called, then *column*.transform returns undefined. If a +*source* is specified, then *column*.label exposes the given *source*’s +label, if any: if *source* is a string as when representing a named field of +data, then *column*.label is *source*; otherwise *column*.label propagates +*source*.label. This allows derived columns to propagate a human-readable +axis or legend label. -Plot.column is typically used by options transforms to define new channels; the associated columns are populated (derived) when the **transform** option function is invoked. +Plot.column is typically used by options transforms to define new channels; +the associated columns are populated (derived) when the **transform** option +function is invoked. @@ -2556,7 +3367,9 @@ The dodge layout is highly dependent on the input data order: the circles placed Plot.dodgeY({x: "date"}) ``` -Given marks arranged along the *x* axis, the dodgeY transform piles them vertically by defining a *y* position channel that avoids overlapping. The *x* position channel is unchanged. +Given marks arranged along the *x* axis, the dodgeY transform piles them +vertically by defining a *y* position channel that avoids overlapping. The +*x* position channel is unchanged. @@ -2567,7 +3380,9 @@ Given marks arranged along the *x* axis, the dodgeY transform piles them vertica Plot.dodgeX({y: "value"}) ``` -Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. +Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* +position channel that avoids overlapping. The *y* position channel is +unchanged. @@ -2582,7 +3397,16 @@ Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position #### Plot.hexbin(*outputs*, *options*) -Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](https://github.com/observablehq/plot/blob/main/README.md#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. +Aggregates the given input channels into hexagonal bins, creating output +channels with the reduced data. The *options* must specify the **x** and +**y** channels. The **binWidth** option (default 20) defines the distance +between centers of neighboring hexagons in pixels. If any of **z**, **fill**, +or **stroke** is a channel, the first of these channels will be used to +subdivide bins. The *outputs* options are similar to the [bin +transform](https://github.com/observablehq/plot/blob/main/README.md#bin); +each output channel receives as input, for each hexagon, the subset of the +data which has been matched to its center. The outputs object specifies the +aggregation method for each output channel. The following aggregation methods are supported: @@ -2591,7 +3415,8 @@ The following aggregation methods are supported: * *count* - the number of elements (frequency) * *distinct* - the number of distinct values * *sum* - the sum of values -* *proportion* - the sum proportional to the overall total (weighted frequency) +* *proportion* - the sum proportional to the overall total (weighted + frequency) * *proportion-facet* - the sum proportional to the facet total * *min* - the minimum value * *min-index* - the zero-based index of the minimum value @@ -2600,12 +3425,16 @@ The following aggregation methods are supported: * *mean* - the mean value (average) * *median* - the median value * *deviation* - the standard deviation -* *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) +* *variance* - the variance per [Welford’s + algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) * *mode* - the value with the most occurrences -* a function to be passed the array of values for each bin and the extent of the bin +* a function to be passed the array of values for each bin and the extent of + the bin * an object with a *reduce* method -See also the [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) mark. +See also the +[hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) +mark. @@ -2619,7 +3448,8 @@ If an initializer desires a channel that is not supported by the downstream mark #### Plot.initializer(*options*, *initializer*) -This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. +This helper composes the *initializer* function with any other transforms +present in the *options*, and returns a new *options* object. @@ -2691,7 +3521,8 @@ These helper functions are provided for use as a *scale*.tickFormat [axis option Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01" ``` -Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the given *date* is not valid, returns `"Invalid Date"`. +Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the +given *date* is not valid, returns `"Invalid Date"`. @@ -2701,7 +3532,12 @@ Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the give Plot.formatWeekday("es-MX", "long")(0) // "domingo" ``` -Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. +Returns a function that formats a given week day number (from 0 = Sunday to 6 += Saturday) according to the specified *locale* and *format*. The *locale* is +a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to +U.S. English. The *format* is a [weekday +format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, +*short*, or *long*; if not specified, it defaults to *short*. @@ -2711,7 +3547,13 @@ Returns a function that formats a given week day number (from 0 = Sunday to 6 = Plot.formatMonth("es-MX", "long")(0) // "enero" ``` -Returns a function that formats a given month number (from 0 = January to 11 = December) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [month format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to *short*. +Returns a function that formats a given month number (from 0 = January to 11 += December) according to the specified *locale* and *format*. The *locale* is +a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to +U.S. English. The *format* is a [month +format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, +*numeric*, *narrow*, *short*, *long*; if not specified, it defaults to +*short*. diff --git a/src/format.ts b/src/format.ts index 7ca5e69040..2757c77754 100644 --- a/src/format.ts +++ b/src/format.ts @@ -25,7 +25,13 @@ export function formatNumber(locale = "en-US"): (value: any) => string | undefin * Plot.formatMonth("es-MX", "long")(0) // "enero" * ``` * - * Returns a function that formats a given month number (from 0 = January to 11 = December) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [month format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to *short*. + * Returns a function that formats a given month number (from 0 = January to 11 + * = December) according to the specified *locale* and *format*. The *locale* is + * a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to + * U.S. English. The *format* is a [month + * format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, + * *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to + * *short*. */ export function formatMonth( locale = "en-US", @@ -41,7 +47,12 @@ export function formatMonth( * Plot.formatWeekday("es-MX", "long")(0) // "domingo" * ``` * - * Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. + * Returns a function that formats a given week day number (from 0 = Sunday to 6 + * = Saturday) according to the specified *locale* and *format*. The *locale* is + * a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to + * U.S. English. The *format* is a [weekday + * format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, + * *short*, or *long*; if not specified, it defaults to *short*. */ export function formatWeekday(locale = "en-US", weekday: "long" | "short" | "narrow" | undefined = "short") { const format = weekdayFormat(locale, weekday); @@ -54,7 +65,8 @@ export function formatWeekday(locale = "en-US", weekday: "long" | "short" | "nar * Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01" * ``` * - * Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the given *date* is not valid, returns `"Invalid Date"`. + * Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the + * given *date* is not valid, returns `"Invalid Date"`. */ export function formatIsoDate(date: Date): string { return isoFormat(date, "Invalid Date"); diff --git a/src/legends.js b/src/legends.js index 306a856bfd..5136bb9248 100644 --- a/src/legends.js +++ b/src/legends.js @@ -12,13 +12,19 @@ const legendRegistry = new Map([ ]); /** - * Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: + * Returns a standalone legend for the scale defined by the given *options* + * object. The *options* object must define at least one scale; see [Scale + * options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) + * for how to define a scale. For example, here is a ramp legend of a linear + * color scale with the default domain of [0, 1] and default scheme *turbo*: * * ```js * Plot.legend({color: {type: "linear"}}) * ``` * - * The *options* object may also include any additional legend options described in the previous section. For example, to make the above legend slightly wider: + * The *options* object may also include any additional legend options described + * in the previous section. For example, to make the above legend slightly + * wider: * * ```js * Plot.legend({ diff --git a/src/marks/area.js b/src/marks/area.js index f0b7a31ec8..1234b16e2c 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -76,7 +76,15 @@ export class Area extends Mark { * Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. + * Returns a new area with the given *data* and *options*. Plot.area is rarely + * used directly; it is only needed when the baseline and topline have neither + * common *x* nor *y* values. + * [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) + * is used in the common horizontal orientation where the baseline and topline + * share *x* values, while + * [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) + * is used in the vertical orientation where the baseline and topline share *y* + * values. */ export function area(data, options) { if (options === undefined) return areaY(data, {x: first, y: second}); @@ -88,15 +96,31 @@ export function area(data, options) { * Plot.areaX(aapl, {y: "Date", x: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. + * Returns a new area with the given *data* and *options*. This constructor is + * used when the baseline and topline share *y* values, as in a time-series area + * chart where time goes up↑. If neither the **x1** nor **x2** option is + * specified, the **x** option may be specified as shorthand to apply an + * implicit [stackX + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); + * this is the typical configuration for an area chart with a baseline at *x* = + * 0. If the **x** option is not specified, it defaults to the identity + * function. The **y** option specifies the **y1** channel; and the **y1** and + * **y2** options are ignored. * - * If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binY + * transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is + * implicitly applied to the specified *options*. The reducer of the output *x* + * channel may be specified via the **reduce** option, which defaults to + * *first*. To default to zero instead of showing gaps in data, as when the + * observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) * ``` * - * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + * The **interval** option is recommended to “regularize” sampled data; for + * example, if your data represents timestamped temperature measurements and you + * expect one sample per day, use d3.utcDay as the interval. */ export function areaX(data, options) { const {y = indexOf, ...rest} = maybeDenseIntervalY(options); @@ -108,15 +132,31 @@ export function areaX(data, options) { * Plot.areaY(aapl, {x: "Date", y: "Close"}) * ``` * - * Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. + * Returns a new area with the given *data* and *options*. This constructor is + * used when the baseline and topline share *x* values, as in a time-series area + * chart where time goes right→. If neither the **y1** nor **y2** option is + * specified, the **y** option may be specified as shorthand to apply an + * implicit [stackY + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); + * this is the typical configuration for an area chart with a baseline at *y* = + * 0. If the **y** option is not specified, it defaults to the identity + * function. The **x** option specifies the **x1** channel; and the **x1** and + * **x2** options are ignored. * - * If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binX + * transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is + * implicitly applied to the specified *options*. The reducer of the output *y* + * channel may be specified via the **reduce** option, which defaults to + * *first*. To default to zero instead of showing gaps in data, as when the + * observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) * ``` * - * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + * The **interval** option is recommended to “regularize” sampled data; for + * example, if your data represents timestamped temperature measurements and you + * expect one sample per day, use d3.utcDay as the interval. */ export function areaY(data, options) { const {x = indexOf, ...rest} = maybeDenseIntervalX(options); diff --git a/src/marks/bar.js b/src/marks/bar.js index f4d3f17bbe..0a3874f981 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -134,20 +134,35 @@ export class BarY extends AbstractBar { * Plot.barX(alphabet, {y: "letter", x: "frequency"}) * ``` * - * Returns a new horizontal bar↔︎ with the given *data* and *options*. The following channels are required: + * Returns a new horizontal bar↔︎ with the given *data* and *options*. The + * following channels are required: * * * **x1** - the starting horizontal position; bound to the *x* scale * * **x2** - the ending horizontal position; bound to the *x* scale * - * If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. + * If neither the **x1** nor **x2** option is specified, the **x** option may be + * specified as shorthand to apply an implicit [stackX + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); + * this is the typical configuration for a horizontal bar chart with bars + * aligned at *x* = 0. If the **x** option is not specified, it defaults to the + * identity function. If *options* is undefined, then it defaults to **x2** as + * the identity function and **y** as the index of data; this allows an array of + * numbers to be passed to Plot.barX to make a quick sequential bar chart. * - * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. + * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be + * derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce + * *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. + * If the interval is specified as a number *n*, *x1* and *x2* are taken as the + * two consecutive multiples of *n* that bracket *x*. * - * In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: + * In addition to the [standard bar + * channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the + * following optional channels are supported: * * * **y** - the vertical position; bound to the *y* scale, which must be *band* * - * If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). + * If the **y** channel is not specified, the bar will span the full vertical + * extent of the plot (or facet). */ export function barX(data, options = {y: indexOf, x2: identity}) { return new BarX(data, maybeStackX(maybeIntervalX(maybeIdentityX(options)))); @@ -158,20 +173,36 @@ export function barX(data, options = {y: indexOf, x2: identity}) { * Plot.barY(alphabet, {x: "letter", y: "frequency"}) * ``` * - * Returns a new vertical bar↕︎ with the given *data* and *options*. The following channels are required: + * Returns a new vertical bar↕︎ with the given *data* and *options*. The + * following channels are required: * * * **y1** - the starting vertical position; bound to the *y* scale * * **y2** - the ending vertical position; bound to the *y* scale * - * If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. + * If neither the **y1** nor **y2** option is specified, the **y** option may be + * specified as shorthand to apply an implicit [stackY + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); + * this is the typical configuration for a vertical bar chart with bars aligned + * at *y* = 0. If the **y** option is not specified, it defaults to the identity + * function. If *options* is undefined, then it defaults to **y2** as the + * identity function and **x** as the index of data; this allows an array of + * numbers to be passed to Plot.barY to make a quick sequential bar chart. * - * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. + * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be + * derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce + * *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. + * If the interval is specified as a number *n*, *y1* and *y2* are taken as the + * two consecutive multiples of *n* that bracket *y*. * - * In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: + * In addition to the [standard bar + * channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the + * following optional channels are supported: * - * * **x** - the horizontal position; bound to the *x* scale, which must be *band* + * * **x** - the horizontal position; bound to the *x* scale, which must be + * *band* * - * If the **x** channel is not specified, the bar will span the full horizontal extent of the plot (or facet). + * If the **x** channel is not specified, the bar will span the full horizontal + * extent of the plot (or facet). */ export function barY(data, options = {x: indexOf, y2: identity}) { return new BarY(data, maybeStackY(maybeIntervalY(maybeIdentityY(options)))); diff --git a/src/marks/box.js b/src/marks/box.js index 80f52dd67f..6f18204d57 100644 --- a/src/marks/box.js +++ b/src/marks/box.js @@ -12,7 +12,10 @@ import {tickX, tickY} from "./tick.js"; * Plot.boxX(simpsons.map(d => d.imdb_rating)) * ``` * - * Returns a horizontal boxplot mark. If the **x** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **y** option is not specified, it defaults to null; if the **y** option is specified, it should represent an ordinal (discrete) value. + * Returns a horizontal boxplot mark. If the **x** option is not specified, it + * defaults to the identity function, as when *data* is an array of numbers. If + * the **y** option is not specified, it defaults to null; if the **y** option + * is specified, it should represent an ordinal (discrete) value. */ export function boxX(data, options = {}) { // Returns a composite mark for producing a horizontal box plot, applying the @@ -42,7 +45,10 @@ export function boxX(data, options = {}) { * Plot.boxY(simpsons.map(d => d.imdb_rating)) * ``` * - * Returns a vertical boxplot mark. If the **y** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **x** option is not specified, it defaults to null; if the **x** option is specified, it should represent an ordinal (discrete) value. + * Returns a vertical boxplot mark. If the **y** option is not specified, it + * defaults to the identity function, as when *data* is an array of numbers. If + * the **x** option is not specified, it defaults to null; if the **x** option + * is specified, it should represent an ordinal (discrete) value. */ export function boxY(data, options = {}) { // Returns a composite mark for producing a vertical box plot, applying the diff --git a/src/marks/cell.js b/src/marks/cell.js index 469898f1b7..6df7557432 100644 --- a/src/marks/cell.js +++ b/src/marks/cell.js @@ -29,7 +29,10 @@ export class Cell extends AbstractBar { * Plot.cell(simpsons, {x: "number_in_season", y: "season", fill: "imdb_rating"}) * ``` * - * Returns a new cell with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new cell with the given *data* and *options*. If neither the **x** + * nor **y** options are specified, *data* is assumed to be an array of pairs + * [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, + * *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function cell(data, options = {}) { let {x, y, ...remainingOptions} = options; @@ -42,7 +45,12 @@ export function cell(data, options = {}) { * Plot.cellX(simpsons.map(d => d.imdb_rating)) * ``` * - * Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * Equivalent to + * [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), + * except that if the **x** option is not specified, it defaults to [0, 1, 2, + * …], and if the **fill** option is not specified and **stroke** is not a + * channel, the fill defaults to the identity function and assumes that *data* = + * [*x₀*, *x₁*, *x₂*, …]. */ export function cellX(data, options = {}) { let {x = indexOf, fill, stroke, ...remainingOptions} = options; @@ -55,7 +63,12 @@ export function cellX(data, options = {}) { * Plot.cellY(simpsons.map(d => d.imdb_rating)) * ``` * - * Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * Equivalent to + * [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), + * except that if the **y** option is not specified, it defaults to [0, 1, 2, + * …], and if the **fill** option is not specified and **stroke** is not a + * channel, the fill defaults to the identity function and assumes that *data* = + * [*y₀*, *y₁*, *y₂*, …]. */ export function cellY(data, options = {}) { let {y = indexOf, fill, stroke, ...remainingOptions} = options; diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index bf187eb1ce..e80603db4f 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -280,45 +280,72 @@ function delaunayMark(DelaunayMark, data, {x, y, ...options} = {}) { } /** - * Draws links for each edge of the Delaunay triangulation of the points given by the **x** and **y** channels. Supports the same options as the [link mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived automatically from **x** and **y**. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the link inherits the corresponding channel value from one of its two endpoints arbitrarily. + * Draws links for each edge of the Delaunay triangulation of the points given + * by the **x** and **y** channels. Supports the same options as the [link + * mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived + * automatically from **x** and **y**. When an aesthetic channel is specified + * (such as **stroke** or **strokeWidth**), the link inherits the corresponding + * channel value from one of its two endpoints arbitrarily. * - * If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + * If a **z** channel is specified, the input points are grouped by *z*, and + * separate Delaunay triangulations are constructed for each group. */ export function delaunayLink(data, options) { return delaunayMark(DelaunayLink, data, options); } /** - * Draws a mesh of the Delaunay triangulation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. + * Draws a mesh of the Delaunay triangulation of the points given by the **x** + * and **y** channels. The **stroke** option defaults to _currentColor_, and the + * **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When + * an aesthetic channel is specified (such as **stroke** or **strokeWidth**), + * the mesh inherits the corresponding channel value from one of its constituent + * points arbitrarily. * - * If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. + * If a **z** channel is specified, the input points are grouped by *z*, and + * separate Delaunay triangulations are constructed for each group. */ export function delaunayMesh(data, options) { return delaunayMark(DelaunayMesh, data, options); } /** - * Draws a convex hull around the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_ and the **fill** option defaults to _none_. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the hull inherits the corresponding channel value from one of its constituent points arbitrarily. + * Draws a convex hull around the points given by the **x** and **y** channels. + * The **stroke** option defaults to _currentColor_ and the **fill** option + * defaults to _none_. When an aesthetic channel is specified (such as + * **stroke** or **strokeWidth**), the hull inherits the corresponding channel + * value from one of its constituent points arbitrarily. * - * If a **z** channel is specified, the input points are grouped by *z*, and separate convex hulls are constructed for each group. If the **z** channel is not specified, it defaults to either the **fill** channel, if any, or the **stroke** channel, if any. + * If a **z** channel is specified, the input points are grouped by *z*, and + * separate convex hulls are constructed for each group. If the **z** channel is + * not specified, it defaults to either the **fill** channel, if any, or the + * **stroke** channel, if any. */ export function hull(data, options) { return delaunayMark(Hull, data, options); } /** - * Draws polygons for each cell of the Voronoi tesselation of the points given by the **x** and **y** channels. + * Draws polygons for each cell of the Voronoi tesselation of the points given + * by the **x** and **y** channels. * - * If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + * If a **z** channel is specified, the input points are grouped by *z*, and + * separate Voronoi tesselations are constructed for each group. */ export function voronoi(data, options) { return delaunayMark(Voronoi, data, options); } /** - * Draws a mesh for the cell boundaries of the Voronoi tesselation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. + * Draws a mesh for the cell boundaries of the Voronoi tesselation of the points + * given by the **x** and **y** channels. The **stroke** option defaults to + * _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** + * option is not supported. When an aesthetic channel is specified (such as + * **stroke** or **strokeWidth**), the mesh inherits the corresponding channel + * value from one of its constituent points arbitrarily. * - * If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. + * If a **z** channel is specified, the input points are grouped by *z*, and + * separate Voronoi tesselations are constructed for each group. */ export function voronoiMesh(data, options) { return delaunayMark(VoronoiMesh, data, options); diff --git a/src/marks/density.js b/src/marks/density.js index 9e5c7c78fa..a8d9cf2d93 100644 --- a/src/marks/density.js +++ b/src/marks/density.js @@ -66,11 +66,24 @@ export class Density extends Mark { } /** - * Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option. + * Draws contours representing the estimated density of the two-dimensional + * points given by the **x** and **y** channels, and possibly weighted by the + * **weight** channel. If either of the **x** or **y** channels are not + * specified, the corresponding position is controlled by the **frameAnchor** + * option. * - * The **thresholds** option, which defaults to 20, specifies one more than the number of contours that will be computed at uniformly-spaced intervals between 0 (exclusive) and the maximum density (exclusive). The **thresholds** option may also be specified as an array or iterable of explicit density values. The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels. + * The **thresholds** option, which defaults to 20, specifies one more than the + * number of contours that will be computed at uniformly-spaced intervals + * between 0 (exclusive) and the maximum density (exclusive). The **thresholds** + * option may also be specified as an array or iterable of explicit density + * values. The **bandwidth** option, which defaults to 20, specifies the + * standard deviation of the Gaussian kernel used for estimation in pixels. * - * If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour. + * If a **z**, **stroke** or **fill** channel is specified, the input points are + * grouped by series, and separate sets of contours are generated for each + * series. If the **stroke** or **fill** is specified as *density*, a color + * channel is constructed with values representing the density threshold value + * of each contour. */ export function density(data, options = {}) { let {x, y, ...remainingOptions} = options; diff --git a/src/marks/dot.js b/src/marks/dot.js index 3c6e83c09d..6b3e380cc5 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -118,7 +118,10 @@ export class Dot extends Mark { * Plot.dot(sales, {x: "units", y: "fruit"}) * ``` * - * Returns a new dot with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new dot with the given *data* and *options*. If neither the **x** + * nor **y** nor **frameAnchor** options are specified, *data* is assumed to be + * an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that + * **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function dot(data, options = {}) { let {x, y, ...remainingOptions} = options; @@ -131,9 +134,15 @@ export function dot(data, options = {}) { * Plot.dotX(cars.map(d => d["economy (mpg)"])) * ``` * - * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * Equivalent to + * [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) + * except that if the **x** option is not specified, it defaults to the identity + * function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. * - * If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + * If an **interval** is specified, such as d3.utcDay, **y** is transformed to + * (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If + * the interval is specified as a number *n*, *y* will be the midpoint of two + * consecutive multiples of *n* that bracket *y*. */ export function dotX(data, options = {}) { const {x = identity, ...remainingOptions} = options; @@ -145,9 +154,15 @@ export function dotX(data, options = {}) { * Plot.dotY(cars.map(d => d["economy (mpg)"])) * ``` * - * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * Equivalent to + * [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) + * except that if the **y** option is not specified, it defaults to the identity + * function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. * - * If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + * If an **interval** is specified, such as d3.utcDay, **x** is transformed to + * (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If + * the interval is specified as a number *n*, *x* will be the midpoint of two + * consecutive multiples of *n* that bracket *x*. */ export function dotY(data, options = {}) { const {y = identity, ...remainingOptions} = options; @@ -155,14 +170,18 @@ export function dotY(data, options = {}) { } /** - * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *circle*. + * Equivalent to + * [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) + * except that the **symbol** option is set to *circle*. */ export function circle(data, options) { return dot(data, {...options, symbol: "circle"}); } /** - * Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *hexagon*. + * Equivalent to + * [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) + * except that the **symbol** option is set to *hexagon*. */ export function hexagon(data, options) { return dot(data, {...options, symbol: "hexagon"}); diff --git a/src/marks/hexgrid.js b/src/marks/hexgrid.js index 98aa23c63d..337a67c786 100644 --- a/src/marks/hexgrid.js +++ b/src/marks/hexgrid.js @@ -13,7 +13,9 @@ const defaults = { }; /** - * The **binWidth** option specifies the distance between the centers of neighboring hexagons, in pixels (defaults to 20). The **clip** option defaults to true, clipping the mark to the frame’s dimensions. + * The **binWidth** option specifies the distance between the centers of + * neighboring hexagons, in pixels (defaults to 20). The **clip** option + * defaults to true, clipping the mark to the frame’s dimensions. */ export function hexgrid(options) { return new Hexgrid(options); diff --git a/src/marks/image.js b/src/marks/image.js index f4f73b5a9f..efe203a40f 100644 --- a/src/marks/image.js +++ b/src/marks/image.js @@ -114,7 +114,10 @@ export class Image extends Mark { * Plot.image(presidents, {x: "inauguration", y: "favorability", src: "portrait"}) * ``` * - * Returns a new image with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new image with the given *data* and *options*. If neither the **x** + * nor **y** nor **frameAnchor** options are specified, *data* is assumed to be + * an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that + * **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function image(data, options = {}) { let {x, y, ...remainingOptions} = options; diff --git a/src/marks/line.js b/src/marks/line.js index 63586afe76..a4db931266 100644 --- a/src/marks/line.js +++ b/src/marks/line.js @@ -75,7 +75,10 @@ export class Line extends Mark { * Plot.line(aapl, {x: "Date", y: "Close"}) * ``` * - * Returns a new line with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new line with the given *data* and *options*. If neither the **x** + * nor **y** options are specified, *data* is assumed to be an array of pairs + * [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, + * *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function line(data, options = {}) { let {x, y, ...remainingOptions} = options; @@ -88,15 +91,26 @@ export function line(data, options = {}) { * Plot.lineX(aapl.map(d => d.Close)) * ``` * - * Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. + * Similar to + * [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) + * except that if the **x** option is not specified, it defaults to the identity + * function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option + * is not specified, it defaults to [0, 1, 2, …]. * - * If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binY + * transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is + * implicitly applied to the specified *options*. The reducer of the output *x* + * channel may be specified via the **reduce** option, which defaults to + * *first*. To default to zero instead of showing gaps in data, as when the + * observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) * ``` * - * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + * The **interval** option is recommended to “regularize” sampled data; for + * example, if your data represents timestamped temperature measurements and you + * expect one sample per day, use d3.utcDay as the interval. */ export function lineX(data, options = {}) { const {x = identity, y = indexOf, ...remainingOptions} = options; @@ -108,15 +122,26 @@ export function lineX(data, options = {}) { * Plot.lineY(aapl.map(d => d.Close)) * ``` * - * Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. + * Similar to + * [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) + * except that if the **y** option is not specified, it defaults to the identity + * function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option + * is not specified, it defaults to [0, 1, 2, …]. * - * If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. + * If the **interval** option is specified, the [binX + * transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is + * implicitly applied to the specified *options*. The reducer of the output *y* + * channel may be specified via the **reduce** option, which defaults to + * *first*. To default to zero instead of showing gaps in data, as when the + * observed value represents a quantity, use the *sum* reducer. * * ```js * Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) * ``` * - * The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. + * The **interval** option is recommended to “regularize” sampled data; for + * example, if your data represents timestamped temperature measurements and you + * expect one sample per day, use d3.utcDay as the interval. */ export function lineY(data, options = {}) { const {x = indexOf, y = identity, ...remainingOptions} = options; diff --git a/src/marks/linearRegression.js b/src/marks/linearRegression.js index b9d13f89ad..7cdd44902d 100644 --- a/src/marks/linearRegression.js +++ b/src/marks/linearRegression.js @@ -127,7 +127,8 @@ class LinearRegressionY extends LinearRegression { * Plot.linearRegressionX(mtcars, {y: "wt", x: "hp"}) * ``` * - * Returns a linear regression mark where *x* is the dependent variable and *y* is the independent variable. + * Returns a linear regression mark where *x* is the dependent variable and *y* + * is the independent variable. */ export function linearRegressionX(data, options = {}) { const { @@ -145,7 +146,8 @@ export function linearRegressionX(data, options = {}) { * Plot.linearRegressionY(mtcars, {x: "wt", y: "hp"}) * ``` * - * Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. + * Returns a linear regression mark where *y* is the dependent variable and *x* + * is the independent variable. */ export function linearRegressionY(data, options = {}) { const { diff --git a/src/marks/rect.js b/src/marks/rect.js index 15cc3d8218..b7a18b0192 100644 --- a/src/marks/rect.js +++ b/src/marks/rect.js @@ -104,7 +104,14 @@ export function rect(data, options) { * Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) * ``` * - * Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. + * Equivalent to + * [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), + * except that if neither the **x1** nor **x2** option is specified, the **x** + * option may be specified as shorthand to apply an implicit [stackX + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); + * this is the typical configuration for a histogram with rects aligned at *x* = + * 0. If the **x** option is not specified, it defaults to the identity + * function. */ export function rectX(data, options = {y: indexOf, interval: 1, x2: identity}) { return new Rect(data, maybeStackX(maybeTrivialIntervalY(maybeIdentityX(options)))); @@ -115,7 +122,14 @@ export function rectX(data, options = {y: indexOf, interval: 1, x2: identity}) { * Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) * ``` * - * Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. + * Equivalent to + * [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), + * except that if neither the **y1** nor **y2** option is specified, the **y** + * option may be specified as shorthand to apply an implicit [stackY + * transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); + * this is the typical configuration for a histogram with rects aligned at *y* = + * 0. If the **y** option is not specified, it defaults to the identity + * function. */ export function rectY(data, options = {x: indexOf, interval: 1, y2: identity}) { return new Rect(data, maybeStackY(maybeTrivialIntervalX(maybeIdentityY(options)))); diff --git a/src/marks/rule.js b/src/marks/rule.js index 8db8f459ce..caedd29d89 100644 --- a/src/marks/rule.js +++ b/src/marks/rule.js @@ -115,15 +115,28 @@ export class RuleY extends Mark { * Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY * ``` * - * Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: + * Returns a new rule↕︎ with the given *data* and *options*. In addition to the + * [standard mark + * options](https://github.com/observablehq/plot/blob/main/README.md#marks), the + * following channels are optional: * * * **x** - the horizontal position; bound to the *x* scale * * **y1** - the starting vertical position; bound to the *y* scale * * **y2** - the ending vertical position; bound to the *y* scale * - * If the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is specified, it is shorthand for the **y2** option with **y1** equal to zero; this is the typical configuration for a vertical lollipop chart with rules aligned at *y* = 0. If the **y1** channel is not specified, the rule will start at the top of the plot (or facet). If the **y2** channel is not specified, the rule will end at the bottom of the plot (or facet). + * If the **x** option is not specified, it defaults to the identity function + * and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is + * specified, it is shorthand for the **y2** option with **y1** equal to zero; + * this is the typical configuration for a vertical lollipop chart with rules + * aligned at *y* = 0. If the **y1** channel is not specified, the rule will + * start at the top of the plot (or facet). If the **y2** channel is not + * specified, the rule will end at the bottom of the plot (or facet). * - * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. + * If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be + * derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce + * *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. + * If the interval is specified as a number *n*, *y1* and *y2* are taken as the + * two consecutive multiples of *n* that bracket *y*. */ export function ruleX(data, options) { let {x = identity, y, y1, y2, ...rest} = maybeIntervalY(options); @@ -135,19 +148,33 @@ export function ruleX(data, options) { * ```js * Plot.ruleY([0]) // as annotation * ``` + * * ```js * Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX * ``` * - * Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: + * Returns a new rule↔︎ with the given *data* and *options*. In addition to the + * [standard mark + * options](https://github.com/observablehq/plot/blob/main/README.md#marks), the + * following channels are optional: * * * **y** - the vertical position; bound to the *y* scale * * **x1** - the starting horizontal position; bound to the *x* scale * * **x2** - the ending horizontal position; bound to the *x* scale * - * If the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is specified, it is shorthand for the **x2** option with **x1** equal to zero; this is the typical configuration for a horizontal lollipop chart with rules aligned at *x* = 0. If the **x1** channel is not specified, the rule will start at the left edge of the plot (or facet). If the **x2** channel is not specified, the rule will end at the right edge of the plot (or facet). + * If the **y** option is not specified, it defaults to the identity function + * and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is + * specified, it is shorthand for the **x2** option with **x1** equal to zero; + * this is the typical configuration for a horizontal lollipop chart with rules + * aligned at *x* = 0. If the **x1** channel is not specified, the rule will + * start at the left edge of the plot (or facet). If the **x2** channel is not + * specified, the rule will end at the right edge of the plot (or facet). * - * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. + * If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be + * derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce + * *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. + * If the interval is specified as a number *n*, *x1* and *x2* are taken as the + * two consecutive multiples of *n* that bracket *x*. */ export function ruleY(data, options) { let {y = identity, x, x1, x2, ...rest} = maybeIntervalX(options); diff --git a/src/marks/text.js b/src/marks/text.js index 739535a8cc..8295272930 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -158,7 +158,10 @@ function applyMultilineText(selection, {monospace, lineAnchor, lineHeight, lineW } /** - * Returns a new text mark with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new text mark with the given *data* and *options*. If neither the + * **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed + * to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such + * that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function text(data, options = {}) { let {x, y, ...remainingOptions} = options; @@ -167,9 +170,15 @@ export function text(data, options = {}) { } /** - * Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * Equivalent to + * [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), + * except **x** defaults to the identity function and assumes that *data* = + * [*x₀*, *x₁*, *x₂*, …]. * - * If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. + * If an **interval** is specified, such as d3.utcDay, **y** is transformed to + * (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If + * the interval is specified as a number *n*, *y* will be the midpoint of two + * consecutive multiples of *n* that bracket *y*. */ export function textX(data, options = {}) { const {x = identity, ...remainingOptions} = options; @@ -177,9 +186,15 @@ export function textX(data, options = {}) { } /** - * Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * Equivalent to + * [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), + * except **y** defaults to the identity function and assumes that *data* = + * [*y₀*, *y₁*, *y₂*, …]. * - * If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. + * If an **interval** is specified, such as d3.utcDay, **x** is transformed to + * (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If + * the interval is specified as a number *n*, *x* will be the midpoint of two + * consecutive multiples of *n* that bracket *x*. */ export function textY(data, options = {}) { const {y = identity, ...remainingOptions} = options; diff --git a/src/marks/tick.js b/src/marks/tick.js index 9e553e853c..9303b7c46d 100644 --- a/src/marks/tick.js +++ b/src/marks/tick.js @@ -105,7 +105,8 @@ export class TickY extends AbstractTick { * Plot.tickX(stateage, {x: "population", y: "age"}) * ``` * - * Returns a new tick↕︎ with the given *data* and *options*. The following channels are required: + * Returns a new tick↕︎ with the given *data* and *options*. The following + * channels are required: * * * **x** - the horizontal position; bound to the *x* scale * @@ -113,7 +114,8 @@ export class TickY extends AbstractTick { * * * **y** - the vertical position; bound to the *y* scale, which must be *band* * - * If the **y** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + * If the **y** channel is not specified, the tick will span the full vertical + * extent of the plot (or facet). */ export function tickX(data, options = {}) { const {x = identity, ...remainingOptions} = options; @@ -125,15 +127,18 @@ export function tickX(data, options = {}) { * Plot.tickY(stateage, {y: "population", x: "age"}) * ``` * - * Returns a new tick↔︎ with the given *data* and *options*. The following channels are required: + * Returns a new tick↔︎ with the given *data* and *options*. The following + * channels are required: * * * **y** - the vertical position; bound to the *y* scale * * The following optional channels are supported: * - * * **x** - the horizontal position; bound to the *x* scale, which must be *band* + * * **x** - the horizontal position; bound to the *x* scale, which must be + * *band* * - * If the **x** channel is not specified, the tick will span the full vertical extent of the plot (or facet). + * If the **x** channel is not specified, the tick will span the full vertical + * extent of the plot (or facet). */ export function tickY(data, options = {}) { const {y = identity, ...remainingOptions} = options; diff --git a/src/marks/tree.js b/src/marks/tree.js index 257378be86..736c5d2338 100644 --- a/src/marks/tree.js +++ b/src/marks/tree.js @@ -7,7 +7,17 @@ import {link} from "./link.js"; import {text} from "./text.js"; /** - * A convenience compound mark for rendering a tree diagram, including a [link](https://github.com/observablehq/plot/blob/main/README.md#link) to render links from parent to child, an optional [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for nodes, and a [text](https://github.com/observablehq/plot/blob/main/README.md#text) for node labels. The link mark uses the [treeLink transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), while the dot and text marks use the [treeNode transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). The following options are supported: + * A convenience compound mark for rendering a tree diagram, including a + * [link](https://github.com/observablehq/plot/blob/main/README.md#link) to + * render links from parent to child, an optional + * [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for + * nodes, and a + * [text](https://github.com/observablehq/plot/blob/main/README.md#text) for + * node labels. The link mark uses the [treeLink + * transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), + * while the dot and text marks use the [treeNode + * transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). + * The following options are supported: * * * **fill** - the dot and text fill color; defaults to *node:internal* * * **stroke** - the link stroke color; inherits **fill** by default @@ -21,14 +31,17 @@ import {text} from "./text.js"; * * **marker** - the link start and end marker * * **markerStart** - the link start marker * * **markerEnd** - the link end marker - * * **dot** - if true, whether to render a dot; defaults to false if no link marker + * * **dot** - if true, whether to render a dot; defaults to false if no link + * marker * * **title** - the text and dot title; defaults to *node:path* * * **text** - the text label; defaults to *node:name* * * **textStroke** - the text stroke; defaults to *white* - * * **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 if right-anchored + * * **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 + * if right-anchored * * **dy** - the text vertical offset; defaults to 0 * - * Any additional *options* are passed through to the constituent link, dot, and text marks and their corresponding treeLink or treeNode transform. + * Any additional *options* are passed through to the constituent link, dot, and + * text marks and their corresponding treeLink or treeNode transform. */ export function tree(data, options = {}) { let { @@ -91,7 +104,10 @@ export function tree(data, options = {}) { } /** - * Like [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. + * Like + * [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), + * except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, + * which aligns leaf nodes. */ export function cluster(data, options) { return tree(data, {...options, treeLayout: Cluster}); diff --git a/src/marks/vector.js b/src/marks/vector.js index 06f1de91d9..7dc00e5700 100644 --- a/src/marks/vector.js +++ b/src/marks/vector.js @@ -79,7 +79,10 @@ export class Vector extends Mark { * Plot.vector(wind, {x: "longitude", y: "latitude", length: "speed", rotate: "direction"}) * ``` * - * Returns a new vector with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. + * Returns a new vector with the given *data* and *options*. If neither the + * **x** nor **y** options are specified, *data* is assumed to be an array of + * pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, + * *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. */ export function vector(data, options = {}) { let {x, y, ...remainingOptions} = options; @@ -88,7 +91,10 @@ export function vector(data, options = {}) { } /** - * Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. + * Equivalent to + * [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) + * except that if the **x** option is not specified, it defaults to the identity + * function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. */ export function vectorX(data, options = {}) { const {x = identity, ...remainingOptions} = options; @@ -96,7 +102,10 @@ export function vectorX(data, options = {}) { } /** - * Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. + * Equivalent to + * [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) + * except that if the **y** option is not specified, it defaults to the identity + * function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. */ export function vectorY(data, options = {}) { const {y = identity, ...remainingOptions} = options; diff --git a/src/options.js b/src/options.js index 454bc87ee6..ff7158ef92 100644 --- a/src/options.js +++ b/src/options.js @@ -6,18 +6,29 @@ const TypedArray = Object.getPrototypeOf(Uint8Array); const objectToString = Object.prototype.toString; /** - * Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *arrayType* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: + * Given an iterable *data* and some *value* accessor, returns an array (a + * column) of the specified *arrayType* with the corresponding value of each + * element of the data. The *value* accessor may be one of the following types: * * * a string - corresponding to the field accessor (`d => d[value]`) * * an accessor function - called as *arrayType*.from(*data*, *value*) - * * a number, Date, or boolean — resulting in an array uniformly filled with the *value* + * * a number, Date, or boolean — resulting in an array uniformly filled with + * the *value* * * an object with a transform method — called as *value*.transform(*data*) * * an array of values - returning the same * * null or undefined - returning the same * - * If *arrayType* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). + * If *arrayType* is specified, it must be Array or a similar class that + * implements the + * [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) + * interface such as a typed array. When *type* is Array or a typed array class, + * the return value of valueof will be an instance of the same (or null or + * undefined). If *type* is not specified, valueof may return either an array or + * a typed array (or null or undefined). * - * Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *arrayType*, the array may be returned as-is without making a copy. + * Plot.valueof is not guaranteed to return a new array. When a transform method + * is used, or when the given *value* is an array that is compatible with the + * requested *arrayType*, the array may be returned as-is without making a copy. */ export function valueof(data, value, arrayType) { const type = typeof value; @@ -207,9 +218,19 @@ export function maybeInput(key, options) { } /** - * This helper for constructing derived columns returns a [*column*, *setColumn*] array. The *column* object implements *column*.transform, returning whatever value was most recently passed to *setColumn*. If *setColumn* is not called, then *column*.transform returns undefined. If a *source* is specified, then *column*.label exposes the given *source*’s label, if any: if *source* is a string as when representing a named field of data, then *column*.label is *source*; otherwise *column*.label propagates *source*.label. This allows derived columns to propagate a human-readable axis or legend label. + * This helper for constructing derived columns returns a [*column*, + * *setColumn*] array. The *column* object implements *column*.transform, + * returning whatever value was most recently passed to *setColumn*. If + * *setColumn* is not called, then *column*.transform returns undefined. If a + * *source* is specified, then *column*.label exposes the given *source*’s + * label, if any: if *source* is a string as when representing a named field of + * data, then *column*.label is *source*; otherwise *column*.label propagates + * *source*.label. This allows derived columns to propagate a human-readable + * axis or legend label. * - * Plot.column is typically used by options transforms to define new channels; the associated columns are populated (derived) when the **transform** option function is invoked. + * Plot.column is typically used by options transforms to define new channels; + * the associated columns are populated (derived) when the **transform** option + * function is invoked. */ export function column(source) { // Defines a column whose values are lazily populated by calling the returned diff --git a/src/plot.js b/src/plot.js index 726c387c62..f31c558aaa 100644 --- a/src/plot.js +++ b/src/plot.js @@ -25,11 +25,24 @@ import {maybeInterval} from "./transforms/interval.js"; import {consumeWarnings} from "./warnings.js"; /** - * Renders a new plot given the specified *options* and returns the corresponding SVG or HTML figure element. All *options* are optional. + * Renders a new plot given the specified *options* and returns the + * corresponding SVG or HTML figure element. All *options* are optional. * * ### Mark options * - * The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). + * The **marks** option specifies an array of + * [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to + * render. Each mark has its own data and options; see the respective mark type + * (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) + * or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for + * which mark options are supported. Each mark may be a nested array of marks, + * allowing composition. Marks may also be a function which returns an SVG + * element, if you wish to insert some arbitrary content into your plot. And + * marks may be null or undefined, which produce no output; this is useful for + * showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn + * in *z* order, last on top. For example, here a single rule at *y* = 0 is + * drawn on top of blue bars for the [*alphabet* + * dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). * * ```js * Plot.plot({ @@ -42,7 +55,8 @@ import {consumeWarnings} from "./warnings.js"; * * ### Layout options * - * These options determine the overall layout of the plot; all are specified as numbers in pixels: + * These options determine the overall layout of the plot; all are specified as + * numbers in pixels: * * * **marginTop** - the top margin * * **marginRight** - the right margin @@ -52,11 +66,39 @@ import {consumeWarnings} from "./warnings.js"; * * **width** - the outer width of the plot (including margins) * * **height** - the outer height of the plot (including margins) * - * The default **width** is 640. On Observable, the width can be set to the [standard width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to make responsive plots. The default **height** is chosen automatically based on the plot’s associated scales; for example, if *y* is linear and there is no *fy* scale, it might be 396. - * - * The default margins depend on the plot’s axes: for example, **marginTop** and **marginBottom** are at least 30 if there is a corresponding top or bottom *x* axis, and **marginLeft** and **marginRight** are at least 40 if there is a corresponding left or right *y* axis. For simplicity’s sake and for consistent layout across plots, margins are not automatically sized to make room for tick labels; instead, shorten your tick labels or increase the margins as needed. (In the future, margins may be specified indirectly via a scale property to make it easier to reorient axes without adjusting margins; see [#210](https://github.com/observablehq/plot/issues/210).) - * - * The **style** option allows custom styles to override Plot’s defaults. It may be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in the same fashion as assigning [*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as assigning [*element*.style properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). Note that unitless numbers ([quirky lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such as `{padding: 20}` may not supported by some browsers; you should instead specify a string with units such as `{padding: "20px"}`. By default, the returned plot has a white background, a max-width of 100%, and the system-ui font. Plot’s marks and axes default to [currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), meaning that they will inherit the surrounding content’s color. For example, a dark theme: + * The default **width** is 640. On Observable, the width can be set to the + * [standard + * width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to + * make responsive plots. The default **height** is chosen automatically based + * on the plot’s associated scales; for example, if *y* is linear and there is + * no *fy* scale, it might be 396. + * + * The default margins depend on the plot’s axes: for example, **marginTop** and + * **marginBottom** are at least 30 if there is a corresponding top or bottom + * *x* axis, and **marginLeft** and **marginRight** are at least 40 if there is + * a corresponding left or right *y* axis. For simplicity’s sake and for + * consistent layout across plots, margins are not automatically sized to make + * room for tick labels; instead, shorten your tick labels or increase the + * margins as needed. (In the future, margins may be specified indirectly via a + * scale property to make it easier to reorient axes without adjusting margins; + * see [#210](https://github.com/observablehq/plot/issues/210).) + * + * The **style** option allows custom styles to override Plot’s defaults. It may + * be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in + * the same fashion as assigning + * [*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) + * or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as + * assigning [*element*.style + * properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). + * Note that unitless numbers ([quirky + * lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such + * as `{padding: 20}` may not supported by some browsers; you should instead + * specify a string with units such as `{padding: "20px"}`. By default, the + * returned plot has a white background, a max-width of 100%, and the system-ui + * font. Plot’s marks and axes default to + * [currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), + * meaning that they will inherit the surrounding content’s color. For example, + * a dark theme: * * ```js * Plot.plot({ @@ -68,7 +110,11 @@ import {consumeWarnings} from "./warnings.js"; * }) * ``` * - * If a **caption** is specified, Plot.plot wraps the generated SVG element in an HTML figure element with a figcaption, returning the figure. To specify an HTML caption, consider using the [`html` tagged template literal](http://github.com/observablehq/htl); otherwise, the specified string represents text that will be escaped as needed. + * If a **caption** is specified, Plot.plot wraps the generated SVG element in + * an HTML figure element with a figcaption, returning the figure. To specify an + * HTML caption, consider using the [`html` tagged template + * literal](http://github.com/observablehq/htl); otherwise, the specified string + * represents text that will be escaped as needed. * * ```js * Plot.plot({ @@ -77,13 +123,26 @@ import {consumeWarnings} from "./warnings.js"; * }) * ``` * - * The generated SVG element has a random class name which applies a default stylesheet. Use the top-level **className** option to specify that class name. + * The generated SVG element has a random class name which applies a default + * stylesheet. Use the top-level **className** option to specify that class + * name. * - * The **document** option specifies the [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to create plot elements. It defaults to window.document, but can be changed to another document, say when using a virtual DOM library for server-side rendering in Node. + * The **document** option specifies the + * [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to + * create plot elements. It defaults to window.document, but can be changed to + * another document, say when using a virtual DOM library for server-side + * rendering in Node. * * ### Scale options * - * Plot passes data through [scales](https://observablehq.com/@observablehq/plot-scales) as needed before rendering marks. A scale maps abstract values such as time or temperature to visual values such as position or color. Within a given plot, marks share scales. For example, if a plot has two Plot.line marks, both share the same *x* and *y* scales for a consistent representation of data. (Plot does not currently support dual-axis charts, which are [not advised](https://blog.datawrapper.de/dualaxis/).) + * Plot passes data through + * [scales](https://observablehq.com/@observablehq/plot-scales) as needed before + * rendering marks. A scale maps abstract values such as time or temperature to + * visual values such as position or color. Within a given plot, marks share + * scales. For example, if a plot has two Plot.line marks, both share the same + * *x* and *y* scales for a consistent representation of data. (Plot does not + * currently support dual-axis charts, which are [not + * advised](https://blog.datawrapper.de/dualaxis/).) * * ```js * Plot.plot({ @@ -94,15 +153,18 @@ import {consumeWarnings} from "./warnings.js"; * }) * ``` * - * Each scale’s options are specified as a nested options object with the corresponding scale name within the top-level plot *options*: + * Each scale’s options are specified as a nested options object with the + * corresponding scale name within the top-level plot *options*: * * * **x** - horizontal position * * **y** - vertical position * * **r** - radius (size) * * **color** - fill or stroke * * **opacity** - fill or stroke opacity - * * **length** - linear length (for [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) - * * **symbol** - categorical symbol (for [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) + * * **length** - linear length (for + * [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) + * * **symbol** - categorical symbol (for + * [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) * * For example, to set the domain for the *x* and *y* scales: * @@ -117,59 +179,171 @@ import {consumeWarnings} from "./warnings.js"; * }) * ``` * - * Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) or [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). - * - * You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. - * - * For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: + * Plot supports many scale types. Some scale types are for quantitative data: + * values that can be added or subtracted, such as temperature or time. Other + * scale types are for ordinal or categorical data: unquantifiable values that + * can only be ordered, such as t-shirt sizes, or values with no inherent order + * that can only be tested for equality, such as types of fruit. Some scale + * types are further intended for specific visual encodings: for example, as + * [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) + * or + * [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). + * + * You can set the scale type explicitly via the *scale*.**type** option, though + * typically the scale type is inferred automatically. Some marks mandate a + * particular scale type: for example, + * [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) + * requires that the *x* scale is a *band* scale. Some scales have a default + * type: for example, the *radius* scale defaults to *sqrt* and the *opacity* + * scale defaults to *linear*. Most often, the scale type is inferred from + * associated data, pulled either from the domain (if specified) or from + * associated channels. A *color* scale defaults to *identity* if no range or + * scheme is specified and all associated defined values are valid CSS color + * strings. Otherwise, strings and booleans imply an ordinal scale; dates imply + * a UTC scale; and anything else is linear. Unless they represent text, we + * recommend explicitly converting strings to more specific types when loading + * data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For + * simplicity’s sake, Plot assumes that data is consistently typed; type + * inference is based solely on the first non-null, non-undefined value. + * + * For quantitative data (*i.e.* numbers), a mathematical transform may be + * applied to the data by changing the scale type: * * * *linear* (default) - linear transform (translate and scale) * * *pow* - power (exponential) transform * * *sqrt* - square-root transform (*pow* transform with exponent = 0.5) * * *log* - logarithmic transform - * * *symlog* - bi-symmetric logarithmic transform per [Webber *et al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) - * - * The appropriate transform depends on the data’s distribution and what you wish to know. A *sqrt* transform exaggerates differences between small values at the expense of large values; it is a special case of the *pow* transform which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* transform is suitable for comparing orders of magnitude and can only be used when the domain does not include zero. The base defaults to 10 and can be specified with the *scale*.**base** option; note that this only affects the axis ticks and not the scale’s behavior. A *symlog* transform is more elaborate, but works well with wide-range values that include zero; it can be configured with the *scale*.**constant** option (default 1). - * - * For temporal data (*i.e.* dates), two variants of a *linear* scale are also supported: + * * *symlog* - bi-symmetric logarithmic transform per [Webber *et + * al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) + * + * The appropriate transform depends on the data’s distribution and what you + * wish to know. A *sqrt* transform exaggerates differences between small values + * at the expense of large values; it is a special case of the *pow* transform + * which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* + * transform is suitable for comparing orders of magnitude and can only be used + * when the domain does not include zero. The base defaults to 10 and can be + * specified with the *scale*.**base** option; note that this only affects the + * axis ticks and not the scale’s behavior. A *symlog* transform is more + * elaborate, but works well with wide-range values that include zero; it can be + * configured with the *scale*.**constant** option (default 1). + * + * For temporal data (*i.e.* dates), two variants of a *linear* scale are also + * supported: * * * *utc* (default, recommended) - UTC time * * *time* - local time * - * UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. - * - * For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](https://github.com/observablehq/plot/blob/main/README.md#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) - * - * You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. - * - * Quantitative scales, as well as identity position scales, coerce channel values to numbers; both null and undefined are coerced to NaN. Similarly, time scales coerce channel values to dates; numbers are assumed to be milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). - * - * A scale’s domain (the extent of its inputs, abstract values) and range (the extent of its outputs, visual values) are typically inferred automatically. You can set them explicitly using these options: - * - * * *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or categorical values - * * *scale*.**range** - typically [*min*, *max*], or an array of ordinal or categorical values - * * *scale*.**unknown** - the desired output value (defaults to undefined) for invalid input values - * * *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* - * * *scale*.**interval** - an interval or time interval (for interval data; see below) - * - * For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. - * - * The default range depends on the scale: for [position scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). For [color scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. - * - * The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. - * - * For data at regular intervals, such as integer values or daily samples, the *scale*.**interval** option can be used to enforce uniformity. The specified *interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), *interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. The option can also be specified as a number, in which case it will be promoted to a numeric interval with the given step. This option sets the default *scale*.transform to the given interval’s *interval*.floor function. In addition, the default *scale*.domain is an array of uniformly-spaced values spanning the extent of the values associated with the scale. + * UTC is recommended over local time as charts in UTC time are guaranteed to + * appear consistently to all viewers whereas charts in local time will depend + * on the viewer’s time zone. Due to limitations in JavaScript’s Date class, + * Plot does not yet support an explicit time zone other than UTC. + * + * For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the + * *point* or *band* [position scale + * types](https://github.com/observablehq/plot/blob/main/README.md#position-options). + * The *categorical* scale type is also supported; it is equivalent to *ordinal* + * except as a [color + * scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), + * where it provides a different default color scheme. (Since position is + * inherently ordinal or even quantitative, categorical data must be assigned an + * effective order when represented as position, and hence *categorical* and + * *ordinal* may be considered synonymous in context.) + * + * You can opt-out of a scale using the *identity* scale type. This is useful if + * you wish to specify literal colors or pixel positions within a mark channel + * rather than relying on the scale to convert abstract values into visual + * values. For position scales (*x* and *y*), an *identity* scale is still + * quantitative and may produce an axis, yet unlike a *linear* scale the domain + * and range are fixed based on the plot layout. + * + * Quantitative scales, as well as identity position scales, coerce channel + * values to numbers; both null and undefined are coerced to NaN. Similarly, + * time scales coerce channel values to dates; numbers are assumed to be + * milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 + * format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). + * + * A scale’s domain (the extent of its inputs, abstract values) and range (the + * extent of its outputs, visual values) are typically inferred automatically. + * You can set them explicitly using these options: + * + * * *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or + * categorical values + * * *scale*.**range** - typically [*min*, *max*], or an array of ordinal or + * categorical values + * * *scale*.**unknown** - the desired output value (defaults to undefined) for + * invalid input values + * * *scale*.**reverse** - reverses the domain (or in somes cases, the range), + * say to flip the chart along *x* or *y* + * * *scale*.**interval** - an interval or time interval (for interval data; see + * below) + * + * For most quantitative scales, the default domain is the [*min*, *max*] of all + * values associated with the scale. For the *radius* and *opacity* scales, the + * default domain is [0, *max*] to ensure a meaningful value encoding. For + * ordinal scales, the default domain is the set of all distinct values + * associated with the scale in natural ascending order; for a different order, + * set the domain explicitly or add a [sort + * option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) + * to an associated mark. For threshold scales, the default domain is [0] to + * separate negative and non-negative values. For quantile scales, the default + * domain is the set of all defined values associated with the scale. If a scale + * is reversed, it is equivalent to setting the domain as [*max*, *min*] instead + * of [*min*, *max*]. + * + * The default range depends on the scale: for [position + * scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) + * (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and + * margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). + * For [color + * scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), + * there are default color schemes for quantitative, ordinal, and categorical + * data. For opacity, the default range is [0, 1]. And for radius, the default + * range is designed to produce dots of “reasonable” size assuming a *sqrt* + * scale type for accurate area representation: zero maps to zero, the first + * quartile maps to a radius of three pixels, and other values are extrapolated. + * This convention for radius ensures that if the scale’s data values are all + * equal, dots have the default constant radius of three pixels, while if the + * data varies, dots will tend to be larger. + * + * The behavior of the *scale*.**unknown** option depends on the scale type. For + * quantitative and temporal scales, the unknown value is used whenever the + * input value is undefined, null, or NaN. For ordinal or categorical scales, + * the unknown value is returned for any input value outside the domain. For + * band or point scales, the unknown option has no effect; it is effectively + * always equal to undefined. If the unknown option is set to undefined (the + * default), or null or NaN, then the affected input values will be considered + * undefined and filtered from the output. + * + * For data at regular intervals, such as integer values or daily samples, the + * *scale*.**interval** option can be used to enforce uniformity. The specified + * *interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), + * *interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. + * The option can also be specified as a number, in which case it will be + * promoted to a numeric interval with the given step. This option sets the + * default *scale*.transform to the given interval’s *interval*.floor function. + * In addition, the default *scale*.domain is an array of uniformly-spaced + * values spanning the extent of the values associated with the scale. * * Quantitative scales can be further customized with additional options: * * * *scale*.**clamp** - if true, clamp input values to the scale’s domain - * * *scale*.**nice** - if true (or a tick count), extend the domain to nice round values + * * *scale*.**nice** - if true (or a tick count), extend the domain to nice + * round values * * *scale*.**zero** - if true, extend the domain to include zero if needed - * * *scale*.**percent** - if true, transform proportions in [0, 1] to percentages in [0, 100] + * * *scale*.**percent** - if true, transform proportions in [0, 1] to + * percentages in [0, 100] * - * Clamping is typically used in conjunction with setting an explicit domain since if the domain is inferred, no values will be outside the domain. Clamping is useful for focusing on a subset of the data while ensuring that extreme values remain visible, but use caution: clamped values may need an annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and **zero** options are supported as shorthand for setting the respective option on all scales. + * Clamping is typically used in conjunction with setting an explicit domain + * since if the domain is inferred, no values will be outside the domain. + * Clamping is useful for focusing on a subset of the data while ensuring that + * extreme values remain visible, but use caution: clamped values may need an + * annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and + * **zero** options are supported as shorthand for setting the respective option + * on all scales. * - * The *scale*.**transform** option allows you to apply a function to all values before they are passed through the scale. This is convenient for transforming a scale’s data, say to convert to thousands or between temperature units. + * The *scale*.**transform** option allows you to apply a function to all values + * before they are passed through the scale. This is convenient for transforming + * a scale’s data, say to convert to thousands or between temperature units. * * ```js * Plot.plot({ @@ -183,7 +357,11 @@ import {consumeWarnings} from "./warnings.js"; * * #### *plot*.scale(*scaleName*) * - * Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) function of a returned plot. The *scaleName* must be one of the known scale names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, `"symbol"`, or `"length"`. If the associated *plot* has no scale with the given *scaleName*, returns undefined. + * Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) + * function of a returned plot. The *scaleName* must be one of the known scale + * names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, + * `"symbol"`, or `"length"`. If the associated *plot* has no scale with the + * given *scaleName*, returns undefined. * * ```js * const plot = Plot.plot(…); // render a plot @@ -517,7 +695,11 @@ export class Mark { } /** - * A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. + * A convenience method for composing a mark from a series of other marks. + * Returns an array of marks that implements the *mark*.plot function. See the + * [box mark + * implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) + * for an example. */ export function marks(...marks) { marks.plot = Mark.prototype.plot; diff --git a/src/scales.js b/src/scales.js index acda64ab30..7d46e28b50 100644 --- a/src/scales.js +++ b/src/scales.js @@ -472,7 +472,11 @@ export function coerceDate(x) { } /** - * You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: + * You can also create a standalone scale with Plot.**scale**(*options*). The + * *options* object must define at least one scale; see [Scale + * options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) + * for how to define a scale. For example, here is a linear color scale with the + * default domain of [0, 1] and default scheme *turbo*: * * ```js * const color = Plot.scale({color: {type: "linear"}}); @@ -480,163 +484,312 @@ export function coerceDate(x) { * * #### Scale objects * - * Both [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) and [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. + * Both + * [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) + * and + * [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) + * return scale objects. These objects represent the actual (or “materialized”) + * scale options used by Plot, including the domain, range, interpolate + * function, *etc.* The scale’s label, if any, is also returned; however, note + * that other axis properties are not currently exposed. Point and band scales + * also expose their materialized bandwidth and step. * - * To reuse a scale across plots, pass the corresponding scale object into another plot specification: + * To reuse a scale across plots, pass the corresponding scale object into + * another plot specification: * * ```js * const plot1 = Plot.plot(…); * const plot2 = Plot.plot({…, color: plot1.scale("color")}); * ``` * - * For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. + * For convenience, scale objects expose a *scale*.**apply**(*input*) method + * which returns the scale’s output for the given *input* value. When + * applicable, scale objects also expose a *scale*.**invert**(*output*) method + * which returns the corresponding input value from the scale’s domain for the + * given *output* value. * * ### Position options * * The position scales (*x*, *y*, *fx*, and *fy*) support additional options: * - * * *scale*.**inset** - inset the default range by the specified amount in pixels - * * *scale*.**round** - round the output value to the nearest integer (whole pixel) + * * *scale*.**inset** - inset the default range by the specified amount in + * pixels + * * *scale*.**round** - round the output value to the nearest integer (whole + * pixel) * - * The *x* and *fx* scales support asymmetric insets for more precision. Replace inset by: + * The *x* and *fx* scales support asymmetric insets for more precision. Replace + * inset by: * - * * *scale*.**insetLeft** - insets the start of the default range by the specified number of pixels - * * *scale*.**insetRight** - insets the end of the default range by the specified number of pixels + * * *scale*.**insetLeft** - insets the start of the default range by the + * specified number of pixels + * * *scale*.**insetRight** - insets the end of the default range by the + * specified number of pixels * * Similarly, the *y* and *fy* scales support asymmetric insets with: * - * * *scale*.**insetTop** - insets the top of the default range by the specified number of pixels - * * *scale*.**insetBottom** - insets the bottom of the default range by the specified number of pixels - * - * The inset scale options can provide “breathing room” to separate marks from axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels prevents dots from overlapping with the axes. The *scale*.round option is useful for crisp edges by rounding to the nearest pixel boundary. - * - * In addition to the generic *ordinal* scale type, which requires an explicit output range value for each input domain value, Plot supports special *point* and *band* scale types for encoding ordinal data as position. These scale types accept a [*min*, *max*] range similar to quantitative scales, and divide this continuous interval into discrete points or bands based on the number of distinct values in the domain (*i.e.*, the domain’s cardinality). If the associated marks have no effective width along the ordinal dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, say for a bar, use a *band* scale. In the image below, the top *x* scale is a *point* scale while the bottom *x* scale is a *band* scale; see [Plot: Scales](https://observablehq.com/@observablehq/plot-scales) for an interactive version. - * - * point and band scales - * - * Ordinal position scales support additional options, all specified as proportions in [0, 1]: - * - * * *scale*.**padding** - how much of the range to reserve to inset first and last point or band - * * *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 = at middle, 1 = at end) + * * *scale*.**insetTop** - insets the top of the default range by the specified + * number of pixels + * * *scale*.**insetBottom** - insets the bottom of the default range by the + * specified number of pixels + * + * The inset scale options can provide “breathing room” to separate marks from + * axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with + * the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels + * prevents dots from overlapping with the axes. The *scale*.round option is + * useful for crisp edges by rounding to the nearest pixel boundary. + * + * In addition to the generic *ordinal* scale type, which requires an explicit + * output range value for each input domain value, Plot supports special *point* + * and *band* scale types for encoding ordinal data as position. These scale + * types accept a [*min*, *max*] range similar to quantitative scales, and + * divide this continuous interval into discrete points or bands based on the + * number of distinct values in the domain (*i.e.*, the domain’s cardinality). + * If the associated marks have no effective width along the ordinal + * dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, + * say for a bar, use a *band* scale. In the image below, the top *x* scale is a + * *point* scale while the bottom *x* scale is a *band* scale; see [Plot: + * Scales](https://observablehq.com/@observablehq/plot-scales) for an + * interactive version. + * + * point and band
+ * scales + * + * Ordinal position scales support additional options, all specified as + * proportions in [0, 1]: + * + * * *scale*.**padding** - how much of the range to reserve to inset first and + * last point or band + * * *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 + * = at middle, 1 = at end) * * For a *band* scale, you can further fine-tune padding: * - * * *scale*.**paddingInner** - how much of the range to reserve to separate adjacent bands - * * *scale*.**paddingOuter** - how much of the range to reserve to inset first and last band + * * *scale*.**paddingInner** - how much of the range to reserve to separate + * adjacent bands + * * *scale*.**paddingOuter** - how much of the range to reserve to inset first + * and last band * - * Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of available space reserved for separating bands), while point scale padding defaults to 0.5 (the gap between the first point and the edge is half the distance of the gap between points, and likewise for the gap between the last point and the opposite edge). Note that rounding and mark insets (e.g., for bars and rects) also affect separation between adjacent marks. + * Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of + * available space reserved for separating bands), while point scale padding + * defaults to 0.5 (the gap between the first point and the edge is half the + * distance of the gap between points, and likewise for the gap between the last + * point and the opposite edge). Note that rounding and mark insets (e.g., for + * bars and rects) also affect separation between adjacent marks. * - * Plot automatically generates axes for position scales. You can configure these axes with the following options: + * Plot automatically generates axes for position scales. You can configure + * these axes with the following options: * - * * *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or *right* for *y*; null to suppress + * * *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or + * *right* for *y*; null to suppress * * *scale*.**ticks** - the approximate number of ticks to generate * * *scale*.**tickSize** - the size of each tick (in pixels; default 6) - * * *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) - * * *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) - * * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) + * * *scale*.**tickPadding** - the separation between the tick and its label (in + * pixels; default 3) + * * *scale*.**tickFormat** - to format tick values, either a function or format + * specifier string; see + * [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) + * * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees + * clockwise; default 0) * * *scale*.**grid** - if true, draw grid lines across the plot for each tick * * *scale*.**line** - if true, draw the axis line * * *scale*.**label** - a string to label the axis - * * *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, *left*, or *center* - * * *scale*.**labelOffset** - the label position offset (in pixels; default 0, typically for facet axes) - * * *scale*.**fontVariant** - the font-variant attribute for axis ticks; defaults to tabular-nums for quantitative axes - * * *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree + * * *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, + * *left*, or *center* + * * *scale*.**labelOffset** - the label position offset (in pixels; default 0, + * typically for facet axes) + * * *scale*.**fontVariant** - the font-variant attribute for axis ticks; + * defaults to tabular-nums for quantitative axes + * * *scale*.**ariaLabel** - a short label representing the axis in the + * accessibility tree * * *scale*.**ariaDescription** - a textual description for the axis * - * Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. + * Top-level options are also supported as shorthand: **grid** (for *x* and *y* + * only; see + * [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), + * **label**, **axis**, **inset**, **round**, **align**, and **padding**. * * ### Color options * - * The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordinal*—can be used to encode color. In addition, Plot supports special scale types for color: + * The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and + * *ordinal*—can be used to encode color. In addition, Plot supports special + * scale types for color: * - * * *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* scheme + * * *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* + * scheme * * *sequential* - equivalent to *linear* * * *cyclical* - equivalent to *linear*, but defaults to the *rainbow* scheme - * * *threshold* - encodes based on the specified discrete thresholds; defaults to the *rdylbu* scheme - * * *quantile* - encodes based on the computed quantile thresholds; defaults to the *rdylbu* scheme - * * *quantize* - transforms a continuous domain into quantized thresholds; defaults to the *rdylbu* scheme - * * *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults to the *rdbu* scheme - * * *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-symlog* - like *symlog*, but with a pivot; defaults to the *rdbu* scheme - * - * For a *threshold* scale, the *domain* represents *n* (typically numeric) thresholds which will produce a *range* of *n* + 1 output colors; the *i*th color of the *range* applies to values that are smaller than the *i*th element of the domain and larger or equal to the *i* - 1th element of the domain. For a *quantile* scale, the *domain* represents all input values to the scale, and the *n* option specifies how many quantiles to compute from the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output range of *n* colors. For a *quantize* scale, the domain will be transformed into approximately *n* quantized values, where *n* is an option that defaults to 5. - * - * By default, all diverging color scales are symmetric around the pivot; set *symmetric* to false if you want to cover the whole extent on both sides. + * * *threshold* - encodes based on the specified discrete thresholds; defaults + * to the *rdylbu* scheme + * * *quantile* - encodes based on the computed quantile thresholds; defaults to + * the *rdylbu* scheme + * * *quantize* - transforms a continuous domain into quantized thresholds; + * defaults to the *rdylbu* scheme + * * *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* + * scheme + * * *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults + * to the *rdbu* scheme + * * *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* + * scheme + * * *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* + * scheme + * * *diverging-symlog* - like *symlog*, but with a pivot; defaults to the + * *rdbu* scheme + * + * For a *threshold* scale, the *domain* represents *n* (typically numeric) + * thresholds which will produce a *range* of *n* + 1 output colors; the *i*th + * color of the *range* applies to values that are smaller than the *i*th + * element of the domain and larger or equal to the *i* - 1th element of the + * domain. For a *quantile* scale, the *domain* represents all input values to + * the scale, and the *n* option specifies how many quantiles to compute from + * the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output + * range of *n* colors. For a *quantize* scale, the domain will be transformed + * into approximately *n* quantized values, where *n* is an option that defaults + * to 5. + * + * By default, all diverging color scales are symmetric around the pivot; set + * *symmetric* to false if you want to cover the whole extent on both sides. * * Color scales support two additional options: * - * * *scale*.**scheme** - a named color scheme in lieu of a range, such as *reds* - * * *scale*.**interpolate** - in conjunction with a range, how to interpolate colors - * - * For quantile and quantize color scales, the *scale*.scheme option is used in conjunction with *scale*.**n**, which determines how many quantiles or quantized values to compute, and thus the number of elements in the scale’s range; it defaults to 5 (for quintiles in the case of a quantile scale). - * - * The following sequential scale schemes are supported for both quantitative and ordinal data: - * - * * blues *blues* - * * greens *greens* - * * greys *greys* - * * oranges *oranges* - * * purples *purples* - * * reds *reds* - * * bugn *bugn* - * * bupu *bupu* - * * gnbu *gnbu* - * * orrd *orrd* - * * pubu *pubu* - * * pubugn *pubugn* - * * purd *purd* - * * rdpu *rdpu* - * * ylgn *ylgn* - * * ylgnbu *ylgnbu* - * * ylorbr *ylorbr* - * * ylorrd *ylorrd* - * * cividis *cividis* - * * inferno *inferno* - * * magma *magma* - * * plasma *plasma* - * * viridis *viridis* - * * cubehelix *cubehelix* - * * turbo *turbo* - * * warm *warm* - * * cool *cool* - * - * The default color scheme, *turbo*, was chosen primarily to ensure high-contrast visibility. Color schemes such as *blues* make low-value marks difficult to see against a white background, for better or for worse. To use a subset of a continuous color scheme (or any single-argument *interpolate* function), set the *scale*.range property to the corresponding subset of [0, 1]; for example, to use the first half of the *rainbow* color scheme, use a range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to encode a quantitative value without hue, consider using *opacity* rather than *color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). + * * *scale*.**scheme** - a named color scheme in lieu of a range, such as + * *reds* + * * *scale*.**interpolate** - in conjunction with a range, how to interpolate + * colors + * + * For quantile and quantize color scales, the *scale*.scheme option is used in + * conjunction with *scale*.**n**, which determines how many quantiles or + * quantized values to compute, and thus the number of elements in the scale’s + * range; it defaults to 5 (for quintiles in the case of a quantile scale). + * + * The following sequential scale schemes are supported for both quantitative + * and ordinal data: + * + * * blues + * *blues* + * * greens + * *greens* + * * greys + * *greys* + * * oranges *oranges* + * * purples *purples* + * * reds + * *reds* + * * bugn + * *bugn* + * * bupu + * *bupu* + * * gnbu + * *gnbu* + * * orrd + * *orrd* + * * pubu + * *pubu* + * * pubugn + * *pubugn* + * * purd + * *purd* + * * rdpu + * *rdpu* + * * ylgn + * *ylgn* + * * ylgnbu + * *ylgnbu* + * * ylorbr + * *ylorbr* + * * ylorrd + * *ylorrd* + * * cividis *cividis* + * * inferno *inferno* + * * magma + * *magma* + * * plasma + * *plasma* + * * viridis *viridis* + * * cubehelix *cubehelix* + * * turbo + * *turbo* + * * warm + * *warm* + * * cool + * *cool* + * + * The default color scheme, *turbo*, was chosen primarily to ensure + * high-contrast visibility. Color schemes such as *blues* make low-value marks + * difficult to see against a white background, for better or for worse. To use + * a subset of a continuous color scheme (or any single-argument *interpolate* + * function), set the *scale*.range property to the corresponding subset of [0, + * 1]; for example, to use the first half of the *rainbow* color scheme, use a + * range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to + * encode a quantitative value without hue, consider using *opacity* rather than + * *color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). * * The following diverging scale schemes are supported: * - * * brbg *brbg* - * * prgn *prgn* - * * piyg *piyg* - * * puor *puor* - * * rdbu *rdbu* - * * rdgy *rdgy* - * * rdylbu *rdylbu* - * * rdylgn *rdylgn* - * * spectral *spectral* - * * burd *burd* - * * buylrd *buylrd* - * - * Picking a diverging color scheme name defaults the scale type to *diverging*; set the scale type to *linear* to treat the color scheme as sequential instead. Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*). + * * brbg + * *brbg* + * * prgn + * *prgn* + * * piyg + * *piyg* + * * puor + * *puor* + * * rdbu + * *rdbu* + * * rdgy + * *rdgy* + * * rdylbu + * *rdylbu* + * * rdylgn + * *rdylgn* + * * spectral *spectral* + * * burd + * *burd* + * * buylrd + * *buylrd* + * + * Picking a diverging color scheme name defaults the scale type to *diverging*; + * set the scale type to *linear* to treat the color scheme as sequential + * instead. Diverging color scales support a *scale*.**pivot** option, which + * defaults to zero. Values below the pivot will use the lower half of the color + * scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot + * will use the upper half (grays for *rdgy*). * * The following cylical color schemes are supported: * - * * rainbow *rainbow* - * * sinebow *sinebow* + * * rainbow *rainbow* + * * sinebow *sinebow* * * The following categorical color schemes are supported: * - * * accent *accent* (8 colors) - * * category10 *category10* (10 colors) - * * dark2 *dark2* (8 colors) - * * paired *paired* (12 colors) - * * pastel1 *pastel1* (9 colors) - * * pastel2 *pastel2* (8 colors) - * * set1 *set1* (9 colors) - * * set2 *set2* (8 colors) - * * set3 *set3* (12 colors) - * * tableau10 *tableau10* (10 colors) + * * accent + * *accent* (8 colors) + * * category10 *category10* (10 colors) + * * dark2 + * *dark2* (8 colors) + * * paired *paired* (12 colors) + * * pastel1 *pastel1* (9 colors) + * * pastel2 *pastel2* (8 colors) + * * set1 + * *set1* (9 colors) + * * set2 + * *set2* (8 colors) + * * set3 + * *set3* (12 colors) + * * tableau10 *tableau10* (10 colors) * * The following color interpolators are supported: * @@ -657,7 +810,8 @@ export function coerceDate(x) { * }) * ``` * - * Or to use gamma-corrected RGB (via [d3-interpolate](https://github.com/d3/d3-interpolate)): + * Or to use gamma-corrected RGB (via + * [d3-interpolate](https://github.com/d3/d3-interpolate)): * * ```js * Plot.plot({ @@ -671,21 +825,52 @@ export function coerceDate(x) { * * ### Sort options * - * If an ordinal scale’s domain is not set, it defaults to natural ascending order; to order the domain by associated values in another dimension, either compute the domain manually (consider [d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) or use an associated mark’s **sort** option. For example, to sort bars by ascending frequency rather than alphabetically by letter: + * If an ordinal scale’s domain is not set, it defaults to natural ascending + * order; to order the domain by associated values in another dimension, either + * compute the domain manually (consider + * [d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) + * or use an associated mark’s **sort** option. For example, to sort bars by + * ascending frequency rather than alphabetically by letter: * * ```js * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) * ``` * - * The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. - * - * Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: + * The sort option is an object whose keys are ordinal scale names, such as *x* + * or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. + * By specifying an existing channel rather than a new value, you avoid + * repeating the order definition and can refer to channels derived by + * [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) + * (such as + * [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or + * [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When + * sorting on the *x*, if no such channel is defined, the *x2* channel will be + * used instead if available, and similarly for *y* and *y2*; this is useful for + * marks that implicitly stack such as + * [area](https://github.com/observablehq/plot/blob/main/README.md#area), + * [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and + * [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort + * value may also be specified as *width* or *height*, representing derived + * channels |*x2* - *x1*| and |*y2* - *y1*| respectively. + * + * Note that there may be multiple associated values in the secondary dimension + * for a given value in the primary ordinal dimension. The secondary values are + * therefore grouped for each associated primary value, and each group is then + * aggregated by applying a reducer. Lastly the primary values are sorted based + * on the associated reduced value in natural ascending order to produce the + * domain. The default reducer is *max*, but may be changed by specifying the + * *reduce* option. The above code is shorthand for: * * ```js * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) * ``` * - * Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. + * Generally speaking, a reducer only needs to be specified when there are + * multiple secondary values for a given primary value. TODO An example of + * assigning categorical colors in a scatterplot by descending count to maximize + * discriminability. See the [group + * transform](https://github.com/observablehq/plot/blob/main/README.md#group) + * for the list of supported reducers. * * For descending rather than ascending order, use the *reverse* option: * @@ -693,36 +878,62 @@ export function coerceDate(x) { * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) * ``` * - * An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) + * An additional *limit* option truncates the domain to the first *n* values + * after sorting. If *limit* is negative, the last *n* values are used instead. + * Hence, a positive *limit* with *reverse* = true will return the top *n* + * values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th + * values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic + * filter + * transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), + * limiting the *x* domain here does not affect the computation of the *y* + * domain, which is computed independently without respect to filtering.) * * ```js * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) * ``` * - * If different sort options are needed for different ordinal scales, the channel name can be replaced with a *value* object with additional per-scale options. + * If different sort options are needed for different ordinal scales, the + * channel name can be replaced with a *value* object with additional per-scale + * options. * * ```js * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse: true}}}) * ``` * - * If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. + * If the input channel is *data*, then the reducer is passed groups of the + * mark’s data; this is typically used in conjunction with a custom reducer + * function, as when the built-in single-channel reducers are insufficient. * - * Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). + * Note: when the value of the sort option is a string or a function, it is + * interpreted as a [basic sort + * transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). + * To use both sort options and a sort transform, use + * [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). * * ### Facet options * - * The *facet* option enables [faceting](https://observablehq.com/@observablehq/plot-facets). When faceting, two additional band scales may be configured: + * The *facet* option enables + * [faceting](https://observablehq.com/@observablehq/plot-facets). When + * faceting, two additional band scales may be configured: * * * **fx** - the horizontal position, a *band* scale * * **fy** - the vertical position, a *band* scale * - * Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: + * Similar to + * [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), + * faceting requires specifying data and at least one of two optional channels: * * * facet.**data** - the data to be faceted - * * facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* - * * facet.**y** - the vertical position; bound to the *fy* scale, which must be *band* + * * facet.**x** - the horizontal position; bound to the *fx* scale, which must + * be *band* + * * facet.**y** - the vertical position; bound to the *fy* scale, which must be + * *band* * - * The facet.**x** and facet.**y** channels are strictly ordinal or categorical (*i.e.*, discrete); each distinct channel value defines a facet. Quantitative data must be manually discretized for faceting, say by rounding or binning. (Automatic binning for quantitative data may be added in the future; see [#14](https://github.com/observablehq/plot/issues/14).) + * The facet.**x** and facet.**y** channels are strictly ordinal or categorical + * (*i.e.*, discrete); each distinct channel value defines a facet. Quantitative + * data must be manually discretized for faceting, say by rounding or binning. + * (Automatic binning for quantitative data may be added in the future; see + * [#14](https://github.com/observablehq/plot/issues/14).) * * The following *facet* constant options are also supported: * @@ -734,12 +945,16 @@ export function coerceDate(x) { * * facet.**grid** - if true, draw grid lines for each facet * * facet.**label** - if null, disable default facet axis labels * - * Faceting can be explicitly enabled or disabled on a mark with the *facet* option, which accepts the following values: + * Faceting can be explicitly enabled or disabled on a mark with the *facet* + * option, which accepts the following values: * - * * *auto* (default) - equivalent to *include* when mark data is strictly equal to facet data; else null - * * *include* (or true) - draw the subset of the mark’s data in the current facet + * * *auto* (default) - equivalent to *include* when mark data is strictly equal + * to facet data; else null + * * *include* (or true) - draw the subset of the mark’s data in the current + * facet * * *exclude* - draw the subset of the mark’s data *not* in the current facet - * * null (or false) - repeat this mark’s data across all facets (i.e., no faceting) + * * null (or false) - repeat this mark’s data across all facets (i.e., no + * faceting) * * ```js * Plot.plot({ @@ -755,17 +970,34 @@ export function coerceDate(x) { * }) * ``` * - * When the *include* or *exclude* facet mode is chosen, the mark data must be parallel to the facet data: the mark data must have the same length and order as the facet data. If the data are not parallel, then the wrong data may be shown in each facet. The default *auto* therefore requires strict equality (`===`) for safety, and using the facet data as mark data is recommended when using the *exclude* facet mode. (To construct parallel data safely, consider using [*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on the facet data.) + * When the *include* or *exclude* facet mode is chosen, the mark data must be + * parallel to the facet data: the mark data must have the same length and order + * as the facet data. If the data are not parallel, then the wrong data may be + * shown in each facet. The default *auto* therefore requires strict equality + * (`===`) for safety, and using the facet data as mark data is recommended when + * using the *exclude* facet mode. (To construct parallel data safely, consider + * using + * [*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) + * on the facet data.) * * ## Legends * - * Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: + * Plot can generate legends for *color*, *opacity*, and *symbol* + * [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). + * (An opacity scale is treated as a color scale with varying transparency.) For + * an inline legend, use the *scale*.**legend** option: * * * *scale*.**legend** - if truthy, generate a legend for the given scale * - * If the *scale*.**legend** option is true, the default legend will be produced for the scale; otherwise, the meaning of the *legend* option depends on the scale. For quantitative color scales, it defaults to *ramp* but may be set to *swatches* for a discrete scale (most commonly for *threshold* color scales); for ordinal color scales and symbol scales, only the *swatches* value is supported. + * If the *scale*.**legend** option is true, the default legend will be produced + * for the scale; otherwise, the meaning of the *legend* option depends on the + * scale. For quantitative color scales, it defaults to *ramp* but may be set to + * *swatches* for a discrete scale (most commonly for *threshold* color scales); + * for ordinal color scales and symbol scales, only the *swatches* value is + * supported. * - * For example, this scatterplot includes a swatches legend for the ordinal color scale: + * For example, this scatterplot includes a swatches legend for the ordinal + * color scale: * * ```js * Plot.plot({ @@ -778,7 +1010,8 @@ export function coerceDate(x) { * }) * ``` * - * Whereas this scatterplot would render a ramp legend for its diverging color scale: + * Whereas this scatterplot would render a ramp legend for its diverging color + * scale: * * ```js * Plot.plot({ @@ -794,7 +1027,11 @@ export function coerceDate(x) { * * #### *plot*.legend(*scaleName*, *options*) * - * Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: + * Given an existing *plot* returned by + * [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), + * returns a detached legend for the *plot*’s scale with the given *scaleName*. + * The *scaleName* must refer to a scale that supports legends: either + * `"color"`, `"opacity"`, or `"symbol"`. For example: * * ```js * myplot = Plot.plot(…) @@ -809,9 +1046,12 @@ export function coerceDate(x) { * mylegend = myplot.legend("color", {width: 320}) * ``` * - * If there is no scale with the given *scaleName* on the given *plot*, then *plot*.legend will return undefined. + * If there is no scale with the given *scaleName* on the given *plot*, then + * *plot*.legend will return undefined. * - * Categorical and ordinal color legends are rendered as swatches, unless *options*.**legend** is set to *ramp*. The swatches can be configured with the following options: + * Categorical and ordinal color legends are rendered as swatches, unless + * *options*.**legend** is set to *ramp*. The swatches can be configured with + * the following options: * * * *options*.**tickFormat** - a format function for the labels * * *options*.**swatchSize** - the size of the swatch (if square) @@ -819,10 +1059,12 @@ export function coerceDate(x) { * * *options*.**swatchHeight** - the swatches’ height * * *options*.**columns** - the number of swatches per row * * *options*.**marginLeft** - the legend’s left margin - * * *options*.**className** - a class name, that defaults to a randomly generated string scoping the styles + * * *options*.**className** - a class name, that defaults to a randomly + * generated string scoping the styles * * *options*.**width** - the legend’s width (in pixels) * - * Symbol legends are rendered as swatches and support the options above in addition to the following options: + * Symbol legends are rendered as swatches and support the options above in + * addition to the following options: * * * *options*.**fill** - the symbol fill color * * *options*.**fillOpacity** - the symbol fill opacity; defaults to 1 @@ -831,12 +1073,18 @@ export function coerceDate(x) { * * *options*.**strokeWidth** - the symbol stroke width; defaults to 1.5 * * *options*.**r** - the symbol radius; defaults to 4.5 pixels * - * The **fill** and **stroke** symbol legend options can be specified as “color” to apply the color scale when the symbol scale is a redundant encoding. The **fill** defaults to none. The **stroke** defaults to currentColor if the fill is none, and to none otherwise. The **fill** and **stroke** options may also be inherited from the corresponding options on an associated dot mark. + * The **fill** and **stroke** symbol legend options can be specified as “color” + * to apply the color scale when the symbol scale is a redundant encoding. The + * **fill** defaults to none. The **stroke** defaults to currentColor if the + * fill is none, and to none otherwise. The **fill** and **stroke** options may + * also be inherited from the corresponding options on an associated dot mark. * - * Continuous color legends are rendered as a ramp, and can be configured with the following options: + * Continuous color legends are rendered as a ramp, and can be configured with + * the following options: * * * *options*.**label** - the scale’s label - * * *options*.**ticks** - the desired number of ticks, or an array of tick values + * * *options*.**ticks** - the desired number of ticks, or an array of tick + * values * * *options*.**tickFormat** - a format function for the legend’s ticks * * *options*.**tickSize** - the tick size * * *options*.**round** - if true (default), round tick positions to pixels @@ -847,7 +1095,9 @@ export function coerceDate(x) { * * *options*.**marginBottom** - the legend’s bottom margin * * *options*.**marginLeft** - the legend’s left margin * - * The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). + * The **style** legend option allows custom styles to override Plot’s defaults; + * it has the same behavior as in Plot’s top-level [layout + * options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). */ export function scale(options = {}) { let scale; diff --git a/src/transforms/basic.js b/src/transforms/basic.js index 3562f12d20..e0a9a29cc8 100644 --- a/src/transforms/basic.js +++ b/src/transforms/basic.js @@ -3,7 +3,14 @@ import {ascendingDefined, descendingDefined} from "../defined.js"; import {arrayify, isDomainSort, isOptions, maybeValue, valueof} from "../options.js"; /** - * Given an *options* object that may specify some basic transforms (*filter*, *sort*, or *reverse*) or a custom *transform* function, composes those transforms if any with the given *transform* function, returning a new *options* object. If a custom *transform* function is present on the given *options*, any basic transforms are ignored. Any additional input *options* are passed through in the returned *options* object. This method facilitates applying the basic transforms prior to applying the given custom *transform* and is used internally by Plot’s built-in transforms. + * Given an *options* object that may specify some basic transforms (*filter*, + * *sort*, or *reverse*) or a custom *transform* function, composes those + * transforms if any with the given *transform* function, returning a new + * *options* object. If a custom *transform* function is present on the given + * *options*, any basic transforms are ignored. Any additional input *options* + * are passed through in the returned *options* object. This method facilitates + * applying the basic transforms prior to applying the given custom *transform* + * and is used internally by Plot’s built-in transforms. */ export function basic(options = {}, transform) { let {filter: f1, sort: s1, reverse: r1, transform: t1, initializer: i1, ...remainingOptions} = options; @@ -24,7 +31,8 @@ export function basic(options = {}, transform) { } /** - * This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. + * This helper composes the *initializer* function with any other transforms + * present in the *options*, and returns a new *options* object. */ export function initializer(options = {}, initializer) { let {filter: f1, sort: s1, reverse: r1, initializer: i1, ...remainingOptions} = options; @@ -71,7 +79,9 @@ function apply(options, t) { * Plot.filter(d => d.body_mass_g > 3000, options) // show data whose body mass is greater than 3kg * ``` * - * Filters the data given the specified *test*. The test can be given as an accessor function (which receives the datum and index), or as a channel value definition such as a field name; truthy values are retained. + * Filters the data given the specified *test*. The test can be given as an + * accessor function (which receives the datum and index), or as a channel value + * definition such as a field name; truthy values are retained. */ export function filter(test, options) { return apply(options, filterTransform(test)); @@ -104,7 +114,9 @@ function reverseTransform(data, facets) { * Plot.shuffle(options) // show data in random order * ``` * - * Shuffles the data randomly. If a *seed* option is specified, a linear congruential generator with the given seed is used to generate random numbers deterministically; otherwise, Math.random is used. + * Shuffles the data randomly. If a *seed* option is specified, a linear + * congruential generator with the given seed is used to generate random numbers + * deterministically; otherwise, Math.random is used. */ export function shuffle(options = {}) { const {seed, ...remainingOptions} = options; @@ -116,7 +128,11 @@ export function shuffle(options = {}) { * Plot.sort("body_mass_g", options) // show data in ascending body mass order * ``` * - * Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. + * Sorts the data by the specified *order*, which can be an accessor function, a + * comparator function, or a channel value definition such as a field name. See + * also [index + * sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), + * which allows marks to be sorted by a named channel, such as *r* for radius. */ export function sort(order, options) { return { diff --git a/src/transforms/bin.js b/src/transforms/bin.js index 8a3fdaa504..91ab1666d5 100644 --- a/src/transforms/bin.js +++ b/src/transforms/bin.js @@ -35,7 +35,8 @@ import {maybeInterval} from "./interval.js"; * Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) * ``` * - * Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. + * Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or + * *stroke*, if any. * * @link https://github.com/observablehq/plot/blob/main/README.md#bin */ @@ -51,7 +52,8 @@ export function binX(outputs = {y: "count"}, options = {}) { * Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) * ``` * - * Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. + * Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or + * *stroke*, if any. */ export function binY(outputs = {x: "count"}, options = {}) { // Group on {z, fill, stroke}, then optionally on x, then bin y. @@ -65,7 +67,8 @@ export function binY(outputs = {x: "count"}, options = {}) { * Plot.rect(athletes, Plot.bin({fillOpacity: "count"}, {x: "weight", y: "height"})) * ``` * - * Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke*, if any. + * Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or + * *stroke*, if any. * * @link https://github.com/observablehq/plot/blob/main/README.md#bin */ diff --git a/src/transforms/dodge.js b/src/transforms/dodge.js index 87f189a16a..513a7b1efa 100644 --- a/src/transforms/dodge.js +++ b/src/transforms/dodge.js @@ -20,7 +20,9 @@ function maybeAnchor(anchor) { * Plot.dodgeX({y: "value"}) * ``` * - * Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. + * Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* + * position channel that avoids overlapping. The *y* position channel is + * unchanged. * * @link https://github.com/observablehq/plot/blob/main/README.md#dodge */ @@ -48,7 +50,9 @@ export function dodgeX(dodgeOptions = {}, options = {}) { * Plot.dodgeY({x: "date"}) * ``` * - * Given marks arranged along the *x* axis, the dodgeY transform piles them vertically by defining a *y* position channel that avoids overlapping. The *x* position channel is unchanged. + * Given marks arranged along the *x* axis, the dodgeY transform piles them + * vertically by defining a *y* position channel that avoids overlapping. The + * *x* position channel is unchanged. * * @link https://github.com/observablehq/plot/blob/main/README.md#dodge */ diff --git a/src/transforms/group.js b/src/transforms/group.js index 13efb7ecc2..5aa82be38a 100644 --- a/src/transforms/group.js +++ b/src/transforms/group.js @@ -37,7 +37,9 @@ import {basic} from "./basic.js"; * Plot.groupZ({x: "proportion"}, {fill: "species"}) * ``` * - * Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of *z*, *fill*, or *stroke* are channels, then all data (within each facet) is placed into a single group. + * Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of + * *z*, *fill*, or *stroke* are channels, then all data (within each facet) is + * placed into a single group. * * @link https://github.com/observablehq/plot/blob/main/README.md#group */ @@ -83,7 +85,8 @@ export function groupY(outputs = {x: "count"}, options = {}) { * Plot.group({fill: "count"}, {x: "island", y: "species"}) * ``` * - * Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if any. + * Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if + * any. * * @link https://github.com/observablehq/plot/blob/main/README.md#group */ diff --git a/src/transforms/hexbin.js b/src/transforms/hexbin.js index 3fc073a3fc..ebd0e50dc8 100644 --- a/src/transforms/hexbin.js +++ b/src/transforms/hexbin.js @@ -13,7 +13,16 @@ export const ox = 0.5, oy = 0; /** - * Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](https://github.com/observablehq/plot/blob/main/README.md#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. + * Aggregates the given input channels into hexagonal bins, creating output + * channels with the reduced data. The *options* must specify the **x** and + * **y** channels. The **binWidth** option (default 20) defines the distance + * between centers of neighboring hexagons in pixels. If any of **z**, **fill**, + * or **stroke** is a channel, the first of these channels will be used to + * subdivide bins. The *outputs* options are similar to the [bin + * transform](https://github.com/observablehq/plot/blob/main/README.md#bin); + * each output channel receives as input, for each hexagon, the subset of the + * data which has been matched to its center. The outputs object specifies the + * aggregation method for each output channel. * * The following aggregation methods are supported: * @@ -22,7 +31,8 @@ export const ox = 0.5, * * *count* - the number of elements (frequency) * * *distinct* - the number of distinct values * * *sum* - the sum of values - * * *proportion* - the sum proportional to the overall total (weighted frequency) + * * *proportion* - the sum proportional to the overall total (weighted + * frequency) * * *proportion-facet* - the sum proportional to the facet total * * *min* - the minimum value * * *min-index* - the zero-based index of the minimum value @@ -31,12 +41,16 @@ export const ox = 0.5, * * *mean* - the mean value (average) * * *median* - the median value * * *deviation* - the standard deviation - * * *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) + * * *variance* - the variance per [Welford’s + * algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) * * *mode* - the value with the most occurrences - * * a function to be passed the array of values for each bin and the extent of the bin + * * a function to be passed the array of values for each bin and the extent of + * the bin * * an object with a *reduce* method * - * See also the [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) mark. + * See also the + * [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) + * mark. */ export function hexbin(outputs = {fill: "count"}, options = {}) { // TODO filter e.g. to show empty hexbins? diff --git a/src/transforms/map.js b/src/transforms/map.js index a6a787b125..eea4e9ed5c 100644 --- a/src/transforms/map.js +++ b/src/transforms/map.js @@ -7,7 +7,8 @@ import {basic} from "./basic.js"; * Plot.mapX("cumsum", {x: d3.randomNormal()}) * ``` * - * Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores any of **x**, **x1**, and **x2** not present in *options*. + * Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but + * ignores any of **x**, **x1**, and **x2** not present in *options*. * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ @@ -23,7 +24,8 @@ export function mapX(mapping, options = {}) { * Plot.mapY("cumsum", {y: d3.randomNormal()}) * ``` * - * Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. + * Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but + * ignores any of **y**, **y1**, and **y2** not present in *options*. * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ @@ -39,7 +41,10 @@ export function mapY(mapping, options = {}) { * Plot.map({y: "cumsum"}, {y: d3.randomNormal()}) * ``` * - * Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for each channel declared in the specified *outputs* object, applies the corresponding map method. Each channel in *outputs* must have a corresponding input channel in *options*. + * Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for + * each channel declared in the specified *outputs* object, applies the + * corresponding map method. Each channel in *outputs* must have a corresponding + * input channel in *options*. * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js index 0d49eafba5..a997095ab9 100644 --- a/src/transforms/normalize.js +++ b/src/transforms/normalize.js @@ -8,7 +8,9 @@ import {mapX, mapY} from "./map.js"; * Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) * ``` * - * Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), but applies the normalize map method with the given *basis*. + * Like + * [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), + * but applies the normalize map method with the given *basis*. */ export function normalizeX(basis, options) { if (arguments.length === 1) ({basis, ...options} = basis); @@ -20,7 +22,9 @@ export function normalizeX(basis, options) { * Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) * ``` * - * Like [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), but applies the normalize map method with the given *basis*. + * Like + * [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), + * but applies the normalize map method with the given *basis*. */ export function normalizeY(basis, options) { if (arguments.length === 1) ({basis, ...options} = basis); @@ -32,7 +36,8 @@ export function normalizeY(basis, options) { * Plot.map({y: Plot.normalize("first")}, {x: "Date", y: "Close", stroke: "Symbol"}) * ``` * - * Returns a normalize map method for the given *basis*, suitable for use with Plot.map. + * Returns a normalize map method for the given *basis*, suitable for use with + * Plot.map. */ export function normalize(basis) { if (basis === undefined) return normalizeFirst; diff --git a/src/transforms/select.js b/src/transforms/select.js index b99cea98c5..5f972851d3 100644 --- a/src/transforms/select.js +++ b/src/transforms/select.js @@ -3,9 +3,17 @@ import {maybeZ, valueof} from "../options.js"; import {basic} from "./basic.js"; /** - * Selects the points of each series selected by the *selector*, which can be specified either as a function which receives as input the index of the series, the shorthand “first” or “last”, or as a {*key*: *value*} object with exactly one *key* being the name of a channel and the *value* being a function which receives as input the index of the series and the channel values. The *value* may alternatively be specified as the shorthand “min” and “max” which respectively select the minimum and maximum points for the specified channel. + * Selects the points of each series selected by the *selector*, which can be + * specified either as a function which receives as input the index of the + * series, the shorthand “first” or “last”, or as a {*key*: *value*} object with + * exactly one *key* being the name of a channel and the *value* being a + * function which receives as input the index of the series and the channel + * values. The *value* may alternatively be specified as the shorthand “min” and + * “max” which respectively select the minimum and maximum points for the + * specified channel. * - * For example, to select the point within each series that is the closest to the median of the *y* channel: + * For example, to select the point within each series that is the closest to + * the median of the *y* channel: * * ```js * Plot.select({ diff --git a/src/transforms/stack.js b/src/transforms/stack.js index 3bc41420e8..58791b89b8 100644 --- a/src/transforms/stack.js +++ b/src/transforms/stack.js @@ -8,7 +8,8 @@ import {basic} from "./basic.js"; * Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) * ``` * - * See Plot.stackY, but with *x* as the input value channel, *y* as the stack index, *x1*, *x2* and *x* as the output channels. + * See Plot.stackY, but with *x* as the input value channel, *y* as the stack + * index, *x1*, *x2* and *x* as the output channels. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -24,7 +25,10 @@ export function stackX(stackOptions = {}, options = {}) { * Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. + * Equivalent to + * [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), + * except that the **x1** channel is returned as the **x** channel. This can be + * used, for example, to draw a line at the left edge of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -40,7 +44,10 @@ export function stackX1(stackOptions = {}, options = {}) { * Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. + * Equivalent to + * [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), + * except that the **x2** channel is returned as the **x** channel. This can be + * used, for example, to draw a line at the right edge of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -56,7 +63,13 @@ export function stackX2(stackOptions = {}, options = {}) { * Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) * ``` * - * Creates new channels **y1** and **y2**, obtained by stacking the original **y** channel for data points that share a common **x** (and possibly **z**) value. A new **y** channel is also returned, which lazily computes the middle value of **y1** and **y2**. The input **y** channel defaults to a constant 1, resulting in a count of the data points. The stack options (*offset*, *order*, and *reverse*) may be specified as part of the *options* object, if the only argument, or as a separate *stack* options argument. + * Creates new channels **y1** and **y2**, obtained by stacking the original + * **y** channel for data points that share a common **x** (and possibly **z**) + * value. A new **y** channel is also returned, which lazily computes the middle + * value of **y1** and **y2**. The input **y** channel defaults to a constant 1, + * resulting in a count of the data points. The stack options (*offset*, + * *order*, and *reverse*) may be specified as part of the *options* object, if + * the only argument, or as a separate *stack* options argument. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -72,7 +85,10 @@ export function stackY(stackOptions = {}, options = {}) { * Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. + * Equivalent to + * [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), + * except that the **y1** channel is returned as the **y** channel. This can be + * used, for example, to draw a line at the bottom of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ @@ -88,7 +104,10 @@ export function stackY1(stackOptions = {}, options = {}) { * Plot.stackY2({x: "year", y: "revenue", z: "format", fill: "group"}) * ``` * - * Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y2** channel is returned as the **y** channel. This can be used, for example, to draw a line at the top of each stacked area. + * Equivalent to + * [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), + * except that the **y2** channel is returned as the **y** channel. This can be + * used, for example, to draw a line at the top of each stacked area. * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ diff --git a/src/transforms/tree.js b/src/transforms/tree.js index 02d17338f4..830d6a5c6c 100644 --- a/src/transforms/tree.js +++ b/src/transforms/tree.js @@ -4,9 +4,18 @@ import {column, identity, isObject, one, valueof} from "../options.js"; import {basic} from "./basic.js"; /** - * Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), [text](https://github.com/observablehq/plot/blob/main/README.md#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). + * Based on the tree options described above, populates the **x** and **y** + * channels with the positions for each node. The following defaults are also + * applied: the default **frameAnchor** inherits the **treeAnchor**. This + * transform is intended to be used with + * [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), + * [text](https://github.com/observablehq/plot/blob/main/README.md#text), and + * other point-based marks. This transform is rarely used directly; see the + * [Plot.tree compound + * mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). * - * The treeNode transform will derive output columns for any *options* that have one of the following named node values: + * The treeNode transform will derive output columns for any *options* that have + * one of the following named node values: * * * *node:name* - the node’s name (the last part of its path) * * *node:path* - the node’s full, normalized, slash-separated path @@ -14,7 +23,9 @@ import {basic} from "./basic.js"; * * *node:depth* - the distance from the node to the root * * *node:height* - the distance from the node to its deepest descendant * - * In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each node in the tree. + * In addition, if any option value is specified as an object with a **node** + * method, a derived output column will be generated by invoking the **node** + * method for each node in the tree. */ export function treeNode(options = {}) { let { @@ -70,9 +81,19 @@ export function treeNode(options = {}) { } /** - * Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](https://github.com/observablehq/plot/blob/main/README.md#link), [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). + * Based on the tree options described above, populates the **x1**, **y1**, + * **x2**, and **y2** channels. The following defaults are also applied: the + * default **curve** is *bump-x*, the default **stroke** is #555, the default + * **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This + * transform is intended to be used with + * [link](https://github.com/observablehq/plot/blob/main/README.md#link), + * [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and + * other two-point-based marks. This transform is rarely used directly; see the + * [Plot.tree compound + * mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). * - * The treeLink transform will derive output columns for any *options* that have one of the following named link values: + * The treeLink transform will derive output columns for any *options* that have + * one of the following named link values: * * * *node:name* - the child node’s name (the last part of its path) * * *node:path* - the child node’s full, normalized, slash-separated path @@ -82,9 +103,15 @@ export function treeNode(options = {}) { * * *parent:name* - the parent node’s name (the last part of its path) * * *parent:path* - the parent node’s full, normalized, slash-separated path * * *parent:depth* - the distance from the parent node to the root - * * *parent:height* - the distance from the parent node to its deepest descendant + * * *parent:height* - the distance from the parent node to its deepest + * descendant * - * In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each child node in the tree; likewise if any option value is specified as an object with a **link** method, a derived output column will be generated by invoking the **link** method for each link in the tree, being passed two node arguments, the child and the parent. + * In addition, if any option value is specified as an object with a **node** + * method, a derived output column will be generated by invoking the **node** + * method for each child node in the tree; likewise if any option value is + * specified as an object with a **link** method, a derived output column will + * be generated by invoking the **link** method for each link in the tree, being + * passed two node arguments, the child and the parent. */ export function treeLink(options = {}) { let { From 924729e32fa6300cbda6705a660999ac51f34682 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Thu, 25 Aug 2022 00:07:58 -0400 Subject: [PATCH 51/59] run readme:check as part of yarn test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 02aa083694..c20663cf8c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "src/**/*.js" ], "scripts": { - "test": "yarn test:typecheck && yarn test:lint && yarn test:mocha", + "test": "yarn test:typecheck && yarn test:lint && yarn readme:check && yarn test:mocha", "test:mocha": "mkdir -p test/output && mocha --conditions=mocha 'test/**/*-test.*' 'test/plot.js'", "test:lint": "eslint src test", "test:typecheck": "tsc --noEmit", From ac5bf045e9676446f008179a46f485cab455f6b1 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Thu, 25 Aug 2022 14:44:12 -0400 Subject: [PATCH 52/59] jsdoc scale - move content from JSDoc to the README --- README.md | 2 + src/scales.js | 358 -------------------------------------------------- 2 files changed, 2 insertions(+), 358 deletions(-) diff --git a/README.md b/README.md index 8c18ea7ce9..0fc6ec211d 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,8 @@ const plot2 = Plot.plot({…, color: plot1.scale("color")}); For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. + + ### Position options The position scales (*x*, *y*, *fx*, and *fy*) support additional options: diff --git a/src/scales.js b/src/scales.js index acda64ab30..15bd7ddd9b 100644 --- a/src/scales.js +++ b/src/scales.js @@ -490,364 +490,6 @@ export function coerceDate(x) { * ``` * * For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. - * - * ### Position options - * - * The position scales (*x*, *y*, *fx*, and *fy*) support additional options: - * - * * *scale*.**inset** - inset the default range by the specified amount in pixels - * * *scale*.**round** - round the output value to the nearest integer (whole pixel) - * - * The *x* and *fx* scales support asymmetric insets for more precision. Replace inset by: - * - * * *scale*.**insetLeft** - insets the start of the default range by the specified number of pixels - * * *scale*.**insetRight** - insets the end of the default range by the specified number of pixels - * - * Similarly, the *y* and *fy* scales support asymmetric insets with: - * - * * *scale*.**insetTop** - insets the top of the default range by the specified number of pixels - * * *scale*.**insetBottom** - insets the bottom of the default range by the specified number of pixels - * - * The inset scale options can provide “breathing room” to separate marks from axes or the plot’s edge. For example, in a scatterplot with a Plot.dot with the default 3-pixel radius and 1.5-pixel stroke width, an inset of 5 pixels prevents dots from overlapping with the axes. The *scale*.round option is useful for crisp edges by rounding to the nearest pixel boundary. - * - * In addition to the generic *ordinal* scale type, which requires an explicit output range value for each input domain value, Plot supports special *point* and *band* scale types for encoding ordinal data as position. These scale types accept a [*min*, *max*] range similar to quantitative scales, and divide this continuous interval into discrete points or bands based on the number of distinct values in the domain (*i.e.*, the domain’s cardinality). If the associated marks have no effective width along the ordinal dimension—such as a dot, rule, or tick—then use a *point* scale; otherwise, say for a bar, use a *band* scale. In the image below, the top *x* scale is a *point* scale while the bottom *x* scale is a *band* scale; see [Plot: Scales](https://observablehq.com/@observablehq/plot-scales) for an interactive version. - * - * point and band scales - * - * Ordinal position scales support additional options, all specified as proportions in [0, 1]: - * - * * *scale*.**padding** - how much of the range to reserve to inset first and last point or band - * * *scale*.**align** - where to distribute points or bands (0 = at start, 0.5 = at middle, 1 = at end) - * - * For a *band* scale, you can further fine-tune padding: - * - * * *scale*.**paddingInner** - how much of the range to reserve to separate adjacent bands - * * *scale*.**paddingOuter** - how much of the range to reserve to inset first and last band - * - * Align defaults to 0.5 (centered). Band scale padding defaults to 0.1 (10% of available space reserved for separating bands), while point scale padding defaults to 0.5 (the gap between the first point and the edge is half the distance of the gap between points, and likewise for the gap between the last point and the opposite edge). Note that rounding and mark insets (e.g., for bars and rects) also affect separation between adjacent marks. - * - * Plot automatically generates axes for position scales. You can configure these axes with the following options: - * - * * *scale*.**axis** - the orientation: *top* or *bottom* for *x*; *left* or *right* for *y*; null to suppress - * * *scale*.**ticks** - the approximate number of ticks to generate - * * *scale*.**tickSize** - the size of each tick (in pixels; default 6) - * * *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) - * * *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) - * * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) - * * *scale*.**grid** - if true, draw grid lines across the plot for each tick - * * *scale*.**line** - if true, draw the axis line - * * *scale*.**label** - a string to label the axis - * * *scale*.**labelAnchor** - the label anchor: *top*, *right*, *bottom*, *left*, or *center* - * * *scale*.**labelOffset** - the label position offset (in pixels; default 0, typically for facet axes) - * * *scale*.**fontVariant** - the font-variant attribute for axis ticks; defaults to tabular-nums for quantitative axes - * * *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree - * * *scale*.**ariaDescription** - a textual description for the axis - * - * Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. - * - * ### Color options - * - * The normal scale types—*linear*, *sqrt*, *pow*, *log*, *symlog*, and *ordinal*—can be used to encode color. In addition, Plot supports special scale types for color: - * - * * *categorical* - equivalent to *ordinal*, but defaults to the *tableau10* scheme - * * *sequential* - equivalent to *linear* - * * *cyclical* - equivalent to *linear*, but defaults to the *rainbow* scheme - * * *threshold* - encodes based on the specified discrete thresholds; defaults to the *rdylbu* scheme - * * *quantile* - encodes based on the computed quantile thresholds; defaults to the *rdylbu* scheme - * * *quantize* - transforms a continuous domain into quantized thresholds; defaults to the *rdylbu* scheme - * * *diverging* - like *linear*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-log* - like *log*, but with a pivot that defaults to 1; defaults to the *rdbu* scheme - * * *diverging-pow* - like *pow*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-sqrt* - like *sqrt*, but with a pivot; defaults to the *rdbu* scheme - * * *diverging-symlog* - like *symlog*, but with a pivot; defaults to the *rdbu* scheme - * - * For a *threshold* scale, the *domain* represents *n* (typically numeric) thresholds which will produce a *range* of *n* + 1 output colors; the *i*th color of the *range* applies to values that are smaller than the *i*th element of the domain and larger or equal to the *i* - 1th element of the domain. For a *quantile* scale, the *domain* represents all input values to the scale, and the *n* option specifies how many quantiles to compute from the *domain*; *n* quantiles will produce *n* - 1 thresholds, and an output range of *n* colors. For a *quantize* scale, the domain will be transformed into approximately *n* quantized values, where *n* is an option that defaults to 5. - * - * By default, all diverging color scales are symmetric around the pivot; set *symmetric* to false if you want to cover the whole extent on both sides. - * - * Color scales support two additional options: - * - * * *scale*.**scheme** - a named color scheme in lieu of a range, such as *reds* - * * *scale*.**interpolate** - in conjunction with a range, how to interpolate colors - * - * For quantile and quantize color scales, the *scale*.scheme option is used in conjunction with *scale*.**n**, which determines how many quantiles or quantized values to compute, and thus the number of elements in the scale’s range; it defaults to 5 (for quintiles in the case of a quantile scale). - * - * The following sequential scale schemes are supported for both quantitative and ordinal data: - * - * * blues *blues* - * * greens *greens* - * * greys *greys* - * * oranges *oranges* - * * purples *purples* - * * reds *reds* - * * bugn *bugn* - * * bupu *bupu* - * * gnbu *gnbu* - * * orrd *orrd* - * * pubu *pubu* - * * pubugn *pubugn* - * * purd *purd* - * * rdpu *rdpu* - * * ylgn *ylgn* - * * ylgnbu *ylgnbu* - * * ylorbr *ylorbr* - * * ylorrd *ylorrd* - * * cividis *cividis* - * * inferno *inferno* - * * magma *magma* - * * plasma *plasma* - * * viridis *viridis* - * * cubehelix *cubehelix* - * * turbo *turbo* - * * warm *warm* - * * cool *cool* - * - * The default color scheme, *turbo*, was chosen primarily to ensure high-contrast visibility. Color schemes such as *blues* make low-value marks difficult to see against a white background, for better or for worse. To use a subset of a continuous color scheme (or any single-argument *interpolate* function), set the *scale*.range property to the corresponding subset of [0, 1]; for example, to use the first half of the *rainbow* color scheme, use a range of [0, 0.5]. By default, the full range [0, 1] is used. If you wish to encode a quantitative value without hue, consider using *opacity* rather than *color* (e.g., use Plot.dot’s *strokeOpacity* instead of *stroke*). - * - * The following diverging scale schemes are supported: - * - * * brbg *brbg* - * * prgn *prgn* - * * piyg *piyg* - * * puor *puor* - * * rdbu *rdbu* - * * rdgy *rdgy* - * * rdylbu *rdylbu* - * * rdylgn *rdylgn* - * * spectral *spectral* - * * burd *burd* - * * buylrd *buylrd* - * - * Picking a diverging color scheme name defaults the scale type to *diverging*; set the scale type to *linear* to treat the color scheme as sequential instead. Diverging color scales support a *scale*.**pivot** option, which defaults to zero. Values below the pivot will use the lower half of the color scheme (*e.g.*, reds for the *rdgy* scheme), while values above the pivot will use the upper half (grays for *rdgy*). - * - * The following cylical color schemes are supported: - * - * * rainbow *rainbow* - * * sinebow *sinebow* - * - * The following categorical color schemes are supported: - * - * * accent *accent* (8 colors) - * * category10 *category10* (10 colors) - * * dark2 *dark2* (8 colors) - * * paired *paired* (12 colors) - * * pastel1 *pastel1* (9 colors) - * * pastel2 *pastel2* (8 colors) - * * set1 *set1* (9 colors) - * * set2 *set2* (8 colors) - * * set3 *set3* (12 colors) - * * tableau10 *tableau10* (10 colors) - * - * The following color interpolators are supported: - * - * * *rgb* - RGB (red, green, blue) - * * *hsl* - HSL (hue, saturation, lightness) - * * *lab* - CIELAB (*a.k.a.* “Lab”) - * * *hcl* - CIELChab (*a.k.a.* “LCh” or “HCL”) - * - * For example, to use CIELChab: - * - * ```js - * Plot.plot({ - * color: { - * range: ["red", "blue"], - * interpolate: "hcl" - * }, - * marks: … - * }) - * ``` - * - * Or to use gamma-corrected RGB (via [d3-interpolate](https://github.com/d3/d3-interpolate)): - * - * ```js - * Plot.plot({ - * color: { - * range: ["red", "blue"], - * interpolate: d3.interpolateRgb.gamma(2.2) - * }, - * marks: … - * }) - * ``` - * - * ### Sort options - * - * If an ordinal scale’s domain is not set, it defaults to natural ascending order; to order the domain by associated values in another dimension, either compute the domain manually (consider [d3.groupSort](https://github.com/d3/d3-array/blob/main/README.md#groupSort)) or use an associated mark’s **sort** option. For example, to sort bars by ascending frequency rather than alphabetically by letter: - * - * ```js - * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) - * ``` - * - * The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. - * - * Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: - * - * ```js - * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) - * ``` - * - * Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. - * - * For descending rather than ascending order, use the *reverse* option: - * - * ```js - * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) - * ``` - * - * An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) - * - * ```js - * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) - * ``` - * - * If different sort options are needed for different ordinal scales, the channel name can be replaced with a *value* object with additional per-scale options. - * - * ```js - * Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse: true}}}) - * ``` - * - * If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. - * - * Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). - * - * ### Facet options - * - * The *facet* option enables [faceting](https://observablehq.com/@observablehq/plot-facets). When faceting, two additional band scales may be configured: - * - * * **fx** - the horizontal position, a *band* scale - * * **fy** - the vertical position, a *band* scale - * - * Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: - * - * * facet.**data** - the data to be faceted - * * facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* - * * facet.**y** - the vertical position; bound to the *fy* scale, which must be *band* - * - * The facet.**x** and facet.**y** channels are strictly ordinal or categorical (*i.e.*, discrete); each distinct channel value defines a facet. Quantitative data must be manually discretized for faceting, say by rounding or binning. (Automatic binning for quantitative data may be added in the future; see [#14](https://github.com/observablehq/plot/issues/14).) - * - * The following *facet* constant options are also supported: - * - * * facet.**marginTop** - the top margin - * * facet.**marginRight** - the right margin - * * facet.**marginBottom** - the bottom margin - * * facet.**marginLeft** - the left margin - * * facet.**margin** - shorthand for the four margins - * * facet.**grid** - if true, draw grid lines for each facet - * * facet.**label** - if null, disable default facet axis labels - * - * Faceting can be explicitly enabled or disabled on a mark with the *facet* option, which accepts the following values: - * - * * *auto* (default) - equivalent to *include* when mark data is strictly equal to facet data; else null - * * *include* (or true) - draw the subset of the mark’s data in the current facet - * * *exclude* - draw the subset of the mark’s data *not* in the current facet - * * null (or false) - repeat this mark’s data across all facets (i.e., no faceting) - * - * ```js - * Plot.plot({ - * facet: { - * data: penguins, - * x: "sex" - * }, - * marks: [ - * Plot.frame(), // draws an outline around each facet - * Plot.dot(penguins, {x: "culmen_length_mm", y: "culmen_depth_mm", fill: "#eee", facet: "exclude"}), // draws excluded penguins on each facet - * Plot.dot(penguins, {x: "culmen_length_mm", y: "culmen_depth_mm"}) // draws only the current facet’s subset - * ] - * }) - * ``` - * - * When the *include* or *exclude* facet mode is chosen, the mark data must be parallel to the facet data: the mark data must have the same length and order as the facet data. If the data are not parallel, then the wrong data may be shown in each facet. The default *auto* therefore requires strict equality (`===`) for safety, and using the facet data as mark data is recommended when using the *exclude* facet mode. (To construct parallel data safely, consider using [*array*.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on the facet data.) - * - * ## Legends - * - * Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: - * - * * *scale*.**legend** - if truthy, generate a legend for the given scale - * - * If the *scale*.**legend** option is true, the default legend will be produced for the scale; otherwise, the meaning of the *legend* option depends on the scale. For quantitative color scales, it defaults to *ramp* but may be set to *swatches* for a discrete scale (most commonly for *threshold* color scales); for ordinal color scales and symbol scales, only the *swatches* value is supported. - * - * For example, this scatterplot includes a swatches legend for the ordinal color scale: - * - * ```js - * Plot.plot({ - * color: { - * legend: true - * }, - * marks: [ - * Plot.dot(athletes, {x: "weight", y: "height", stroke: "sex"}) - * ] - * }) - * ``` - * - * Whereas this scatterplot would render a ramp legend for its diverging color scale: - * - * ```js - * Plot.plot({ - * color: { - * type: "diverging", - * legend: true - * }, - * marks: [ - * Plot.dot(gistemp, {x: "Date", y: "Anomaly", stroke: "Anomaly"}) - * ] - * }) - * ``` - * - * #### *plot*.legend(*scaleName*, *options*) - * - * Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: - * - * ```js - * myplot = Plot.plot(…) - * ``` - * ```js - * mylegend = myplot.legend("color") - * ``` - * - * Or, with additional *options*: - * - * ```js - * mylegend = myplot.legend("color", {width: 320}) - * ``` - * - * If there is no scale with the given *scaleName* on the given *plot*, then *plot*.legend will return undefined. - * - * Categorical and ordinal color legends are rendered as swatches, unless *options*.**legend** is set to *ramp*. The swatches can be configured with the following options: - * - * * *options*.**tickFormat** - a format function for the labels - * * *options*.**swatchSize** - the size of the swatch (if square) - * * *options*.**swatchWidth** - the swatches’ width - * * *options*.**swatchHeight** - the swatches’ height - * * *options*.**columns** - the number of swatches per row - * * *options*.**marginLeft** - the legend’s left margin - * * *options*.**className** - a class name, that defaults to a randomly generated string scoping the styles - * * *options*.**width** - the legend’s width (in pixels) - * - * Symbol legends are rendered as swatches and support the options above in addition to the following options: - * - * * *options*.**fill** - the symbol fill color - * * *options*.**fillOpacity** - the symbol fill opacity; defaults to 1 - * * *options*.**stroke** - the symbol stroke color - * * *options*.**strokeOpacity** - the symbol stroke opacity; defaults to 1 - * * *options*.**strokeWidth** - the symbol stroke width; defaults to 1.5 - * * *options*.**r** - the symbol radius; defaults to 4.5 pixels - * - * The **fill** and **stroke** symbol legend options can be specified as “color” to apply the color scale when the symbol scale is a redundant encoding. The **fill** defaults to none. The **stroke** defaults to currentColor if the fill is none, and to none otherwise. The **fill** and **stroke** options may also be inherited from the corresponding options on an associated dot mark. - * - * Continuous color legends are rendered as a ramp, and can be configured with the following options: - * - * * *options*.**label** - the scale’s label - * * *options*.**ticks** - the desired number of ticks, or an array of tick values - * * *options*.**tickFormat** - a format function for the legend’s ticks - * * *options*.**tickSize** - the tick size - * * *options*.**round** - if true (default), round tick positions to pixels - * * *options*.**width** - the legend’s width - * * *options*.**height** - the legend’s height - * * *options*.**marginTop** - the legend’s top margin - * * *options*.**marginRight** - the legend’s right margin - * * *options*.**marginBottom** - the legend’s bottom margin - * * *options*.**marginLeft** - the legend’s left margin - * - * The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). */ export function scale(options = {}) { let scale; From 02c5825c467dda61509bf9d6064d7b24f411f58c Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Thu, 25 Aug 2022 17:14:12 -0400 Subject: [PATCH 53/59] valueof arrayType->type --- README.md | 10 +++++----- src/options.js | 28 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0fc6ec211d..fad880801d 100644 --- a/README.md +++ b/README.md @@ -2502,20 +2502,20 @@ Plot provides a few helpers for implementing transforms. -#### Plot.valueof(*data*, *value*, *arrayType*) +#### Plot.valueof(*data*, *value*, *type*) -Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *arrayType* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: +Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *type* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: * a string - corresponding to the field accessor (`d => d[value]`) -* an accessor function - called as *arrayType*.from(*data*, *value*) +* an accessor function - called as *type*.from(*data*, *value*) * a number, Date, or boolean — resulting in an array uniformly filled with the *value* * an object with a transform method — called as *value*.transform(*data*) * an array of values - returning the same * null or undefined - returning the same -If *arrayType* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). +If *type* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). -Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *arrayType*, the array may be returned as-is without making a copy. +Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *type*, the array may be returned as-is without making a copy. diff --git a/src/options.js b/src/options.js index 454bc87ee6..2ca6ed5551 100644 --- a/src/options.js +++ b/src/options.js @@ -6,30 +6,30 @@ const TypedArray = Object.getPrototypeOf(Uint8Array); const objectToString = Object.prototype.toString; /** - * Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *arrayType* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: + * Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *type* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: * * * a string - corresponding to the field accessor (`d => d[value]`) - * * an accessor function - called as *arrayType*.from(*data*, *value*) + * * an accessor function - called as *type*.from(*data*, *value*) * * a number, Date, or boolean — resulting in an array uniformly filled with the *value* * * an object with a transform method — called as *value*.transform(*data*) * * an array of values - returning the same * * null or undefined - returning the same * - * If *arrayType* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). + * If *type* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). * - * Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *arrayType*, the array may be returned as-is without making a copy. + * Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *type*, the array may be returned as-is without making a copy. */ -export function valueof(data, value, arrayType) { - const type = typeof value; - return type === "string" - ? map(data, field(value), arrayType) - : type === "function" - ? map(data, value, arrayType) - : type === "number" || value instanceof Date || type === "boolean" - ? map(data, constant(value), arrayType) +export function valueof(data, value, type) { + const valueType = typeof value; + return valueType === "string" + ? map(data, field(value), type) + : valueType === "function" + ? map(data, value, type) + : valueType === "number" || value instanceof Date || valueType === "boolean" + ? map(data, constant(value), type) : value && typeof value.transform === "function" - ? arrayify(value.transform(data), arrayType) - : arrayify(value, arrayType); // preserve undefined type + ? arrayify(value.transform(data), type) + : arrayify(value, type); // preserve undefined type } export const field = (name) => (d) => d[name]; From 21476dec3a2774fd83f56dfc6e6bc190434f565f Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:10:17 -0400 Subject: [PATCH 54/59] use map argument name in map functions --- src/transforms/map.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/transforms/map.js b/src/transforms/map.js index a6a787b125..9a62eaee09 100644 --- a/src/transforms/map.js +++ b/src/transforms/map.js @@ -11,9 +11,9 @@ import {basic} from "./basic.js"; * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ -export function mapX(mapping, options = {}) { - return map( - Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, mapping])), +export function mapX(map, options = {}) { + return mapAlias( + Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, map])), options ); } @@ -27,9 +27,9 @@ export function mapX(mapping, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#map */ -export function mapY(mapping, options = {}) { - return map( - Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, mapping])), +export function mapY(map, options = {}) { + return mapAlias( + Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, map])), options ); } @@ -67,6 +67,9 @@ export function map(outputs = {}, options = {}) { }; } +// This is used internally so we can use `map` as an argument name. +const mapAlias = map; + function maybeMap(map) { if (map && typeof map.map === "function") return map; if (typeof map === "function") return mapFunction(map); From 32373b8dd53b7da644713e81c7140d06408ca737 Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:17:15 -0400 Subject: [PATCH 55/59] use original README as source of truth for argument names throughout --- src/format.ts | 12 ++++++------ src/transforms/stack.js | 39 +++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/format.ts b/src/format.ts index 7ca5e69040..4452484f68 100644 --- a/src/format.ts +++ b/src/format.ts @@ -29,11 +29,11 @@ export function formatNumber(locale = "en-US"): (value: any) => string | undefin */ export function formatMonth( locale = "en-US", - month: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined = "short" + format: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined = "short" ) { - const format = monthFormat(locale, month); + const fmt = monthFormat(locale, format); return (i: Date | number | null | undefined) => - i != null && !isNaN((i = +new Date(Date.UTC(2000, +i)))) ? format.format(i) : undefined; + i != null && !isNaN((i = +new Date(Date.UTC(2000, +i)))) ? fmt.format(i) : undefined; } /** @@ -43,10 +43,10 @@ export function formatMonth( * * Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. */ -export function formatWeekday(locale = "en-US", weekday: "long" | "short" | "narrow" | undefined = "short") { - const format = weekdayFormat(locale, weekday); +export function formatWeekday(locale = "en-US", format: "long" | "short" | "narrow" | undefined = "short") { + const fmt = weekdayFormat(locale, format); return (i: Date | number | null | undefined) => - i != null && !isNaN((i = +new Date(Date.UTC(2001, 0, +i)))) ? format.format(i) : undefined; + i != null && !isNaN((i = +new Date(Date.UTC(2001, 0, +i)))) ? fmt.format(i) : undefined; } /** diff --git a/src/transforms/stack.js b/src/transforms/stack.js index 3bc41420e8..908ee6a80e 100644 --- a/src/transforms/stack.js +++ b/src/transforms/stack.js @@ -12,10 +12,10 @@ import {basic} from "./basic.js"; * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackX(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackX(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x, ...rest} = options; // note: consumes x! - const [transform, Y, x1, x2] = stack(y, x, "x", stackOptions, rest); + const [transform, Y, x1, x2] = stackAlias(y, x, "x", stack, rest); return {...transform, y1, y: Y, x1, x2, x: mid(x1, x2)}; } @@ -28,10 +28,10 @@ export function stackX(stackOptions = {}, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackX1(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackX1(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x} = options; - const [transform, Y, X] = stack(y, x, "x", stackOptions, options); + const [transform, Y, X] = stackAlias(y, x, "x", stack, options); return {...transform, y1, y: Y, x: X}; } @@ -44,10 +44,10 @@ export function stackX1(stackOptions = {}, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackX2(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackX2(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x} = options; - const [transform, Y, , X] = stack(y, x, "x", stackOptions, options); + const [transform, Y, , X] = stackAlias(y, x, "x", stack, options); return {...transform, y1, y: Y, x: X}; } @@ -60,10 +60,10 @@ export function stackX2(stackOptions = {}, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackY(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackY(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y, ...rest} = options; // note: consumes y! - const [transform, X, y1, y2] = stack(x, y, "y", stackOptions, rest); + const [transform, X, y1, y2] = stackAlias(x, y, "y", stack, rest); return {...transform, x1, x: X, y1, y2, y: mid(y1, y2)}; } @@ -76,10 +76,10 @@ export function stackY(stackOptions = {}, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackY1(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackY1(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y} = options; - const [transform, X, Y] = stack(x, y, "y", stackOptions, options); + const [transform, X, Y] = stackAlias(x, y, "y", stack, options); return {...transform, x1, x: X, y: Y}; } @@ -92,10 +92,10 @@ export function stackY1(stackOptions = {}, options = {}) { * * @link https://github.com/observablehq/plot/blob/main/README.md#stack */ -export function stackY2(stackOptions = {}, options = {}) { - if (arguments.length === 1) [stackOptions, options] = mergeOptions(stackOptions); +export function stackY2(stack = {}, options = {}) { + if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y} = options; - const [transform, X, , Y] = stack(x, y, "y", stackOptions, options); + const [transform, X, , Y] = stackAlias(x, y, "y", stack, options); return {...transform, x1, x: X, y: Y}; } @@ -161,6 +161,9 @@ function stack(x, y = one, ky, {offset, order, reverse}, options) { ]; } +// This is used internally so we can use `stack` as an argument name. +const stackAlias = stack; + function maybeOffset(offset) { if (offset == null) return; if (typeof offset === "function") return offset; From 7c520d3f9aac1d1560301800f7d676f1004017ce Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:23:18 -0400 Subject: [PATCH 56/59] jsdoc-to-readme - make links to other parts of README relative --- README.md | 122 ++++++++++++++++++------------------- scripts/jsdoc-to-readme.ts | 10 ++- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index fad880801d..221465dd61 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Renders a new plot given the specified *options* and returns the corresponding S ### Mark options -The **marks** option specifies an array of [marks](https://github.com/observablehq/plot/blob/main/README.md#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](https://github.com/observablehq/plot/blob/main/README.md#bar) or [dot](https://github.com/observablehq/plot/blob/main/README.md#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). +The **marks** option specifies an array of [marks](#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](#bar) or [dot](#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). ```js Plot.plot({ @@ -140,8 +140,8 @@ Each scale’s options are specified as a nested options object with the corresp * **r** - radius (size) * **color** - fill or stroke * **opacity** - fill or stroke opacity -* **length** - linear length (for [vectors](https://github.com/observablehq/plot/blob/main/README.md#vector)) -* **symbol** - categorical symbol (for [dots](https://github.com/observablehq/plot/blob/main/README.md#dot)) +* **length** - linear length (for [vectors](#vector)) +* **symbol** - categorical symbol (for [dots](#dot)) For example, to set the domain for the *x* and *y* scales: @@ -156,9 +156,9 @@ Plot.plot({ }) ``` -Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](https://github.com/observablehq/plot/blob/main/README.md#position-options) or [color](https://github.com/observablehq/plot/blob/main/README.md#color-options). +Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](#position-options) or [color](#color-options). -You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](https://github.com/observablehq/plot/blob/main/README.md#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. +You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: @@ -177,7 +177,7 @@ For temporal data (*i.e.* dates), two variants of a *linear* scale are also supp UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. -For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](https://github.com/observablehq/plot/blob/main/README.md#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](https://github.com/observablehq/plot/blob/main/README.md#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) +For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. @@ -191,9 +191,9 @@ A scale’s domain (the extent of its inputs, abstract values) and range (the ex * *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* * *scale*.**interval** - an interval or time interval (for interval data; see below) -For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](https://github.com/observablehq/plot/blob/main/README.md#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. +For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. -The default range depends on the scale: for [position scales](https://github.com/observablehq/plot/blob/main/README.md#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](https://github.com/observablehq/plot/blob/main/README.md#layout-options). For [color scales](https://github.com/observablehq/plot/blob/main/README.md#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. +The default range depends on the scale: for [position scales](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. @@ -234,7 +234,7 @@ console.log(color.range); // inspect the color scale’s range, ["red", "blue"] #### Plot.scale(*options*) -You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: ```js const color = Plot.scale({color: {type: "linear"}}); @@ -242,7 +242,7 @@ const color = Plot.scale({color: {type: "linear"}}); #### Scale objects -Both [*plot*.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscalescalename) and [Plot.scale](https://github.com/observablehq/plot/blob/main/README.md#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. +Both [*plot*.scale](#plotscalescalename) and [Plot.scale](#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. To reuse a scale across plots, pass the corresponding scale object into another plot specification: @@ -617,7 +617,7 @@ The **style** legend option allows custom styles to override Plot’s defaults; #### Plot.legend(*options*) -Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](https://github.com/observablehq/plot/blob/main/README.md#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: ```js Plot.legend({color: {type: "linear"}}) @@ -810,7 +810,7 @@ The area mark supports [curve options](#curves) to control interpolation between Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) ``` -Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](https://github.com/observablehq/plot/blob/main/README.md#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](https://github.com/observablehq/plot/blob/main/README.md#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. +Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. @@ -820,9 +820,9 @@ Returns a new area with the given *data* and *options*. Plot.area is rarely used Plot.areaX(aapl, {y: "Date", x: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. -If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -838,9 +838,9 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.areaY(aapl, {x: "Date", y: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. -If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) @@ -909,11 +909,11 @@ Returns a new horizontal bar↔︎ with the given *data* and *options*. The foll * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. +If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. -In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: +In addition to the [standard bar channels](#bar), the following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* @@ -932,11 +932,11 @@ Returns a new vertical bar↕︎ with the given *data* and *options*. The follow * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. +If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. -In addition to the [standard bar channels](https://github.com/observablehq/plot/blob/main/README.md#bar), the following optional channels are supported: +In addition to the [standard bar channels](#bar), the following optional channels are supported: * **x** - the horizontal position; bound to the *x* scale, which must be *band* @@ -1020,7 +1020,7 @@ Returns a new cell with the given *data* and *options*. If neither the **x** nor Plot.cellX(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.cell](#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. @@ -1030,7 +1030,7 @@ Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README. Plot.cellY(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](https://github.com/observablehq/plot/blob/main/README.md#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.cell](#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -1149,7 +1149,7 @@ Returns a new dot with the given *data* and *options*. If neither the **x** nor Plot.dotX(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.dot](#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. @@ -1161,7 +1161,7 @@ If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*in Plot.dotY(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.dot](#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. @@ -1169,13 +1169,13 @@ If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*in #### Plot.circle(*data*, *options*) -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *circle*. +Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *circle*. #### Plot.hexagon(*data*, *options*) -Equivalent to [Plot.dot](https://github.com/observablehq/plot/blob/main/README.md#plotdotdata-options) except that the **symbol** option is set to *hexagon*. +Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *hexagon*. @@ -1309,9 +1309,9 @@ Returns a new line with the given *data* and *options*. If neither the **x** nor Plot.lineX(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. +Similar to [Plot.line](#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binY transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) @@ -1327,9 +1327,9 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.lineY(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](https://github.com/observablehq/plot/blob/main/README.md#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. +Similar to [Plot.line](#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binX transform](https://github.com/observablehq/plot/blob/main/README.md#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. ```js Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) @@ -1407,7 +1407,7 @@ Returns a new rect with the given *data* and *options*. Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. +Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. @@ -1417,7 +1417,7 @@ Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README. Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Equivalent to [Plot.rect](https://github.com/observablehq/plot/blob/main/README.md#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. +Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. @@ -1440,7 +1440,7 @@ Plot.ruleX([0]) // as annotation Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY ``` -Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: +Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: * **x** - the horizontal position; bound to the *x* scale * **y1** - the starting vertical position; bound to the *y* scale @@ -1461,7 +1461,7 @@ Plot.ruleY([0]) // as annotation Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX ``` -Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](https://github.com/observablehq/plot/blob/main/README.md#marks), the following channels are optional: +Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: * **y** - the vertical position; bound to the *y* scale * **x1** - the starting horizontal position; bound to the *x* scale @@ -1527,7 +1527,7 @@ Returns a new text mark with the given *data* and *options*. If neither the **x* #### Plot.textX(*data*, *options*) -Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.text](#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. @@ -1535,7 +1535,7 @@ If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*in #### Plot.textY(*data*, *options*) -Equivalent to [Plot.text](https://github.com/observablehq/plot/blob/main/README.md#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.text](#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. @@ -1631,13 +1631,13 @@ Returns a new vector with the given *data* and *options*. If neither the **x** n #### Plot.vectorX(*data*, *options*) -Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to [Plot.vector](#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. #### Plot.vectorY(*data*, *options*) -Equivalent to [Plot.vector](https://github.com/observablehq/plot/blob/main/README.md#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to [Plot.vector](#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -1717,7 +1717,7 @@ The *filter*, *sort* and *reverse* transforms are also available as functions, a Plot.sort("body_mass_g", options) // show data in ascending body mass order ``` -Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](https://github.com/observablehq/plot/blob/main/README.md#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. +Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. @@ -2099,7 +2099,7 @@ Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for ea -#### Plot.mapX(*mapping*, *options*) +#### Plot.mapX(*map*, *options*) ```js Plot.mapX("cumsum", {x: d3.randomNormal()}) @@ -2110,7 +2110,7 @@ Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores -#### Plot.mapY(*mapping*, *options*) +#### Plot.mapY(*map*, *options*) ```js Plot.mapY("cumsum", {y: d3.randomNormal()}) @@ -2137,7 +2137,7 @@ Returns a normalize map method for the given *basis*, suitable for use with Plot Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotmapxmap-options), but applies the normalize map method with the given *basis*. +Like [Plot.mapX](#plotmapxmap-options), but applies the normalize map method with the given *basis*. @@ -2147,7 +2147,7 @@ Like [Plot.mapX](https://github.com/observablehq/plot/blob/main/README.md#plotma Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapY](https://github.com/observablehq/plot/blob/main/README.md#plotmapymap-options), but applies the normalize map method with the given *basis*. +Like [Plot.mapY](#plotmapymap-options), but applies the normalize map method with the given *basis*. @@ -2301,7 +2301,7 @@ If two arguments are passed to the stack transform functions below, the stack-sp -#### Plot.stackY(*stackOptions*, *options*) +#### Plot.stackY(*stack*, *options*) ```js Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) @@ -2312,29 +2312,29 @@ Creates new channels **y1** and **y2**, obtained by stacking the original **y** -#### Plot.stackY1(*stackOptions*, *options*) +#### Plot.stackY1(*stack*, *options*) ```js Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackY](https://github.com/observablehq/plot/blob/main/README.md#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. +Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. -#### Plot.stackX(*stackOptions*, *options*) +#### Plot.stackX(*stack*, *options*) ```js Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) @@ -2345,24 +2345,24 @@ See Plot.stackY, but with *x* as the input value channel, *y* as the stack index -#### Plot.stackX1(*stackOptions*, *options*) +#### Plot.stackX1(*stack*, *options*) ```js Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. +Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. -#### Plot.stackX2(*stackOptions*, *options*) +#### Plot.stackX2(*stack*, *options*) ```js Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](https://github.com/observablehq/plot/blob/main/README.md#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. +Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. @@ -2425,7 +2425,7 @@ The default **treeLayout** implements the Reingold–Tilford “tidy” algorith #### Plot.treeNode(*options*) -Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](https://github.com/observablehq/plot/blob/main/README.md#dot), [text](https://github.com/observablehq/plot/blob/main/README.md#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). +Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](#dot), [text](#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). The treeNode transform will derive output columns for any *options* that have one of the following named node values: @@ -2441,7 +2441,7 @@ In addition, if any option value is specified as an object with a **node** metho #### Plot.treeLink(*options*) -Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](https://github.com/observablehq/plot/blob/main/README.md#link), [arrow](https://github.com/observablehq/plot/blob/main/README.md#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options). +Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](#link), [arrow](#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). The treeLink transform will derive output columns for any *options* that have one of the following named link values: @@ -2461,7 +2461,7 @@ In addition, if any option value is specified as an object with a **node** metho #### Plot.tree(*data*, *options*) -A convenience compound mark for rendering a tree diagram, including a [link](https://github.com/observablehq/plot/blob/main/README.md#link) to render links from parent to child, an optional [dot](https://github.com/observablehq/plot/blob/main/README.md#dot) for nodes, and a [text](https://github.com/observablehq/plot/blob/main/README.md#text) for node labels. The link mark uses the [treeLink transform](https://github.com/observablehq/plot/blob/main/README.md#plottreelinkoptions), while the dot and text marks use the [treeNode transform](https://github.com/observablehq/plot/blob/main/README.md#plottreenodeoptions). The following options are supported: +A convenience compound mark for rendering a tree diagram, including a [link](#link) to render links from parent to child, an optional [dot](#dot) for nodes, and a [text](#text) for node labels. The link mark uses the [treeLink transform](#plottreelinkoptions), while the dot and text marks use the [treeNode transform](#plottreenodeoptions). The following options are supported: * **fill** - the dot and text fill color; defaults to *node:internal* * **stroke** - the link stroke color; inherits **fill** by default @@ -2488,7 +2488,7 @@ Any additional *options* are passed through to the constituent link, dot, and te #### Plot.cluster(*data*, *options*) -Like [Plot.tree](https://github.com/observablehq/plot/blob/main/README.md#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. +Like [Plot.tree](#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. @@ -2584,7 +2584,7 @@ Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position #### Plot.hexbin(*outputs*, *options*) -Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](https://github.com/observablehq/plot/blob/main/README.md#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. +Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. The following aggregation methods are supported: @@ -2607,7 +2607,7 @@ The following aggregation methods are supported: * a function to be passed the array of values for each bin and the extent of the bin * an object with a *reduce* method -See also the [hexgrid](https://github.com/observablehq/plot/blob/main/README.md#hexgrid) mark. +See also the [hexgrid](#hexgrid) mark. @@ -2697,7 +2697,7 @@ Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the give -#### Plot.formatWeekday(*locale*, *weekday*) +#### Plot.formatWeekday(*locale*, *format*) ```js Plot.formatWeekday("es-MX", "long")(0) // "domingo" @@ -2707,7 +2707,7 @@ Returns a function that formats a given week day number (from 0 = Sunday to 6 = -#### Plot.formatMonth(*locale*, *month*) +#### Plot.formatMonth(*locale*, *format*) ```js Plot.formatMonth("es-MX", "long")(0) // "enero" diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts index d336bec673..b71ad20122 100644 --- a/scripts/jsdoc-to-readme.ts +++ b/scripts/jsdoc-to-readme.ts @@ -68,7 +68,7 @@ function getJsDocs(name: string, declaration: ExportedDeclarations, prefix = "## if ("getJsDocs" in declaration) { return `${prefix} Plot.${name}\n${declaration .getJsDocs() - .map((doc) => doc.getDescription()) + .map((doc) => makeRelativeUrls(doc.getDescription())) .join("\n\n")}`; } return `JSDoc extraction for ${declaration.getKindName()} not yet implemented.`; @@ -82,7 +82,7 @@ function getJsDocsForFunction(name: string, declaration: FunctionDeclaration, pr const parts = [title]; const docs = declaration.getJsDocs(); if (docs.length) { - parts.push(docs.map((doc) => doc.getDescription()).join("\n\n")); + parts.push(docs.map((doc) => makeRelativeUrls(doc.getDescription())).join("\n\n")); return parts.join("\n"); } // If we didn't find docs on the implementation, it's probably on one of the @@ -91,13 +91,17 @@ function getJsDocsForFunction(name: string, declaration: FunctionDeclaration, pr for (const overload of overloads) { const docs = overload.getJsDocs(); if (!docs.length) continue; - parts.push(docs.map((doc) => doc.getDescription()).join("\n\n")); + parts.push(docs.map((doc) => makeRelativeUrls(doc.getDescription())).join("\n\n")); return parts.join("\n"); } return "No JSDocs found."; } +function makeRelativeUrls(description: string) { + return description.replace(new RegExp("https://github.com/observablehq/plot/blob/main/README.md#", "g"), "#"); +} + const check = process.argv[process.argv.length - 1] === "--check"; const original = readFileSync(readmePath, {encoding: "utf-8"}); const output = injectJsDoc(original); From 839f33cbe9a996e454d65b5d3892460620f42cbd Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:27:57 -0400 Subject: [PATCH 57/59] fix README --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 221465dd61..a7d2f88f2d 100644 --- a/README.md +++ b/README.md @@ -296,7 +296,7 @@ Plot automatically generates axes for position scales. You can configure these a * *scale*.**ticks** - the approximate number of ticks to generate * *scale*.**tickSize** - the size of each tick (in pixels; default 6) * *scale*.**tickPadding** - the separation between the tick and its label (in pixels; default 3) -* *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](https://github.com/observablehq/plot/blob/main/README.md#formats) +* *scale*.**tickFormat** - to format tick values, either a function or format specifier string; see [Formats](#formats) * *scale*.**tickRotate** - whether to rotate tick labels (an angle in degrees clockwise; default 0) * *scale*.**grid** - if true, draw grid lines across the plot for each tick * *scale*.**line** - if true, draw the axis line @@ -307,7 +307,7 @@ Plot automatically generates axes for position scales. You can configure these a * *scale*.**ariaLabel** - a short label representing the axis in the accessibility tree * *scale*.**ariaDescription** - a textual description for the axis -Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](https://github.com/observablehq/plot/blob/main/README.md#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. +Top-level options are also supported as shorthand: **grid** (for *x* and *y* only; see [facet.grid](#facet-options)), **label**, **axis**, **inset**, **round**, **align**, and **padding**. ### Color options @@ -441,7 +441,7 @@ If an ordinal scale’s domain is not set, it defaults to natural ascending orde Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y"}}) ``` -The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](https://github.com/observablehq/plot/blob/main/README.md#transforms) (such as [stack](https://github.com/observablehq/plot/blob/main/README.md#stack) or [bin](https://github.com/observablehq/plot/blob/main/README.md#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](https://github.com/observablehq/plot/blob/main/README.md#area), [bar](https://github.com/observablehq/plot/blob/main/README.md#bar), and [rect](https://github.com/observablehq/plot/blob/main/README.md#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. +The sort option is an object whose keys are ordinal scale names, such as *x* or *fx*, and whose values are mark channel names, such as *y*, *y1*, or *y2*. By specifying an existing channel rather than a new value, you avoid repeating the order definition and can refer to channels derived by [transforms](#transforms) (such as [stack](#stack) or [bin](#bin)). When sorting on the *x*, if no such channel is defined, the *x2* channel will be used instead if available, and similarly for *y* and *y2*; this is useful for marks that implicitly stack such as [area](#area), [bar](#bar), and [rect](#rect). A sort value may also be specified as *width* or *height*, representing derived channels |*x2* - *x1*| and |*y2* - *y1*| respectively. Note that there may be multiple associated values in the secondary dimension for a given value in the primary ordinal dimension. The secondary values are therefore grouped for each associated primary value, and each group is then aggregated by applying a reducer. Lastly the primary values are sorted based on the associated reduced value in natural ascending order to produce the domain. The default reducer is *max*, but may be changed by specifying the *reduce* option. The above code is shorthand for: @@ -449,7 +449,7 @@ Note that there may be multiple associated values in the secondary dimension for Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}) ``` -Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](https://github.com/observablehq/plot/blob/main/README.md#group) for the list of supported reducers. +Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. TODO An example of assigning categorical colors in a scatterplot by descending count to maximize discriminability. See the [group transform](#group) for the list of supported reducers. For descending rather than ascending order, use the *reverse* option: @@ -457,7 +457,7 @@ For descending rather than ascending order, use the *reverse* option: Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}) ``` -An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](https://github.com/observablehq/plot/blob/main/README.md#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) +An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo* ≤ *i* < *hi* will be selected. (Note that like the [basic filter transform](#transforms), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.) ```js Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", limit: 5}}) @@ -471,7 +471,7 @@ Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: {value: "y", reverse If the input channel is *data*, then the reducer is passed groups of the mark’s data; this is typically used in conjunction with a custom reducer function, as when the built-in single-channel reducers are insufficient. -Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](https://github.com/observablehq/plot/blob/main/README.md#transforms). To use both sort options and a sort transform, use [Plot.sort](https://github.com/observablehq/plot/blob/main/README.md#plotsortorder-options). +Note: when the value of the sort option is a string or a function, it is interpreted as a [basic sort transform](#transforms). To use both sort options and a sort transform, use [Plot.sort](#plotsortorder-options). ### Facet options @@ -480,7 +480,7 @@ The *facet* option enables [faceting](https://observablehq.com/@observablehq/plo * **fx** - the horizontal position, a *band* scale * **fy** - the vertical position, a *band* scale -Similar to [marks](https://github.com/observablehq/plot/blob/main/README.md#marks), faceting requires specifying data and at least one of two optional channels: +Similar to [marks](#marks), faceting requires specifying data and at least one of two optional channels: * facet.**data** - the data to be faceted * facet.**x** - the horizontal position; bound to the *fx* scale, which must be *band* @@ -523,7 +523,7 @@ When the *include* or *exclude* facet mode is chosen, the mark data must be para ## Legends -Plot can generate legends for *color*, *opacity*, and *symbol* [scales](https://github.com/observablehq/plot/blob/main/README.md#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: +Plot can generate legends for *color*, *opacity*, and *symbol* [scales](#scale-options). (An opacity scale is treated as a color scale with varying transparency.) For an inline legend, use the *scale*.**legend** option: * *scale*.**legend** - if truthy, generate a legend for the given scale @@ -558,7 +558,7 @@ Plot.plot({ #### *plot*.legend(*scaleName*, *options*) -Given an existing *plot* returned by [Plot.plot](https://github.com/observablehq/plot/blob/main/README.md#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: +Given an existing *plot* returned by [Plot.plot](#plotplotoptions), returns a detached legend for the *plot*’s scale with the given *scaleName*. The *scaleName* must refer to a scale that supports legends: either `"color"`, `"opacity"`, or `"symbol"`. For example: ```js myplot = Plot.plot(…) @@ -611,7 +611,7 @@ Continuous color legends are rendered as a ramp, and can be configured with the * *options*.**marginBottom** - the legend’s bottom margin * *options*.**marginLeft** - the legend’s left margin -The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](https://github.com/observablehq/plot/blob/main/README.md#layout-options). +The **style** legend option allows custom styles to override Plot’s defaults; it has the same behavior as in Plot’s top-level [layout options](#layout-options). From 9899f688156307bb14830318e6a9359be25fe70a Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:28:32 -0400 Subject: [PATCH 58/59] fix console.log --- scripts/jsdoc-to-readme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts index b71ad20122..52953bf1a7 100644 --- a/scripts/jsdoc-to-readme.ts +++ b/scripts/jsdoc-to-readme.ts @@ -108,7 +108,7 @@ const output = injectJsDoc(original); if (original !== output) { if (check) { - console.log("README.md is out of sync. Please run `yarn readme:udpate`"); + console.log("README.md is out of sync. Please run `yarn readme:update`"); process.exit(1); } else { writeFileSync(readmePath, output); From b1fb27bf545d7718b0ef34d437be961989c714ee Mon Sep 17 00:00:00 2001 From: Duane Millar Barlow Date: Fri, 26 Aug 2022 10:38:38 -0400 Subject: [PATCH 59/59] yarn readme:update --- README.md | 991 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 800 insertions(+), 191 deletions(-) diff --git a/README.md b/README.md index a7d2f88f2d..ad08a19f5c 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,24 @@ See also our [Plot + React example](https://github.com/observablehq/plot-create- ## Plot.plot(*options*) -Renders a new plot given the specified *options* and returns the corresponding SVG or HTML figure element. All *options* are optional. +Renders a new plot given the specified *options* and returns the +corresponding SVG or HTML figure element. All *options* are optional. ### Mark options -The **marks** option specifies an array of [marks](#marks) to render. Each mark has its own data and options; see the respective mark type (*e.g.*, [bar](#bar) or [dot](#dot)) for which mark options are supported. Each mark may be a nested array of marks, allowing composition. Marks may also be a function which returns an SVG element, if you wish to insert some arbitrary content into your plot. And marks may be null or undefined, which produce no output; this is useful for showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn in *z* order, last on top. For example, here a single rule at *y* = 0 is drawn on top of blue bars for the [*alphabet* dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). +The **marks** option specifies an array of +[marks](#marks) to +render. Each mark has its own data and options; see the respective mark type +(*e.g.*, [bar](#bar) +or [dot](#dot)) for +which mark options are supported. Each mark may be a nested array of marks, +allowing composition. Marks may also be a function which returns an SVG +element, if you wish to insert some arbitrary content into your plot. And +marks may be null or undefined, which produce no output; this is useful for +showing marks conditionally (*e.g.*, when a box is checked). Marks are drawn +in *z* order, last on top. For example, here a single rule at *y* = 0 is +drawn on top of blue bars for the [*alphabet* +dataset](https://github.com/observablehq/plot/blob/main/test/data/alphabet.csv). ```js Plot.plot({ @@ -81,7 +94,8 @@ Plot.plot({ ### Layout options -These options determine the overall layout of the plot; all are specified as numbers in pixels: +These options determine the overall layout of the plot; all are specified as +numbers in pixels: * **marginTop** - the top margin * **marginRight** - the right margin @@ -91,11 +105,39 @@ These options determine the overall layout of the plot; all are specified as num * **width** - the outer width of the plot (including margins) * **height** - the outer height of the plot (including margins) -The default **width** is 640. On Observable, the width can be set to the [standard width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to make responsive plots. The default **height** is chosen automatically based on the plot’s associated scales; for example, if *y* is linear and there is no *fy* scale, it might be 396. - -The default margins depend on the plot’s axes: for example, **marginTop** and **marginBottom** are at least 30 if there is a corresponding top or bottom *x* axis, and **marginLeft** and **marginRight** are at least 40 if there is a corresponding left or right *y* axis. For simplicity’s sake and for consistent layout across plots, margins are not automatically sized to make room for tick labels; instead, shorten your tick labels or increase the margins as needed. (In the future, margins may be specified indirectly via a scale property to make it easier to reorient axes without adjusting margins; see [#210](https://github.com/observablehq/plot/issues/210).) - -The **style** option allows custom styles to override Plot’s defaults. It may be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in the same fashion as assigning [*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as assigning [*element*.style properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). Note that unitless numbers ([quirky lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such as `{padding: 20}` may not supported by some browsers; you should instead specify a string with units such as `{padding: "20px"}`. By default, the returned plot has a white background, a max-width of 100%, and the system-ui font. Plot’s marks and axes default to [currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), meaning that they will inherit the surrounding content’s color. For example, a dark theme: +The default **width** is 640. On Observable, the width can be set to the +[standard +width](https://github.com/observablehq/stdlib/blob/main/README.md#width) to +make responsive plots. The default **height** is chosen automatically based +on the plot’s associated scales; for example, if *y* is linear and there is +no *fy* scale, it might be 396. + +The default margins depend on the plot’s axes: for example, **marginTop** and +**marginBottom** are at least 30 if there is a corresponding top or bottom +*x* axis, and **marginLeft** and **marginRight** are at least 40 if there is +a corresponding left or right *y* axis. For simplicity’s sake and for +consistent layout across plots, margins are not automatically sized to make +room for tick labels; instead, shorten your tick labels or increase the +margins as needed. (In the future, margins may be specified indirectly via a +scale property to make it easier to reorient axes without adjusting margins; +see [#210](https://github.com/observablehq/plot/issues/210).) + +The **style** option allows custom styles to override Plot’s defaults. It may +be specified either as a string of inline styles (*e.g.*, `"color: red;"`, in +the same fashion as assigning +[*element*.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)) +or an object of properties (*e.g.*, `{color: "red"}`, in the same fashion as +assigning [*element*.style +properties](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration)). +Note that unitless numbers ([quirky +lengths](https://www.w3.org/TR/css-values-4/#deprecated-quirky-length)) such +as `{padding: 20}` may not supported by some browsers; you should instead +specify a string with units such as `{padding: "20px"}`. By default, the +returned plot has a white background, a max-width of 100%, and the system-ui +font. Plot’s marks and axes default to +[currentColor](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentcolor_keyword), +meaning that they will inherit the surrounding content’s color. For example, +a dark theme: ```js Plot.plot({ @@ -107,7 +149,11 @@ Plot.plot({ }) ``` -If a **caption** is specified, Plot.plot wraps the generated SVG element in an HTML figure element with a figcaption, returning the figure. To specify an HTML caption, consider using the [`html` tagged template literal](http://github.com/observablehq/htl); otherwise, the specified string represents text that will be escaped as needed. +If a **caption** is specified, Plot.plot wraps the generated SVG element in +an HTML figure element with a figcaption, returning the figure. To specify an +HTML caption, consider using the [`html` tagged template +literal](http://github.com/observablehq/htl); otherwise, the specified string +represents text that will be escaped as needed. ```js Plot.plot({ @@ -116,13 +162,26 @@ Plot.plot({ }) ``` -The generated SVG element has a random class name which applies a default stylesheet. Use the top-level **className** option to specify that class name. +The generated SVG element has a random class name which applies a default +stylesheet. Use the top-level **className** option to specify that class +name. -The **document** option specifies the [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to create plot elements. It defaults to window.document, but can be changed to another document, say when using a virtual DOM library for server-side rendering in Node. +The **document** option specifies the +[document](https://developer.mozilla.org/en-US/docs/Web/API/Document) used to +create plot elements. It defaults to window.document, but can be changed to +another document, say when using a virtual DOM library for server-side +rendering in Node. ### Scale options -Plot passes data through [scales](https://observablehq.com/@observablehq/plot-scales) as needed before rendering marks. A scale maps abstract values such as time or temperature to visual values such as position or color. Within a given plot, marks share scales. For example, if a plot has two Plot.line marks, both share the same *x* and *y* scales for a consistent representation of data. (Plot does not currently support dual-axis charts, which are [not advised](https://blog.datawrapper.de/dualaxis/).) +Plot passes data through +[scales](https://observablehq.com/@observablehq/plot-scales) as needed before +rendering marks. A scale maps abstract values such as time or temperature to +visual values such as position or color. Within a given plot, marks share +scales. For example, if a plot has two Plot.line marks, both share the same +*x* and *y* scales for a consistent representation of data. (Plot does not +currently support dual-axis charts, which are [not +advised](https://blog.datawrapper.de/dualaxis/).) ```js Plot.plot({ @@ -133,15 +192,18 @@ Plot.plot({ }) ``` -Each scale’s options are specified as a nested options object with the corresponding scale name within the top-level plot *options*: +Each scale’s options are specified as a nested options object with the +corresponding scale name within the top-level plot *options*: * **x** - horizontal position * **y** - vertical position * **r** - radius (size) * **color** - fill or stroke * **opacity** - fill or stroke opacity -* **length** - linear length (for [vectors](#vector)) -* **symbol** - categorical symbol (for [dots](#dot)) +* **length** - linear length (for + [vectors](#vector)) +* **symbol** - categorical symbol (for + [dots](#dot)) For example, to set the domain for the *x* and *y* scales: @@ -156,59 +218,171 @@ Plot.plot({ }) ``` -Plot supports many scale types. Some scale types are for quantitative data: values that can be added or subtracted, such as temperature or time. Other scale types are for ordinal or categorical data: unquantifiable values that can only be ordered, such as t-shirt sizes, or values with no inherent order that can only be tested for equality, such as types of fruit. Some scale types are further intended for specific visual encodings: for example, as [position](#position-options) or [color](#color-options). - -You can set the scale type explicitly via the *scale*.**type** option, though typically the scale type is inferred automatically. Some marks mandate a particular scale type: for example, [Plot.barY](#plotbarydata-options) requires that the *x* scale is a *band* scale. Some scales have a default type: for example, the *radius* scale defaults to *sqrt* and the *opacity* scale defaults to *linear*. Most often, the scale type is inferred from associated data, pulled either from the domain (if specified) or from associated channels. A *color* scale defaults to *identity* if no range or scheme is specified and all associated defined values are valid CSS color strings. Otherwise, strings and booleans imply an ordinal scale; dates imply a UTC scale; and anything else is linear. Unless they represent text, we recommend explicitly converting strings to more specific types when loading data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For simplicity’s sake, Plot assumes that data is consistently typed; type inference is based solely on the first non-null, non-undefined value. - -For quantitative data (*i.e.* numbers), a mathematical transform may be applied to the data by changing the scale type: +Plot supports many scale types. Some scale types are for quantitative data: +values that can be added or subtracted, such as temperature or time. Other +scale types are for ordinal or categorical data: unquantifiable values that +can only be ordered, such as t-shirt sizes, or values with no inherent order +that can only be tested for equality, such as types of fruit. Some scale +types are further intended for specific visual encodings: for example, as +[position](#position-options) +or +[color](#color-options). + +You can set the scale type explicitly via the *scale*.**type** option, though +typically the scale type is inferred automatically. Some marks mandate a +particular scale type: for example, +[Plot.barY](#plotbarydata-options) +requires that the *x* scale is a *band* scale. Some scales have a default +type: for example, the *radius* scale defaults to *sqrt* and the *opacity* +scale defaults to *linear*. Most often, the scale type is inferred from +associated data, pulled either from the domain (if specified) or from +associated channels. A *color* scale defaults to *identity* if no range or +scheme is specified and all associated defined values are valid CSS color +strings. Otherwise, strings and booleans imply an ordinal scale; dates imply +a UTC scale; and anything else is linear. Unless they represent text, we +recommend explicitly converting strings to more specific types when loading +data (*e.g.*, with d3.autoType or Observable’s FileAttachment). For +simplicity’s sake, Plot assumes that data is consistently typed; type +inference is based solely on the first non-null, non-undefined value. + +For quantitative data (*i.e.* numbers), a mathematical transform may be +applied to the data by changing the scale type: * *linear* (default) - linear transform (translate and scale) * *pow* - power (exponential) transform * *sqrt* - square-root transform (*pow* transform with exponent = 0.5) * *log* - logarithmic transform -* *symlog* - bi-symmetric logarithmic transform per [Webber *et al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) - -The appropriate transform depends on the data’s distribution and what you wish to know. A *sqrt* transform exaggerates differences between small values at the expense of large values; it is a special case of the *pow* transform which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* transform is suitable for comparing orders of magnitude and can only be used when the domain does not include zero. The base defaults to 10 and can be specified with the *scale*.**base** option; note that this only affects the axis ticks and not the scale’s behavior. A *symlog* transform is more elaborate, but works well with wide-range values that include zero; it can be configured with the *scale*.**constant** option (default 1). - -For temporal data (*i.e.* dates), two variants of a *linear* scale are also supported: +* *symlog* - bi-symmetric logarithmic transform per [Webber *et + al.*](https://www.researchgate.net/publication/233967063_A_bi-symmetric_log_transformation_for_wide-range_data) + +The appropriate transform depends on the data’s distribution and what you +wish to know. A *sqrt* transform exaggerates differences between small values +at the expense of large values; it is a special case of the *pow* transform +which has a configurable *scale*.**exponent** (0.5 for *sqrt*). A *log* +transform is suitable for comparing orders of magnitude and can only be used +when the domain does not include zero. The base defaults to 10 and can be +specified with the *scale*.**base** option; note that this only affects the +axis ticks and not the scale’s behavior. A *symlog* transform is more +elaborate, but works well with wide-range values that include zero; it can be +configured with the *scale*.**constant** option (default 1). + +For temporal data (*i.e.* dates), two variants of a *linear* scale are also +supported: * *utc* (default, recommended) - UTC time * *time* - local time -UTC is recommended over local time as charts in UTC time are guaranteed to appear consistently to all viewers whereas charts in local time will depend on the viewer’s time zone. Due to limitations in JavaScript’s Date class, Plot does not yet support an explicit time zone other than UTC. - -For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the *point* or *band* [position scale types](#position-options). The *categorical* scale type is also supported; it is equivalent to *ordinal* except as a [color scale](#color-options), where it provides a different default color scheme. (Since position is inherently ordinal or even quantitative, categorical data must be assigned an effective order when represented as position, and hence *categorical* and *ordinal* may be considered synonymous in context.) - -You can opt-out of a scale using the *identity* scale type. This is useful if you wish to specify literal colors or pixel positions within a mark channel rather than relying on the scale to convert abstract values into visual values. For position scales (*x* and *y*), an *identity* scale is still quantitative and may produce an axis, yet unlike a *linear* scale the domain and range are fixed based on the plot layout. - -Quantitative scales, as well as identity position scales, coerce channel values to numbers; both null and undefined are coerced to NaN. Similarly, time scales coerce channel values to dates; numbers are assumed to be milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). - -A scale’s domain (the extent of its inputs, abstract values) and range (the extent of its outputs, visual values) are typically inferred automatically. You can set them explicitly using these options: - -* *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or categorical values -* *scale*.**range** - typically [*min*, *max*], or an array of ordinal or categorical values -* *scale*.**unknown** - the desired output value (defaults to undefined) for invalid input values -* *scale*.**reverse** - reverses the domain (or in somes cases, the range), say to flip the chart along *x* or *y* -* *scale*.**interval** - an interval or time interval (for interval data; see below) - -For most quantitative scales, the default domain is the [*min*, *max*] of all values associated with the scale. For the *radius* and *opacity* scales, the default domain is [0, *max*] to ensure a meaningful value encoding. For ordinal scales, the default domain is the set of all distinct values associated with the scale in natural ascending order; for a different order, set the domain explicitly or add a [sort option](#sort-options) to an associated mark. For threshold scales, the default domain is [0] to separate negative and non-negative values. For quantile scales, the default domain is the set of all defined values associated with the scale. If a scale is reversed, it is equivalent to setting the domain as [*max*, *min*] instead of [*min*, *max*]. - -The default range depends on the scale: for [position scales](#position-options) (*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and margins](#layout-options). For [color scales](#color-options), there are default color schemes for quantitative, ordinal, and categorical data. For opacity, the default range is [0, 1]. And for radius, the default range is designed to produce dots of “reasonable” size assuming a *sqrt* scale type for accurate area representation: zero maps to zero, the first quartile maps to a radius of three pixels, and other values are extrapolated. This convention for radius ensures that if the scale’s data values are all equal, dots have the default constant radius of three pixels, while if the data varies, dots will tend to be larger. - -The behavior of the *scale*.**unknown** option depends on the scale type. For quantitative and temporal scales, the unknown value is used whenever the input value is undefined, null, or NaN. For ordinal or categorical scales, the unknown value is returned for any input value outside the domain. For band or point scales, the unknown option has no effect; it is effectively always equal to undefined. If the unknown option is set to undefined (the default), or null or NaN, then the affected input values will be considered undefined and filtered from the output. - -For data at regular intervals, such as integer values or daily samples, the *scale*.**interval** option can be used to enforce uniformity. The specified *interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), *interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. The option can also be specified as a number, in which case it will be promoted to a numeric interval with the given step. This option sets the default *scale*.transform to the given interval’s *interval*.floor function. In addition, the default *scale*.domain is an array of uniformly-spaced values spanning the extent of the values associated with the scale. +UTC is recommended over local time as charts in UTC time are guaranteed to +appear consistently to all viewers whereas charts in local time will depend +on the viewer’s time zone. Due to limitations in JavaScript’s Date class, +Plot does not yet support an explicit time zone other than UTC. + +For ordinal data (*e.g.*, strings), use the *ordinal* scale type or the +*point* or *band* [position scale +types](#position-options). +The *categorical* scale type is also supported; it is equivalent to *ordinal* +except as a [color +scale](#color-options), +where it provides a different default color scheme. (Since position is +inherently ordinal or even quantitative, categorical data must be assigned an +effective order when represented as position, and hence *categorical* and +*ordinal* may be considered synonymous in context.) + +You can opt-out of a scale using the *identity* scale type. This is useful if +you wish to specify literal colors or pixel positions within a mark channel +rather than relying on the scale to convert abstract values into visual +values. For position scales (*x* and *y*), an *identity* scale is still +quantitative and may produce an axis, yet unlike a *linear* scale the domain +and range are fixed based on the plot layout. + +Quantitative scales, as well as identity position scales, coerce channel +values to numbers; both null and undefined are coerced to NaN. Similarly, +time scales coerce channel values to dates; numbers are assumed to be +milliseconds since UNIX epoch, while strings are assumed to be in [ISO 8601 +format](https://github.com/mbostock/isoformat/blob/main/README.md#parsedate-fallback). + +A scale’s domain (the extent of its inputs, abstract values) and range (the +extent of its outputs, visual values) are typically inferred automatically. +You can set them explicitly using these options: + +* *scale*.**domain** - typically [*min*, *max*], or an array of ordinal or + categorical values +* *scale*.**range** - typically [*min*, *max*], or an array of ordinal or + categorical values +* *scale*.**unknown** - the desired output value (defaults to undefined) for + invalid input values +* *scale*.**reverse** - reverses the domain (or in somes cases, the range), + say to flip the chart along *x* or *y* +* *scale*.**interval** - an interval or time interval (for interval data; see + below) + +For most quantitative scales, the default domain is the [*min*, *max*] of all +values associated with the scale. For the *radius* and *opacity* scales, the +default domain is [0, *max*] to ensure a meaningful value encoding. For +ordinal scales, the default domain is the set of all distinct values +associated with the scale in natural ascending order; for a different order, +set the domain explicitly or add a [sort +option](#sort-options) +to an associated mark. For threshold scales, the default domain is [0] to +separate negative and non-negative values. For quantile scales, the default +domain is the set of all defined values associated with the scale. If a scale +is reversed, it is equivalent to setting the domain as [*max*, *min*] instead +of [*min*, *max*]. + +The default range depends on the scale: for [position +scales](#position-options) +(*x*, *y*, *fx*, and *fy*), the default range depends on the plot’s [size and +margins](#layout-options). +For [color +scales](#color-options), +there are default color schemes for quantitative, ordinal, and categorical +data. For opacity, the default range is [0, 1]. And for radius, the default +range is designed to produce dots of “reasonable” size assuming a *sqrt* +scale type for accurate area representation: zero maps to zero, the first +quartile maps to a radius of three pixels, and other values are extrapolated. +This convention for radius ensures that if the scale’s data values are all +equal, dots have the default constant radius of three pixels, while if the +data varies, dots will tend to be larger. + +The behavior of the *scale*.**unknown** option depends on the scale type. For +quantitative and temporal scales, the unknown value is used whenever the +input value is undefined, null, or NaN. For ordinal or categorical scales, +the unknown value is returned for any input value outside the domain. For +band or point scales, the unknown option has no effect; it is effectively +always equal to undefined. If the unknown option is set to undefined (the +default), or null or NaN, then the affected input values will be considered +undefined and filtered from the output. + +For data at regular intervals, such as integer values or daily samples, the +*scale*.**interval** option can be used to enforce uniformity. The specified +*interval*—such as d3.utcMonth—must expose an *interval*.floor(*value*), +*interval*.offset(*value*), and *interval*.range(*start*, *stop*) functions. +The option can also be specified as a number, in which case it will be +promoted to a numeric interval with the given step. This option sets the +default *scale*.transform to the given interval’s *interval*.floor function. +In addition, the default *scale*.domain is an array of uniformly-spaced +values spanning the extent of the values associated with the scale. Quantitative scales can be further customized with additional options: * *scale*.**clamp** - if true, clamp input values to the scale’s domain -* *scale*.**nice** - if true (or a tick count), extend the domain to nice round values +* *scale*.**nice** - if true (or a tick count), extend the domain to nice + round values * *scale*.**zero** - if true, extend the domain to include zero if needed -* *scale*.**percent** - if true, transform proportions in [0, 1] to percentages in [0, 100] +* *scale*.**percent** - if true, transform proportions in [0, 1] to + percentages in [0, 100] -Clamping is typically used in conjunction with setting an explicit domain since if the domain is inferred, no values will be outside the domain. Clamping is useful for focusing on a subset of the data while ensuring that extreme values remain visible, but use caution: clamped values may need an annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and **zero** options are supported as shorthand for setting the respective option on all scales. +Clamping is typically used in conjunction with setting an explicit domain +since if the domain is inferred, no values will be outside the domain. +Clamping is useful for focusing on a subset of the data while ensuring that +extreme values remain visible, but use caution: clamped values may need an +annotation to avoid misinterpretation. Top-level **clamp**, **nice**, and +**zero** options are supported as shorthand for setting the respective option +on all scales. -The *scale*.**transform** option allows you to apply a function to all values before they are passed through the scale. This is convenient for transforming a scale’s data, say to convert to thousands or between temperature units. +The *scale*.**transform** option allows you to apply a function to all values +before they are passed through the scale. This is convenient for transforming +a scale’s data, say to convert to thousands or between temperature units. ```js Plot.plot({ @@ -222,7 +396,11 @@ Plot.plot({ #### *plot*.scale(*scaleName*) -Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) function of a returned plot. The *scaleName* must be one of the known scale names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, `"symbol"`, or `"length"`. If the associated *plot* has no scale with the given *scaleName*, returns undefined. +Scale definitions can be exposed through the *plot*.**scale**(*scaleName*) +function of a returned plot. The *scaleName* must be one of the known scale +names: `"x"`, `"y"`, `"fx"`, `"fy"`, `"r"`, `"color"`, `"opacity"`, +`"symbol"`, or `"length"`. If the associated *plot* has no scale with the +given *scaleName*, returns undefined. ```js const plot = Plot.plot(…); // render a plot @@ -234,7 +412,11 @@ console.log(color.range); // inspect the color scale’s range, ["red", "blue"] #### Plot.scale(*options*) -You can also create a standalone scale with Plot.**scale**(*options*). The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +You can also create a standalone scale with Plot.**scale**(*options*). The +*options* object must define at least one scale; see [Scale +options](#scale-options) +for how to define a scale. For example, here is a linear color scale with the +default domain of [0, 1] and default scheme *turbo*: ```js const color = Plot.scale({color: {type: "linear"}}); @@ -242,16 +424,29 @@ const color = Plot.scale({color: {type: "linear"}}); #### Scale objects -Both [*plot*.scale](#plotscalescalename) and [Plot.scale](#plotscaleoptions) return scale objects. These objects represent the actual (or “materialized”) scale options used by Plot, including the domain, range, interpolate function, *etc.* The scale’s label, if any, is also returned; however, note that other axis properties are not currently exposed. Point and band scales also expose their materialized bandwidth and step. +Both +[*plot*.scale](#plotscalescalename) +and +[Plot.scale](#plotscaleoptions) +return scale objects. These objects represent the actual (or “materialized”) +scale options used by Plot, including the domain, range, interpolate +function, *etc.* The scale’s label, if any, is also returned; however, note +that other axis properties are not currently exposed. Point and band scales +also expose their materialized bandwidth and step. -To reuse a scale across plots, pass the corresponding scale object into another plot specification: +To reuse a scale across plots, pass the corresponding scale object into +another plot specification: ```js const plot1 = Plot.plot(…); const plot2 = Plot.plot({…, color: plot1.scale("color")}); ``` -For convenience, scale objects expose a *scale*.**apply**(*input*) method which returns the scale’s output for the given *input* value. When applicable, scale objects also expose a *scale*.**invert**(*output*) method which returns the corresponding input value from the scale’s domain for the given *output* value. +For convenience, scale objects expose a *scale*.**apply**(*input*) method +which returns the scale’s output for the given *input* value. When +applicable, scale objects also expose a *scale*.**invert**(*output*) method +which returns the corresponding input value from the scale’s domain for the +given *output* value. @@ -617,13 +812,19 @@ The **style** legend option allows custom styles to override Plot’s defaults; #### Plot.legend(*options*) -Returns a standalone legend for the scale defined by the given *options* object. The *options* object must define at least one scale; see [Scale options](#scale-options) for how to define a scale. For example, here is a ramp legend of a linear color scale with the default domain of [0, 1] and default scheme *turbo*: +Returns a standalone legend for the scale defined by the given *options* +object. The *options* object must define at least one scale; see [Scale +options](#scale-options) +for how to define a scale. For example, here is a ramp legend of a linear +color scale with the default domain of [0, 1] and default scheme *turbo*: ```js Plot.legend({color: {type: "linear"}}) ``` -The *options* object may also include any additional legend options described in the previous section. For example, to make the above legend slightly wider: +The *options* object may also include any additional legend options described +in the previous section. For example, to make the above legend slightly +wider: ```js Plot.legend({ @@ -771,7 +972,11 @@ Plot.barY(alphabet, {x: "letter", y: "frequency"}).plot({width: 1024}) #### Plot.marks(...*marks*) -A convenience method for composing a mark from a series of other marks. Returns an array of marks that implements the *mark*.plot function. See the [box mark implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) for an example. +A convenience method for composing a mark from a series of other marks. +Returns an array of marks that implements the *mark*.plot function. See the +[box mark +implementation](https://github.com/observablehq/plot/blob/main/src/marks/box.js) +for an example. @@ -810,7 +1015,15 @@ The area mark supports [curve options](#curves) to control interpolation between Plot.area(aapl, {x1: "Date", y1: 0, y2: "Close"}) ``` -Returns a new area with the given *data* and *options*. Plot.area is rarely used directly; it is only needed when the baseline and topline have neither common *x* nor *y* values. [Plot.areaY](#plotareaydata-options) is used in the common horizontal orientation where the baseline and topline share *x* values, while [Plot.areaX](#plotareaxdata-options) is used in the vertical orientation where the baseline and topline share *y* values. +Returns a new area with the given *data* and *options*. Plot.area is rarely +used directly; it is only needed when the baseline and topline have neither +common *x* nor *y* values. +[Plot.areaY](#plotareaydata-options) +is used in the common horizontal orientation where the baseline and topline +share *x* values, while +[Plot.areaX](#plotareaxdata-options) +is used in the vertical orientation where the baseline and topline share *y* +values. @@ -820,15 +1033,31 @@ Returns a new area with the given *data* and *options*. Plot.area is rarely used Plot.areaX(aapl, {y: "Date", x: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *y* values, as in a time-series area chart where time goes up↑. If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for an area chart with a baseline at *x* = 0. If the **x** option is not specified, it defaults to the identity function. The **y** option specifies the **y1** channel; and the **y1** and **y2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is +used when the baseline and topline share *y* values, as in a time-series area +chart where time goes up↑. If neither the **x1** nor **x2** option is +specified, the **x** option may be specified as shorthand to apply an +implicit [stackX +transform](#plotstackxstack-options); +this is the typical configuration for an area chart with a baseline at *x* = +0. If the **x** option is not specified, it defaults to the identity +function. The **y** option specifies the **y1** channel; and the **y1** and +**y2** options are ignored. -If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY +transform](#bin) is +implicitly applied to the specified *options*. The reducer of the output *x* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.areaX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -838,15 +1067,31 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.areaY(aapl, {x: "Date", y: "Close"}) ``` -Returns a new area with the given *data* and *options*. This constructor is used when the baseline and topline share *x* values, as in a time-series area chart where time goes right→. If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for an area chart with a baseline at *y* = 0. If the **y** option is not specified, it defaults to the identity function. The **x** option specifies the **x1** channel; and the **x1** and **x2** options are ignored. +Returns a new area with the given *data* and *options*. This constructor is +used when the baseline and topline share *x* values, as in a time-series area +chart where time goes right→. If neither the **y1** nor **y2** option is +specified, the **y** option may be specified as shorthand to apply an +implicit [stackY +transform](#plotstackystack-options); +this is the typical configuration for an area chart with a baseline at *y* = +0. If the **y** option is not specified, it defaults to the identity +function. The **x** option specifies the **x1** channel; and the **x1** and +**x2** options are ignored. -If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX +transform](#bin) is +implicitly applied to the specified *options*. The reducer of the output *y* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.areaY(observations, {x: "date", y: "temperature", interval: d3.utcDay) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -904,20 +1149,35 @@ For the required channels, see [Plot.barX](#plotbarxdata-options) and [Plot.barY Plot.barX(alphabet, {y: "letter", x: "frequency"}) ``` -Returns a new horizontal bar↔︎ with the given *data* and *options*. The following channels are required: +Returns a new horizontal bar↔︎ with the given *data* and *options*. The +following channels are required: * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a horizontal bar chart with bars aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **x2** as the identity function and **y** as the index of data; this allows an array of numbers to be passed to Plot.barX to make a quick sequential bar chart. - -If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. - -In addition to the [standard bar channels](#bar), the following optional channels are supported: +If neither the **x1** nor **x2** option is specified, the **x** option may be +specified as shorthand to apply an implicit [stackX +transform](#plotstackxstack-options); +this is the typical configuration for a horizontal bar chart with bars +aligned at *x* = 0. If the **x** option is not specified, it defaults to the +identity function. If *options* is undefined, then it defaults to **x2** as +the identity function and **y** as the index of data; this allows an array of +numbers to be passed to Plot.barX to make a quick sequential bar chart. + +If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be +derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce +*x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. +If the interval is specified as a number *n*, *x1* and *x2* are taken as the +two consecutive multiples of *n* that bracket *x*. + +In addition to the [standard bar +channels](#bar), the +following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* -If the **y** channel is not specified, the bar will span the full vertical extent of the plot (or facet). +If the **y** channel is not specified, the bar will span the full vertical +extent of the plot (or facet). @@ -927,20 +1187,36 @@ If the **y** channel is not specified, the bar will span the full vertical exten Plot.barY(alphabet, {x: "letter", y: "frequency"}) ``` -Returns a new vertical bar↕︎ with the given *data* and *options*. The following channels are required: +Returns a new vertical bar↕︎ with the given *data* and *options*. The +following channels are required: * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a vertical bar chart with bars aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. If *options* is undefined, then it defaults to **y2** as the identity function and **x** as the index of data; this allows an array of numbers to be passed to Plot.barY to make a quick sequential bar chart. +If neither the **y1** nor **y2** option is specified, the **y** option may be +specified as shorthand to apply an implicit [stackY +transform](#plotstackystack-options); +this is the typical configuration for a vertical bar chart with bars aligned +at *y* = 0. If the **y** option is not specified, it defaults to the identity +function. If *options* is undefined, then it defaults to **y2** as the +identity function and **x** as the index of data; this allows an array of +numbers to be passed to Plot.barY to make a quick sequential bar chart. -If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be +derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce +*y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. +If the interval is specified as a number *n*, *y1* and *y2* are taken as the +two consecutive multiples of *n* that bracket *y*. -In addition to the [standard bar channels](#bar), the following optional channels are supported: +In addition to the [standard bar +channels](#bar), the +following optional channels are supported: -* **x** - the horizontal position; bound to the *x* scale, which must be *band* +* **x** - the horizontal position; bound to the *x* scale, which must be + *band* -If the **x** channel is not specified, the bar will span the full horizontal extent of the plot (or facet). +If the **x** channel is not specified, the bar will span the full horizontal +extent of the plot (or facet). @@ -973,7 +1249,10 @@ The given *options* are passed through to these underlying marks, with the excep Plot.boxX(simpsons.map(d => d.imdb_rating)) ``` -Returns a horizontal boxplot mark. If the **x** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **y** option is not specified, it defaults to null; if the **y** option is specified, it should represent an ordinal (discrete) value. +Returns a horizontal boxplot mark. If the **x** option is not specified, it +defaults to the identity function, as when *data* is an array of numbers. If +the **y** option is not specified, it defaults to null; if the **y** option +is specified, it should represent an ordinal (discrete) value. @@ -983,7 +1262,10 @@ Returns a horizontal boxplot mark. If the **x** option is not specified, it defa Plot.boxY(simpsons.map(d => d.imdb_rating)) ``` -Returns a vertical boxplot mark. If the **y** option is not specified, it defaults to the identity function, as when *data* is an array of numbers. If the **x** option is not specified, it defaults to null; if the **x** option is specified, it should represent an ordinal (discrete) value. +Returns a vertical boxplot mark. If the **y** option is not specified, it +defaults to the identity function, as when *data* is an array of numbers. If +the **x** option is not specified, it defaults to null; if the **x** option +is specified, it should represent an ordinal (discrete) value. @@ -1010,7 +1292,10 @@ The **stroke** defaults to none. The **fill** defaults to currentColor if the st Plot.cell(simpsons, {x: "number_in_season", y: "season", fill: "imdb_rating"}) ``` -Returns a new cell with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new cell with the given *data* and *options*. If neither the **x** +nor **y** options are specified, *data* is assumed to be an array of pairs +[[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, +*x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1020,7 +1305,12 @@ Returns a new cell with the given *data* and *options*. If neither the **x** nor Plot.cellX(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](#plotcelldata-options), except that if the **x** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.cell](#plotcelldata-options), +except that if the **x** option is not specified, it defaults to [0, 1, 2, +…], and if the **fill** option is not specified and **stroke** is not a +channel, the fill defaults to the identity function and assumes that *data* = +[*x₀*, *x₁*, *x₂*, …]. @@ -1030,7 +1320,12 @@ Equivalent to [Plot.cell](#plotcelldata-options), except that if the **x** optio Plot.cellY(simpsons.map(d => d.imdb_rating)) ``` -Equivalent to [Plot.cell](#plotcelldata-options), except that if the **y** option is not specified, it defaults to [0, 1, 2, …], and if the **fill** option is not specified and **stroke** is not a channel, the fill defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.cell](#plotcelldata-options), +except that if the **y** option is not specified, it defaults to [0, 1, 2, +…], and if the **fill** option is not specified and **stroke** is not a +channel, the fill defaults to the identity function and assumes that *data* = +[*y₀*, *y₁*, *y₂*, …]. @@ -1044,41 +1339,68 @@ Equivalent to [Plot.cell](#plotcelldata-options), except that if the **y** optio #### Plot.delaunayLink(*data*, *options*) -Draws links for each edge of the Delaunay triangulation of the points given by the **x** and **y** channels. Supports the same options as the [link mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived automatically from **x** and **y**. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the link inherits the corresponding channel value from one of its two endpoints arbitrarily. +Draws links for each edge of the Delaunay triangulation of the points given +by the **x** and **y** channels. Supports the same options as the [link +mark](#link), except that **x1**, **y1**, **x2**, and **y2** are derived +automatically from **x** and **y**. When an aesthetic channel is specified +(such as **stroke** or **strokeWidth**), the link inherits the corresponding +channel value from one of its two endpoints arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Delaunay triangulations are constructed for each group. #### Plot.delaunayMesh(*data*, *options*) -Draws a mesh of the Delaunay triangulation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a mesh of the Delaunay triangulation of the points given by the **x** +and **y** channels. The **stroke** option defaults to _currentColor_, and the +**strokeOpacity** defaults to 0.2. The **fill** option is not supported. When +an aesthetic channel is specified (such as **stroke** or **strokeWidth**), +the mesh inherits the corresponding channel value from one of its constituent +points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Delaunay triangulations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Delaunay triangulations are constructed for each group. #### Plot.hull(*data*, *options*) -Draws a convex hull around the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_ and the **fill** option defaults to _none_. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the hull inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a convex hull around the points given by the **x** and **y** channels. +The **stroke** option defaults to _currentColor_ and the **fill** option +defaults to _none_. When an aesthetic channel is specified (such as +**stroke** or **strokeWidth**), the hull inherits the corresponding channel +value from one of its constituent points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate convex hulls are constructed for each group. If the **z** channel is not specified, it defaults to either the **fill** channel, if any, or the **stroke** channel, if any. +If a **z** channel is specified, the input points are grouped by *z*, and +separate convex hulls are constructed for each group. If the **z** channel is +not specified, it defaults to either the **fill** channel, if any, or the +**stroke** channel, if any. #### Plot.voronoi(*data*, *options*) -Draws polygons for each cell of the Voronoi tesselation of the points given by the **x** and **y** channels. +Draws polygons for each cell of the Voronoi tesselation of the points given +by the **x** and **y** channels. -If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Voronoi tesselations are constructed for each group. #### Plot.voronoiMesh(*data*, *options*) -Draws a mesh for the cell boundaries of the Voronoi tesselation of the points given by the **x** and **y** channels. The **stroke** option defaults to _currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** option is not supported. When an aesthetic channel is specified (such as **stroke** or **strokeWidth**), the mesh inherits the corresponding channel value from one of its constituent points arbitrarily. +Draws a mesh for the cell boundaries of the Voronoi tesselation of the points +given by the **x** and **y** channels. The **stroke** option defaults to +_currentColor_, and the **strokeOpacity** defaults to 0.2. The **fill** +option is not supported. When an aesthetic channel is specified (such as +**stroke** or **strokeWidth**), the mesh inherits the corresponding channel +value from one of its constituent points arbitrarily. -If a **z** channel is specified, the input points are grouped by *z*, and separate Voronoi tesselations are constructed for each group. +If a **z** channel is specified, the input points are grouped by *z*, and +separate Voronoi tesselations are constructed for each group. @@ -1092,11 +1414,24 @@ If a **z** channel is specified, the input points are grouped by *z*, and separa #### Plot.density(*data*, *options*) -Draws contours representing the estimated density of the two-dimensional points given by the **x** and **y** channels, and possibly weighted by the **weight** channel. If either of the **x** or **y** channels are not specified, the corresponding position is controlled by the **frameAnchor** option. - -The **thresholds** option, which defaults to 20, specifies one more than the number of contours that will be computed at uniformly-spaced intervals between 0 (exclusive) and the maximum density (exclusive). The **thresholds** option may also be specified as an array or iterable of explicit density values. The **bandwidth** option, which defaults to 20, specifies the standard deviation of the Gaussian kernel used for estimation in pixels. - -If a **z**, **stroke** or **fill** channel is specified, the input points are grouped by series, and separate sets of contours are generated for each series. If the **stroke** or **fill** is specified as *density*, a color channel is constructed with values representing the density threshold value of each contour. +Draws contours representing the estimated density of the two-dimensional +points given by the **x** and **y** channels, and possibly weighted by the +**weight** channel. If either of the **x** or **y** channels are not +specified, the corresponding position is controlled by the **frameAnchor** +option. + +The **thresholds** option, which defaults to 20, specifies one more than the +number of contours that will be computed at uniformly-spaced intervals +between 0 (exclusive) and the maximum density (exclusive). The **thresholds** +option may also be specified as an array or iterable of explicit density +values. The **bandwidth** option, which defaults to 20, specifies the +standard deviation of the Gaussian kernel used for estimation in pixels. + +If a **z**, **stroke** or **fill** channel is specified, the input points are +grouped by series, and separate sets of contours are generated for each +series. If the **stroke** or **fill** is specified as *density*, a color +channel is constructed with values representing the density threshold value +of each contour. @@ -1139,7 +1474,10 @@ Dots are drawn in input order, with the last data drawn on top. If sorting is ne Plot.dot(sales, {x: "units", y: "fruit"}) ``` -Returns a new dot with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new dot with the given *data* and *options*. If neither the **x** +nor **y** nor **frameAnchor** options are specified, *data* is assumed to be +an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that +**x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1149,9 +1487,15 @@ Returns a new dot with the given *data* and *options*. If neither the **x** nor Plot.dotX(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](#plotdotdata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.dot](#plotdotdata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. -If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y** is transformed to +(*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If +the interval is specified as a number *n*, *y* will be the midpoint of two +consecutive multiples of *n* that bracket *y*. @@ -1161,21 +1505,31 @@ If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*in Plot.dotY(cars.map(d => d["economy (mpg)"])) ``` -Equivalent to [Plot.dot](#plotdotdata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.dot](#plotdotdata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. -If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x** is transformed to +(*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If +the interval is specified as a number *n*, *x* will be the midpoint of two +consecutive multiples of *n* that bracket *x*. #### Plot.circle(*data*, *options*) -Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *circle*. +Equivalent to +[Plot.dot](#plotdotdata-options) +except that the **symbol** option is set to *circle*. #### Plot.hexagon(*data*, *options*) -Equivalent to [Plot.dot](#plotdotdata-options) except that the **symbol** option is set to *hexagon*. +Equivalent to +[Plot.dot](#plotdotdata-options) +except that the **symbol** option is set to *hexagon*. @@ -1187,7 +1541,9 @@ The hexgrid mark can be used to support marks using the [hexbin](#hexbin) layout #### Plot.hexgrid(*options*) -The **binWidth** option specifies the distance between the centers of neighboring hexagons, in pixels (defaults to 20). The **clip** option defaults to true, clipping the mark to the frame’s dimensions. +The **binWidth** option specifies the distance between the centers of +neighboring hexagons, in pixels (defaults to 20). The **clip** option +defaults to true, clipping the mark to the frame’s dimensions. @@ -1226,7 +1582,10 @@ Images are drawn in input order, with the last data drawn on top. If sorting is Plot.image(presidents, {x: "inauguration", y: "favorability", src: "portrait"}) ``` -Returns a new image with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new image with the given *data* and *options*. If neither the **x** +nor **y** nor **frameAnchor** options are specified, *data* is assumed to be +an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that +**x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1254,7 +1613,8 @@ Multiple regressions can be defined by specifying the *z*, *fill*, or *stroke* c Plot.linearRegressionX(mtcars, {y: "wt", x: "hp"}) ``` -Returns a linear regression mark where *x* is the dependent variable and *y* is the independent variable. +Returns a linear regression mark where *x* is the dependent variable and *y* +is the independent variable. @@ -1264,7 +1624,8 @@ Returns a linear regression mark where *x* is the dependent variable and *y* is Plot.linearRegressionY(mtcars, {x: "wt", y: "hp"}) ``` -Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. +Returns a linear regression mark where *y* is the dependent variable and *x* +is the independent variable. @@ -1299,7 +1660,10 @@ The line mark supports [curve options](#curves) to control interpolation between Plot.line(aapl, {x: "Date", y: "Close"}) ``` -Returns a new line with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new line with the given *data* and *options*. If neither the **x** +nor **y** options are specified, *data* is assumed to be an array of pairs +[[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, +*x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. @@ -1309,15 +1673,26 @@ Returns a new line with the given *data* and *options*. If neither the **x** nor Plot.lineX(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](#plotlinedata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option is not specified, it defaults to [0, 1, 2, …]. +Similar to +[Plot.line](#plotlinedata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If the **y** option +is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binY transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *x* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binY +transform](#bin) is +implicitly applied to the specified *options*. The reducer of the output *x* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.lineX(observations, {y: "date", x: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -1327,15 +1702,26 @@ The **interval** option is recommended to “regularize” sampled data; for exa Plot.lineY(aapl.map(d => d.Close)) ``` -Similar to [Plot.line](#plotlinedata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is not specified, it defaults to [0, 1, 2, …]. +Similar to +[Plot.line](#plotlinedata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option +is not specified, it defaults to [0, 1, 2, …]. -If the **interval** option is specified, the [binX transform](#bin) is implicitly applied to the specified *options*. The reducer of the output *y* channel may be specified via the **reduce** option, which defaults to *first*. To default to zero instead of showing gaps in data, as when the observed value represents a quantity, use the *sum* reducer. +If the **interval** option is specified, the [binX +transform](#bin) is +implicitly applied to the specified *options*. The reducer of the output *y* +channel may be specified via the **reduce** option, which defaults to +*first*. To default to zero instead of showing gaps in data, as when the +observed value represents a quantity, use the *sum* reducer. ```js Plot.lineY(observations, {x: "date", y: "temperature", interval: d3.utcDay}) ``` -The **interval** option is recommended to “regularize” sampled data; for example, if your data represents timestamped temperature measurements and you expect one sample per day, use d3.utcDay as the interval. +The **interval** option is recommended to “regularize” sampled data; for +example, if your data represents timestamped temperature measurements and you +expect one sample per day, use d3.utcDay as the interval. @@ -1407,7 +1793,14 @@ Returns a new rect with the given *data* and *options*. Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **x1** nor **x2** option is specified, the **x** option may be specified as shorthand to apply an implicit [stackX transform](#plotstackxstack-options); this is the typical configuration for a histogram with rects aligned at *x* = 0. If the **x** option is not specified, it defaults to the identity function. +Equivalent to +[Plot.rect](#plotrectdata-options), +except that if neither the **x1** nor **x2** option is specified, the **x** +option may be specified as shorthand to apply an implicit [stackX +transform](#plotstackxstack-options); +this is the typical configuration for a histogram with rects aligned at *x* = +0. If the **x** option is not specified, it defaults to the identity +function. @@ -1417,7 +1810,14 @@ Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **x Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Equivalent to [Plot.rect](#plotrectdata-options), except that if neither the **y1** nor **y2** option is specified, the **y** option may be specified as shorthand to apply an implicit [stackY transform](#plotstackystack-options); this is the typical configuration for a histogram with rects aligned at *y* = 0. If the **y** option is not specified, it defaults to the identity function. +Equivalent to +[Plot.rect](#plotrectdata-options), +except that if neither the **y1** nor **y2** option is specified, the **y** +option may be specified as shorthand to apply an implicit [stackY +transform](#plotstackystack-options); +this is the typical configuration for a histogram with rects aligned at *y* = +0. If the **y** option is not specified, it defaults to the identity +function. @@ -1440,15 +1840,28 @@ Plot.ruleX([0]) // as annotation Plot.ruleX(alphabet, {x: "letter", y: "frequency"}) // like barY ``` -Returns a new rule↕︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: +Returns a new rule↕︎ with the given *data* and *options*. In addition to the +[standard mark +options](#marks), the +following channels are optional: * **x** - the horizontal position; bound to the *x* scale * **y1** - the starting vertical position; bound to the *y* scale * **y2** - the ending vertical position; bound to the *y* scale -If the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is specified, it is shorthand for the **y2** option with **y1** equal to zero; this is the typical configuration for a vertical lollipop chart with rules aligned at *y* = 0. If the **y1** channel is not specified, the rule will start at the top of the plot (or facet). If the **y2** channel is not specified, the rule will end at the bottom of the plot (or facet). +If the **x** option is not specified, it defaults to the identity function +and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. If a **y** option is +specified, it is shorthand for the **y2** option with **y1** equal to zero; +this is the typical configuration for a vertical lollipop chart with rules +aligned at *y* = 0. If the **y1** channel is not specified, the rule will +start at the top of the plot (or facet). If the **y2** channel is not +specified, the rule will end at the bottom of the plot (or facet). -If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce *y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. If the interval is specified as a number *n*, *y1* and *y2* are taken as the two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be +derived from **y**: *interval*.floor(*y*) is invoked for each *y* to produce +*y1*, and *interval*.offset(*y1*) is invoked for each *y1* to produce *y2*. +If the interval is specified as a number *n*, *y1* and *y2* are taken as the +two consecutive multiples of *n* that bracket *y*. @@ -1457,19 +1870,33 @@ If an **interval** is specified, such as d3.utcDay, **y1** and **y2** can be der ```js Plot.ruleY([0]) // as annotation ``` + ```js Plot.ruleY(alphabet, {y: "letter", x: "frequency"}) // like barX ``` -Returns a new rule↔︎ with the given *data* and *options*. In addition to the [standard mark options](#marks), the following channels are optional: +Returns a new rule↔︎ with the given *data* and *options*. In addition to the +[standard mark +options](#marks), the +following channels are optional: * **y** - the vertical position; bound to the *y* scale * **x1** - the starting horizontal position; bound to the *x* scale * **x2** - the ending horizontal position; bound to the *x* scale -If the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is specified, it is shorthand for the **x2** option with **x1** equal to zero; this is the typical configuration for a horizontal lollipop chart with rules aligned at *x* = 0. If the **x1** channel is not specified, the rule will start at the left edge of the plot (or facet). If the **x2** channel is not specified, the rule will end at the right edge of the plot (or facet). +If the **y** option is not specified, it defaults to the identity function +and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. If the **x** option is +specified, it is shorthand for the **x2** option with **x1** equal to zero; +this is the typical configuration for a horizontal lollipop chart with rules +aligned at *x* = 0. If the **x1** channel is not specified, the rule will +start at the left edge of the plot (or facet). If the **x2** channel is not +specified, the rule will end at the right edge of the plot (or facet). -If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce *x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. If the interval is specified as a number *n*, *x1* and *x2* are taken as the two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x1** and **x2** can be +derived from **x**: *interval*.floor(*x*) is invoked for each *x* to produce +*x1*, and *interval*.offset(*x1*) is invoked for each *x1* to produce *x2*. +If the interval is specified as a number *n*, *x1* and *x2* are taken as the +two consecutive multiples of *n* that bracket *x*. @@ -1521,23 +1948,38 @@ The **paintOrder** option defaults to “stroke” and the **strokeWidth** optio #### Plot.text(*data*, *options*) -Returns a new text mark with the given *data* and *options*. If neither the **x** nor **y** nor **frameAnchor** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new text mark with the given *data* and *options*. If neither the +**x** nor **y** nor **frameAnchor** options are specified, *data* is assumed +to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such +that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. #### Plot.textX(*data*, *options*) -Equivalent to [Plot.text](#plottextdata-options), except **x** defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.text](#plottextdata-options), +except **x** defaults to the identity function and assumes that *data* = +[*x₀*, *x₁*, *x₂*, …]. -If an **interval** is specified, such as d3.utcDay, **y** is transformed to (*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If the interval is specified as a number *n*, *y* will be the midpoint of two consecutive multiples of *n* that bracket *y*. +If an **interval** is specified, such as d3.utcDay, **y** is transformed to +(*interval*.floor(*y*) + *interval*.offset(*interval*.floor(*y*))) / 2. If +the interval is specified as a number *n*, *y* will be the midpoint of two +consecutive multiples of *n* that bracket *y*. #### Plot.textY(*data*, *options*) -Equivalent to [Plot.text](#plottextdata-options), except **y** defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.text](#plottextdata-options), +except **y** defaults to the identity function and assumes that *data* = +[*y₀*, *y₁*, *y₂*, …]. -If an **interval** is specified, such as d3.utcDay, **x** is transformed to (*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If the interval is specified as a number *n*, *x* will be the midpoint of two consecutive multiples of *n* that bracket *x*. +If an **interval** is specified, such as d3.utcDay, **x** is transformed to +(*interval*.floor(*x*) + *interval*.offset(*interval*.floor(*x*))) / 2. If +the interval is specified as a number *n*, *x* will be the midpoint of two +consecutive multiples of *n* that bracket *x*. @@ -1557,7 +1999,8 @@ For the required channels, see [Plot.tickX](#plottickxdata-options) and [Plot.ti Plot.tickX(stateage, {x: "population", y: "age"}) ``` -Returns a new tick↕︎ with the given *data* and *options*. The following channels are required: +Returns a new tick↕︎ with the given *data* and *options*. The following +channels are required: * **x** - the horizontal position; bound to the *x* scale @@ -1565,7 +2008,8 @@ The following optional channels are supported: * **y** - the vertical position; bound to the *y* scale, which must be *band* -If the **y** channel is not specified, the tick will span the full vertical extent of the plot (or facet). +If the **y** channel is not specified, the tick will span the full vertical +extent of the plot (or facet). @@ -1575,15 +2019,18 @@ If the **y** channel is not specified, the tick will span the full vertical exte Plot.tickY(stateage, {y: "population", x: "age"}) ``` -Returns a new tick↔︎ with the given *data* and *options*. The following channels are required: +Returns a new tick↔︎ with the given *data* and *options*. The following +channels are required: * **y** - the vertical position; bound to the *y* scale The following optional channels are supported: -* **x** - the horizontal position; bound to the *x* scale, which must be *band* +* **x** - the horizontal position; bound to the *x* scale, which must be + *band* -If the **x** channel is not specified, the tick will span the full vertical extent of the plot (or facet). +If the **x** channel is not specified, the tick will span the full vertical +extent of the plot (or facet). @@ -1625,19 +2072,28 @@ Vectors are drawn in input order, with the last data drawn on top. If sorting is Plot.vector(wind, {x: "longitude", y: "latitude", length: "speed", rotate: "direction"}) ``` -Returns a new vector with the given *data* and *options*. If neither the **x** nor **y** options are specified, *data* is assumed to be an array of pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, *x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. +Returns a new vector with the given *data* and *options*. If neither the +**x** nor **y** options are specified, *data* is assumed to be an array of +pairs [[*x₀*, *y₀*], [*x₁*, *y₁*], [*x₂*, *y₂*], …] such that **x** = [*x₀*, +*x₁*, *x₂*, …] and **y** = [*y₀*, *y₁*, *y₂*, …]. #### Plot.vectorX(*data*, *options*) -Equivalent to [Plot.vector](#plotvectordata-options) except that if the **x** option is not specified, it defaults to the identity function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. +Equivalent to +[Plot.vector](#plotvectordata-options) +except that if the **x** option is not specified, it defaults to the identity +function and assumes that *data* = [*x₀*, *x₁*, *x₂*, …]. #### Plot.vectorY(*data*, *options*) -Equivalent to [Plot.vector](#plotvectordata-options) except that if the **y** option is not specified, it defaults to the identity function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. +Equivalent to +[Plot.vector](#plotvectordata-options) +except that if the **y** option is not specified, it defaults to the identity +function and assumes that *data* = [*y₀*, *y₁*, *y₂*, …]. @@ -1717,7 +2173,11 @@ The *filter*, *sort* and *reverse* transforms are also available as functions, a Plot.sort("body_mass_g", options) // show data in ascending body mass order ``` -Sorts the data by the specified *order*, which can be an accessor function, a comparator function, or a channel value definition such as a field name. See also [index sorting](#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. +Sorts the data by the specified *order*, which can be an accessor function, a +comparator function, or a channel value definition such as a field name. See +also [index +sorting](#index-sorting), +which allows marks to be sorted by a named channel, such as *r* for radius. @@ -1727,7 +2187,9 @@ Sorts the data by the specified *order*, which can be an accessor function, a co Plot.shuffle(options) // show data in random order ``` -Shuffles the data randomly. If a *seed* option is specified, a linear congruential generator with the given seed is used to generate random numbers deterministically; otherwise, Math.random is used. +Shuffles the data randomly. If a *seed* option is specified, a linear +congruential generator with the given seed is used to generate random numbers +deterministically; otherwise, Math.random is used. @@ -1747,7 +2209,9 @@ Reverses the order of the data. Plot.filter(d => d.body_mass_g > 3000, options) // show data whose body mass is greater than 3kg ``` -Filters the data given the specified *test*. The test can be given as an accessor function (which receives the datum and index), or as a channel value definition such as a field name; truthy values are retained. +Filters the data given the specified *test*. The test can be given as an +accessor function (which receives the datum and index), or as a channel value +definition such as a field name; truthy values are retained. @@ -1879,7 +2343,8 @@ Lastly, the bin transform changes the default [mark insets](#marks): rather than Plot.rect(athletes, Plot.bin({fillOpacity: "count"}, {x: "weight", y: "height"})) ``` -Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke*, if any. +Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or +*stroke*, if any. @@ -1890,7 +2355,8 @@ Bins on *x* and *y*. Also groups on the first channel of *z*, *fill*, or *stroke Plot.rectY(athletes, Plot.binX({y: "count"}, {x: "weight"})) ``` -Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. +Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or +*stroke*, if any. @@ -1901,7 +2367,8 @@ Bins on *x*. Also groups on *y* and the first channel of *z*, *fill*, or *stroke Plot.rectX(athletes, Plot.binY({x: "count"}, {y: "weight"})) ``` -Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. +Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or +*stroke*, if any. @@ -1986,7 +2453,8 @@ If any of **z**, **fill**, or **stroke** is a channel, the first of these channe Plot.group({fill: "count"}, {x: "island", y: "species"}) ``` -Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if any. +Groups on *x*, *y*, and the first channel of *z*, *fill*, or *stroke*, if +any. @@ -2019,7 +2487,9 @@ Groups on *y* and the first channel of *z*, *fill*, or *stroke*, if any. Plot.groupZ({x: "proportion"}, {fill: "species"}) ``` -Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of *z*, *fill*, or *stroke* are channels, then all data (within each facet) is placed into a single group. +Groups on the first channel of *z*, *fill*, or *stroke*, if any. If none of +*z*, *fill*, or *stroke* are channels, then all data (within each facet) is +placed into a single group. @@ -2094,7 +2564,10 @@ By default, **anchor** is *middle* and **reduce** is *mean*. Plot.map({y: "cumsum"}, {y: d3.randomNormal()}) ``` -Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for each channel declared in the specified *outputs* object, applies the corresponding map method. Each channel in *outputs* must have a corresponding input channel in *options*. +Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for +each channel declared in the specified *outputs* object, applies the +corresponding map method. Each channel in *outputs* must have a corresponding +input channel in *options*. @@ -2105,7 +2578,8 @@ Groups on the first channel of *z*, *fill*, or *stroke*, if any, and then for ea Plot.mapX("cumsum", {x: d3.randomNormal()}) ``` -Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores any of **x**, **x1**, and **x2** not present in *options*. +Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but +ignores any of **x**, **x1**, and **x2** not present in *options*. @@ -2116,7 +2590,8 @@ Equivalent to Plot.map({x: *map*, x1: *map*, x2: *map*}, *options*), but ignores Plot.mapY("cumsum", {y: d3.randomNormal()}) ``` -Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores any of **y**, **y1**, and **y2** not present in *options*. +Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but +ignores any of **y**, **y1**, and **y2** not present in *options*. @@ -2127,7 +2602,8 @@ Equivalent to Plot.map({y: *map*, y1: *map*, y2: *map*}, *options*), but ignores Plot.map({y: Plot.normalize("first")}, {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Returns a normalize map method for the given *basis*, suitable for use with Plot.map. +Returns a normalize map method for the given *basis*, suitable for use with +Plot.map. @@ -2137,7 +2613,9 @@ Returns a normalize map method for the given *basis*, suitable for use with Plot Plot.normalizeX("first", {y: "Date", x: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapX](#plotmapxmap-options), but applies the normalize map method with the given *basis*. +Like +[Plot.mapX](#plotmapxmap-options), +but applies the normalize map method with the given *basis*. @@ -2147,7 +2625,9 @@ Like [Plot.mapX](#plotmapxmap-options), but applies the normalize map method wit Plot.normalizeY("first", {x: "Date", y: "Close", stroke: "Symbol"}) ``` -Like [Plot.mapY](#plotmapymap-options), but applies the normalize map method with the given *basis*. +Like +[Plot.mapY](#plotmapymap-options), +but applies the normalize map method with the given *basis*. @@ -2187,9 +2667,17 @@ The select transform derives a filtered mark index; it does not affect the mark #### Plot.select(*selector*, *options*) -Selects the points of each series selected by the *selector*, which can be specified either as a function which receives as input the index of the series, the shorthand “first” or “last”, or as a {*key*: *value*} object with exactly one *key* being the name of a channel and the *value* being a function which receives as input the index of the series and the channel values. The *value* may alternatively be specified as the shorthand “min” and “max” which respectively select the minimum and maximum points for the specified channel. +Selects the points of each series selected by the *selector*, which can be +specified either as a function which receives as input the index of the +series, the shorthand “first” or “last”, or as a {*key*: *value*} object with +exactly one *key* being the name of a channel and the *value* being a +function which receives as input the index of the series and the channel +values. The *value* may alternatively be specified as the shorthand “min” and +“max” which respectively select the minimum and maximum points for the +specified channel. -For example, to select the point within each series that is the closest to the median of the *y* channel: +For example, to select the point within each series that is the closest to +the median of the *y* channel: ```js Plot.select({ @@ -2307,7 +2795,13 @@ If two arguments are passed to the stack transform functions below, the stack-sp Plot.stackY({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Creates new channels **y1** and **y2**, obtained by stacking the original **y** channel for data points that share a common **x** (and possibly **z**) value. A new **y** channel is also returned, which lazily computes the middle value of **y1** and **y2**. The input **y** channel defaults to a constant 1, resulting in a count of the data points. The stack options (*offset*, *order*, and *reverse*) may be specified as part of the *options* object, if the only argument, or as a separate *stack* options argument. +Creates new channels **y1** and **y2**, obtained by stacking the original +**y** channel for data points that share a common **x** (and possibly **z**) +value. A new **y** channel is also returned, which lazily computes the middle +value of **y1** and **y2**. The input **y** channel defaults to a constant 1, +resulting in a count of the data points. The stack options (*offset*, +*order*, and *reverse*) may be specified as part of the *options* object, if +the only argument, or as a separate *stack* options argument. @@ -2318,7 +2812,10 @@ Creates new channels **y1** and **y2**, obtained by stacking the original **y** Plot.stackY1({x: "year", y: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y1** channel is returned as the **y** channel. This can be used, for example, to draw a line at the bottom of each stacked area. +Equivalent to +[Plot.stackY](#plotstackystack-options), +except that the **y1** channel is returned as the **y** channel. This can be +used, for example, to draw a line at the bottom of each stacked area. @@ -2340,7 +2840,8 @@ Equivalent to [Plot.stackY](#plotstackystack-options), except that the **y2** ch Plot.stackX({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -See Plot.stackY, but with *x* as the input value channel, *y* as the stack index, *x1*, *x2* and *x* as the output channels. +See Plot.stackY, but with *x* as the input value channel, *y* as the stack +index, *x1*, *x2* and *x* as the output channels. @@ -2351,7 +2852,10 @@ See Plot.stackY, but with *x* as the input value channel, *y* as the stack index Plot.stackX1({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** channel is returned as the **x** channel. This can be used, for example, to draw a line at the left edge of each stacked area. +Equivalent to +[Plot.stackX](#plotstackxstack-options), +except that the **x1** channel is returned as the **x** channel. This can be +used, for example, to draw a line at the left edge of each stacked area. @@ -2362,7 +2866,10 @@ Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x1** ch Plot.stackX2({y: "year", x: "revenue", z: "format", fill: "group"}) ``` -Equivalent to [Plot.stackX](#plotstackxstack-options), except that the **x2** channel is returned as the **x** channel. This can be used, for example, to draw a line at the right edge of each stacked area. +Equivalent to +[Plot.stackX](#plotstackxstack-options), +except that the **x2** channel is returned as the **x** channel. This can be +used, for example, to draw a line at the right edge of each stacked area. @@ -2425,9 +2932,18 @@ The default **treeLayout** implements the Reingold–Tilford “tidy” algorith #### Plot.treeNode(*options*) -Based on the tree options described above, populates the **x** and **y** channels with the positions for each node. The following defaults are also applied: the default **frameAnchor** inherits the **treeAnchor**. This transform is intended to be used with [dot](#dot), [text](#text), and other point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). +Based on the tree options described above, populates the **x** and **y** +channels with the positions for each node. The following defaults are also +applied: the default **frameAnchor** inherits the **treeAnchor**. This +transform is intended to be used with +[dot](#dot), +[text](#text), and +other point-based marks. This transform is rarely used directly; see the +[Plot.tree compound +mark](#plottreedata-options). -The treeNode transform will derive output columns for any *options* that have one of the following named node values: +The treeNode transform will derive output columns for any *options* that have +one of the following named node values: * *node:name* - the node’s name (the last part of its path) * *node:path* - the node’s full, normalized, slash-separated path @@ -2435,15 +2951,27 @@ The treeNode transform will derive output columns for any *options* that have on * *node:depth* - the distance from the node to the root * *node:height* - the distance from the node to its deepest descendant -In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each node in the tree. +In addition, if any option value is specified as an object with a **node** +method, a derived output column will be generated by invoking the **node** +method for each node in the tree. #### Plot.treeLink(*options*) -Based on the tree options described above, populates the **x1**, **y1**, **x2**, and **y2** channels. The following defaults are also applied: the default **curve** is *bump-x*, the default **stroke** is #555, the default **strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This transform is intended to be used with [link](#link), [arrow](#arrow), and other two-point-based marks. This transform is rarely used directly; see the [Plot.tree compound mark](#plottreedata-options). +Based on the tree options described above, populates the **x1**, **y1**, +**x2**, and **y2** channels. The following defaults are also applied: the +default **curve** is *bump-x*, the default **stroke** is #555, the default +**strokeWidth** is 1.5, and the default **strokeOpacity** is 0.5. This +transform is intended to be used with +[link](#link), +[arrow](#arrow), and +other two-point-based marks. This transform is rarely used directly; see the +[Plot.tree compound +mark](#plottreedata-options). -The treeLink transform will derive output columns for any *options* that have one of the following named link values: +The treeLink transform will derive output columns for any *options* that have +one of the following named link values: * *node:name* - the child node’s name (the last part of its path) * *node:path* - the child node’s full, normalized, slash-separated path @@ -2453,15 +2981,31 @@ The treeLink transform will derive output columns for any *options* that have on * *parent:name* - the parent node’s name (the last part of its path) * *parent:path* - the parent node’s full, normalized, slash-separated path * *parent:depth* - the distance from the parent node to the root -* *parent:height* - the distance from the parent node to its deepest descendant +* *parent:height* - the distance from the parent node to its deepest + descendant -In addition, if any option value is specified as an object with a **node** method, a derived output column will be generated by invoking the **node** method for each child node in the tree; likewise if any option value is specified as an object with a **link** method, a derived output column will be generated by invoking the **link** method for each link in the tree, being passed two node arguments, the child and the parent. +In addition, if any option value is specified as an object with a **node** +method, a derived output column will be generated by invoking the **node** +method for each child node in the tree; likewise if any option value is +specified as an object with a **link** method, a derived output column will +be generated by invoking the **link** method for each link in the tree, being +passed two node arguments, the child and the parent. #### Plot.tree(*data*, *options*) -A convenience compound mark for rendering a tree diagram, including a [link](#link) to render links from parent to child, an optional [dot](#dot) for nodes, and a [text](#text) for node labels. The link mark uses the [treeLink transform](#plottreelinkoptions), while the dot and text marks use the [treeNode transform](#plottreenodeoptions). The following options are supported: +A convenience compound mark for rendering a tree diagram, including a +[link](#link) to +render links from parent to child, an optional +[dot](#dot) for +nodes, and a +[text](#text) for +node labels. The link mark uses the [treeLink +transform](#plottreelinkoptions), +while the dot and text marks use the [treeNode +transform](#plottreenodeoptions). +The following options are supported: * **fill** - the dot and text fill color; defaults to *node:internal* * **stroke** - the link stroke color; inherits **fill** by default @@ -2475,20 +3019,26 @@ A convenience compound mark for rendering a tree diagram, including a [link](#li * **marker** - the link start and end marker * **markerStart** - the link start marker * **markerEnd** - the link end marker -* **dot** - if true, whether to render a dot; defaults to false if no link marker +* **dot** - if true, whether to render a dot; defaults to false if no link + marker * **title** - the text and dot title; defaults to *node:path* * **text** - the text label; defaults to *node:name* * **textStroke** - the text stroke; defaults to *white* -* **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 if right-anchored +* **dx** - the text horizontal offset; defaults to 6 if left-anchored, or -6 + if right-anchored * **dy** - the text vertical offset; defaults to 0 -Any additional *options* are passed through to the constituent link, dot, and text marks and their corresponding treeLink or treeNode transform. +Any additional *options* are passed through to the constituent link, dot, and +text marks and their corresponding treeLink or treeNode transform. #### Plot.cluster(*data*, *options*) -Like [Plot.tree](#plottreedata-options), except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, which aligns leaf nodes. +Like +[Plot.tree](#plottreedata-options), +except sets the **treeLayout** option to D3’s cluster (dendrogram) algorithm, +which aligns leaf nodes. @@ -2504,32 +3054,60 @@ Plot provides a few helpers for implementing transforms. #### Plot.valueof(*data*, *value*, *type*) -Given an iterable *data* and some *value* accessor, returns an array (a column) of the specified *type* with the corresponding value of each element of the data. The *value* accessor may be one of the following types: +Given an iterable *data* and some *value* accessor, returns an array (a +column) of the specified *type* with the corresponding value of each element +of the data. The *value* accessor may be one of the following types: * a string - corresponding to the field accessor (`d => d[value]`) * an accessor function - called as *type*.from(*data*, *value*) -* a number, Date, or boolean — resulting in an array uniformly filled with the *value* +* a number, Date, or boolean — resulting in an array uniformly filled with + the *value* * an object with a transform method — called as *value*.transform(*data*) * an array of values - returning the same * null or undefined - returning the same -If *type* is specified, it must be Array or a similar class that implements the [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) interface such as a typed array. When *type* is Array or a typed array class, the return value of valueof will be an instance of the same (or null or undefined). If *type* is not specified, valueof may return either an array or a typed array (or null or undefined). +If *type* is specified, it must be Array or a similar class that implements +the +[Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) +interface such as a typed array. When *type* is Array or a typed array class, +the return value of valueof will be an instance of the same (or null or +undefined). If *type* is not specified, valueof may return either an array or +a typed array (or null or undefined). -Plot.valueof is not guaranteed to return a new array. When a transform method is used, or when the given *value* is an array that is compatible with the requested *type*, the array may be returned as-is without making a copy. +Plot.valueof is not guaranteed to return a new array. When a transform method +is used, or when the given *value* is an array that is compatible with the +requested *type*, the array may be returned as-is without making a copy. #### Plot.transform(*options*, *transform*) -Given an *options* object that may specify some basic transforms (*filter*, *sort*, or *reverse*) or a custom *transform* function, composes those transforms if any with the given *transform* function, returning a new *options* object. If a custom *transform* function is present on the given *options*, any basic transforms are ignored. Any additional input *options* are passed through in the returned *options* object. This method facilitates applying the basic transforms prior to applying the given custom *transform* and is used internally by Plot’s built-in transforms. +Given an *options* object that may specify some basic transforms (*filter*, +*sort*, or *reverse*) or a custom *transform* function, composes those +transforms if any with the given *transform* function, returning a new +*options* object. If a custom *transform* function is present on the given +*options*, any basic transforms are ignored. Any additional input *options* +are passed through in the returned *options* object. This method facilitates +applying the basic transforms prior to applying the given custom *transform* +and is used internally by Plot’s built-in transforms. #### Plot.column(*source*) -This helper for constructing derived columns returns a [*column*, *setColumn*] array. The *column* object implements *column*.transform, returning whatever value was most recently passed to *setColumn*. If *setColumn* is not called, then *column*.transform returns undefined. If a *source* is specified, then *column*.label exposes the given *source*’s label, if any: if *source* is a string as when representing a named field of data, then *column*.label is *source*; otherwise *column*.label propagates *source*.label. This allows derived columns to propagate a human-readable axis or legend label. +This helper for constructing derived columns returns a [*column*, +*setColumn*] array. The *column* object implements *column*.transform, +returning whatever value was most recently passed to *setColumn*. If +*setColumn* is not called, then *column*.transform returns undefined. If a +*source* is specified, then *column*.label exposes the given *source*’s +label, if any: if *source* is a string as when representing a named field of +data, then *column*.label is *source*; otherwise *column*.label propagates +*source*.label. This allows derived columns to propagate a human-readable +axis or legend label. -Plot.column is typically used by options transforms to define new channels; the associated columns are populated (derived) when the **transform** option function is invoked. +Plot.column is typically used by options transforms to define new channels; +the associated columns are populated (derived) when the **transform** option +function is invoked. @@ -2558,7 +3136,9 @@ The dodge layout is highly dependent on the input data order: the circles placed Plot.dodgeY({x: "date"}) ``` -Given marks arranged along the *x* axis, the dodgeY transform piles them vertically by defining a *y* position channel that avoids overlapping. The *x* position channel is unchanged. +Given marks arranged along the *x* axis, the dodgeY transform piles them +vertically by defining a *y* position channel that avoids overlapping. The +*x* position channel is unchanged. @@ -2569,7 +3149,9 @@ Given marks arranged along the *x* axis, the dodgeY transform piles them vertica Plot.dodgeX({y: "value"}) ``` -Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. +Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* +position channel that avoids overlapping. The *y* position channel is +unchanged. @@ -2584,7 +3166,16 @@ Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position #### Plot.hexbin(*outputs*, *options*) -Aggregates the given input channels into hexagonal bins, creating output channels with the reduced data. The *options* must specify the **x** and **y** channels. The **binWidth** option (default 20) defines the distance between centers of neighboring hexagons in pixels. If any of **z**, **fill**, or **stroke** is a channel, the first of these channels will be used to subdivide bins. The *outputs* options are similar to the [bin transform](#bin); each output channel receives as input, for each hexagon, the subset of the data which has been matched to its center. The outputs object specifies the aggregation method for each output channel. +Aggregates the given input channels into hexagonal bins, creating output +channels with the reduced data. The *options* must specify the **x** and +**y** channels. The **binWidth** option (default 20) defines the distance +between centers of neighboring hexagons in pixels. If any of **z**, **fill**, +or **stroke** is a channel, the first of these channels will be used to +subdivide bins. The *outputs* options are similar to the [bin +transform](#bin); +each output channel receives as input, for each hexagon, the subset of the +data which has been matched to its center. The outputs object specifies the +aggregation method for each output channel. The following aggregation methods are supported: @@ -2593,7 +3184,8 @@ The following aggregation methods are supported: * *count* - the number of elements (frequency) * *distinct* - the number of distinct values * *sum* - the sum of values -* *proportion* - the sum proportional to the overall total (weighted frequency) +* *proportion* - the sum proportional to the overall total (weighted + frequency) * *proportion-facet* - the sum proportional to the facet total * *min* - the minimum value * *min-index* - the zero-based index of the minimum value @@ -2602,12 +3194,16 @@ The following aggregation methods are supported: * *mean* - the mean value (average) * *median* - the median value * *deviation* - the standard deviation -* *variance* - the variance per [Welford’s algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) +* *variance* - the variance per [Welford’s + algorithm](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) * *mode* - the value with the most occurrences -* a function to be passed the array of values for each bin and the extent of the bin +* a function to be passed the array of values for each bin and the extent of + the bin * an object with a *reduce* method -See also the [hexgrid](#hexgrid) mark. +See also the +[hexgrid](#hexgrid) +mark. @@ -2621,7 +3217,8 @@ If an initializer desires a channel that is not supported by the downstream mark #### Plot.initializer(*options*, *initializer*) -This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. +This helper composes the *initializer* function with any other transforms +present in the *options*, and returns a new *options* object. @@ -2693,7 +3290,8 @@ These helper functions are provided for use as a *scale*.tickFormat [axis option Plot.formatIsoDate(new Date("2020-01-01T00:00.000Z")) // "2020-01-01" ``` -Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the given *date* is not valid, returns `"Invalid Date"`. +Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the +given *date* is not valid, returns `"Invalid Date"`. @@ -2703,7 +3301,12 @@ Given a *date*, returns the shortest equivalent ISO 8601 UTC string. If the give Plot.formatWeekday("es-MX", "long")(0) // "domingo" ``` -Returns a function that formats a given week day number (from 0 = Sunday to 6 = Saturday) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [weekday format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, *short*, or *long*; if not specified, it defaults to *short*. +Returns a function that formats a given week day number (from 0 = Sunday to 6 += Saturday) according to the specified *locale* and *format*. The *locale* is +a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to +U.S. English. The *format* is a [weekday +format](https://tc39.es/ecma402/#datetimeformat-objects): either *narrow*, +*short*, or *long*; if not specified, it defaults to *short*. @@ -2713,7 +3316,13 @@ Returns a function that formats a given week day number (from 0 = Sunday to 6 = Plot.formatMonth("es-MX", "long")(0) // "enero" ``` -Returns a function that formats a given month number (from 0 = January to 11 = December) according to the specified *locale* and *format*. The *locale* is a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to U.S. English. The *format* is a [month format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, *numeric*, *narrow*, *short*, *long*; if not specified, it defaults to *short*. +Returns a function that formats a given month number (from 0 = January to 11 += December) according to the specified *locale* and *format*. The *locale* is +a [BCP 47 language tag](https://tools.ietf.org/html/bcp47) and defaults to +U.S. English. The *format* is a [month +format](https://tc39.es/ecma402/#datetimeformat-objects): either *2-digit*, +*numeric*, *narrow*, *short*, *long*; if not specified, it defaults to +*short*.