diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 3fa0cc8c1a..f91c7ca6e7 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -27,4 +27,3 @@ jobs: echo ::add-matcher::.github/eslint.json yarn run eslint . --format=compact - run: yarn test - - run: yarn readme:check diff --git a/.gitignore b/.gitignore index 8df33ba803..606fc96519 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +build/ dist/ types/ node_modules/ diff --git a/README.md b/README.md index 16c0e4b641..2256fc8a3e 100644 --- a/README.md +++ b/README.md @@ -60,28 +60,15 @@ 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. + + +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](./test/data/alphabet.csv). ```js Plot.plot({ @@ -94,8 +81,7 @@ 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 @@ -105,39 +91,11 @@ numbers in pixels: * **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({ @@ -149,11 +107,7 @@ 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({ @@ -162,26 +116,13 @@ 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({ @@ -192,18 +133,15 @@ 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: @@ -218,171 +156,59 @@ 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({ @@ -394,13 +220,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 @@ -408,15 +232,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](#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"}}); @@ -424,31 +244,18 @@ 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. - + ### Position options @@ -808,23 +615,17 @@ 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*: + + +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({ @@ -835,7 +636,7 @@ Plot.legend({ }) ``` - + ## Marks @@ -968,17 +769,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](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](./src/marks/box.js) for an example. - + ### Area @@ -1007,93 +804,57 @@ 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](#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. - + #### Plot.areaX(*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](#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. - + #### Plot.areaY(*data*, *options*) + + ```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](#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. - + ### Arrow @@ -1121,17 +882,17 @@ 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 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*. - + ### Bar @@ -1141,84 +902,55 @@ 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 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). - + #### Plot.barY(*data*, *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: +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). - + ### Box @@ -1241,33 +973,29 @@ 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 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. - + #### Plot.boxY(*data*, *options*) + + ```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. +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 @@ -1284,50 +1012,41 @@ 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 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₂*, …]. - + #### 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](#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*) + + ```js 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₂*, …]. - + ### Delaunay @@ -1335,74 +1054,55 @@ channel, the fill defaults to the identity function and assumes that *data* = [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. +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. +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. + -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. +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. + + #### 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. +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. + + +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. - + ### Density @@ -1410,30 +1110,17 @@ separate Voronoi tesselations are constructed for each group. [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. + + +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. - + ### Dot @@ -1466,86 +1153,73 @@ 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 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₂*, …]. - + #### 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](#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*. - + #### 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](#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*. - + ### Hexgrid 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. - + ### Image @@ -1574,20 +1248,17 @@ 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 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₂*, …]. - + ### Linear regression @@ -1605,29 +1276,29 @@ 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 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. - + #### Plot.linearRegressionY(*data*, *options*) + + ```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. +Returns a linear regression mark where *y* is the dependent variable and *x* is the independent variable. - + ### Line @@ -1652,78 +1323,57 @@ 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 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₂*, …]. - + #### 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](#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. - + #### 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](#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. - + ### Link @@ -1744,17 +1394,17 @@ 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 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*. - + ### Rect @@ -1775,51 +1425,41 @@ 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 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](#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. - + #### Plot.rectY(*data*, *options*) + + ```js 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. - + ### Rule @@ -1829,10 +1469,10 @@ function. 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 Plot.ruleX([0]) // as annotation ``` @@ -1840,33 +1480,22 @@ 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*. - + #### Plot.ruleY(*data*, *options*) + + ```js Plot.ruleY([0]) // as annotation ``` @@ -1875,30 +1504,17 @@ 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](#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*. - + ### Text @@ -1944,44 +1560,33 @@ 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₂*, …]. + + +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₂*, …]. + -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*. +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*. + + #### Plot.textY(*data*, *options*) -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*. +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*. + + ### Tick @@ -1991,16 +1596,15 @@ consecutive multiples of *n* that bracket *x*. 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 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 @@ -2008,31 +1612,29 @@ 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). - + #### Plot.tickY(*data*, *options*) + + ```js 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). - + ### Vector @@ -2064,38 +1666,33 @@ 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 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₂*, …]. + + ## Decorations @@ -2109,17 +1706,17 @@ 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 Plot.frame({stroke: "red"}) ``` Returns a new frame with the specified *options*. - + ## Transforms @@ -2165,55 +1762,53 @@ 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](#index-sorting), which allows marks to be sorted by a named channel, such as *r* for radius. - + #### Plot.shuffle(*options*) + + ```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. +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 Plot.reverse(options) // reverse the input order ``` Reverses the order of the data. - + #### Plot.filter(*test*, *options*) + + ```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. +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 @@ -2335,42 +1930,41 @@ 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 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. - - + #### Plot.binX(*outputs*, *options*) + + ```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. - + #### Plot.binY(*outputs*, *options*) + + ```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. +Bins on *y*. Also groups on *x* and first channel of *z*, *fill*, or *stroke*, if any. - + ### Group @@ -2445,54 +2039,53 @@ 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 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. - + #### Plot.groupX(*outputs*, *options*) + + ```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. - - + #### Plot.groupY(*outputs*, *options*) + + ```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. - - + #### Plot.groupZ(*outputs*, *options*) + + ```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. - + ### Map @@ -2556,80 +2149,77 @@ The following window reducers are supported: By default, **anchor** is *middle* and **reduce** is *mean*. - - #### Plot.map(*outputs*, *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*. - +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 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*. - - + #### Plot.mapY(*map*, *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*. +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 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. - + #### 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](#plotmapxmap-options), but applies the normalize map method with the given *basis*. - + #### Plot.normalizeY(*basis*, *options*) + + ```js 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*. - + #### Plot.window(*k*) @@ -2663,21 +2253,13 @@ 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. + + +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({ @@ -2705,43 +2287,55 @@ 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 @@ -2787,92 +2381,77 @@ 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*) + + ```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. +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*) + + ```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. - +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*) + + ```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. +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. - - + #### Plot.stackX(*stack*, *options*) + + ```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. - + #### Plot.stackX1(*stack*, *options*) + + ```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. +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*) + + ```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. - +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 @@ -2928,22 +2507,13 @@ 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). + -The treeNode transform will derive output columns for any *options* that have -one of the following named node values: +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: * *node:name* - the node’s name (the last part of its path) * *node:path* - the node’s full, normalized, slash-separated path @@ -2951,27 +2521,17 @@ one of the following named node values: * *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 @@ -2981,31 +2541,17 @@ one of the following named link values: * *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 @@ -3019,28 +2565,24 @@ The following options are supported: * **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. - + ### Custom transforms @@ -3050,66 +2592,42 @@ 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: + + +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. - + ## Initializers @@ -3128,33 +2646,29 @@ 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*) + + ```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. +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*) + + ```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. - +Equivalent to Plot.dodgeY, but piling horizontally, creating a new *x* position channel that avoids overlapping. The *y* position channel is unchanged. - + ### Hexbin @@ -3162,20 +2676,11 @@ unchanged. [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](#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: @@ -3184,8 +2689,7 @@ 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 @@ -3194,18 +2698,14 @@ 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. - + ### Custom initializers @@ -3213,14 +2713,13 @@ 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. + + +This helper composes the *initializer* function with any other transforms present in the *options*, and returns a new *options* object. - + ## Curves @@ -3282,49 +2781,41 @@ 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 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"`. - + #### Plot.formatWeekday(*locale*, *format*) + + ```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*. +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*) + + ```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*. +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 diff --git a/bundle.js b/bundle.js index b475ebe70e..3e529a5d68 100644 --- a/bundle.js +++ b/bundle.js @@ -1,2 +1,2 @@ export {version} from "./package.json"; -export * from "./src/index.js"; +export * from "./dist/index.js"; diff --git a/package.json b/package.json index c144d7a6a5..a189a17afb 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,11 @@ "types/**/*.d.ts" ], "scripts": { - "test": "yarn test:mocha && yarn test:typecheck && yarn test:lint && yarn readme:check", + "test": "yarn test:mocha && yarn test:typecheck && yarn test:lint", "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", + "prepublishOnly": "rm -rf build dist && tsc && tsx scripts/readme-to-jsdoc.ts && rollup -c", "postpublish": "git push && git push --tags", "dev": "vite" }, @@ -47,7 +45,6 @@ "@rollup/plugin-commonjs": "22", "@rollup/plugin-json": "4", "@rollup/plugin-node-resolve": "13", - "@rollup/plugin-typescript": "^8.3.2", "@types/d3": "^7.4.0", "@types/expect": "^24.3.0", "@types/mocha": "^9.1.1", @@ -62,13 +59,12 @@ "htl": "0.3", "js-beautify": "1", "jsdom": "19", + "mkdirp": "^1.0.4", "mocha": "10", "module-alias": "2", "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", diff --git a/rollup.config.js b/rollup.config.js index c02c50d222..63ec4f4250 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,7 +4,6 @@ import commonjs from "@rollup/plugin-commonjs"; import json from "@rollup/plugin-json"; import node from "@rollup/plugin-node-resolve"; import * as meta from "./package.json"; -import typescript from "@rollup/plugin-typescript"; const filename = meta.name.split("/").pop(); @@ -27,7 +26,7 @@ const config = { indent: false, banner: `// ${meta.name} v${meta.version} Copyright ${copyrights.join(", ")}` }, - plugins: [typescript(), commonjs(), json(), node()] + plugins: [commonjs(), json(), node()] }; export default [ diff --git a/scripts/jsdoc-to-readme.ts b/scripts/jsdoc-to-readme.ts deleted file mode 100644 index 52953bf1a7..0000000000 --- a/scripts/jsdoc-to-readme.ts +++ /dev/null @@ -1,119 +0,0 @@ -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("$/.exec(line))) { + const [, name] = match; + if (doc) { + throw new Error(`nested jsdoc directive on line ${i}: ${line}`); + } + if (docmap.has(name)) { + throw new Error(`duplicate jsdoc directive on line ${i}: ${line}`); + } + doc = {name, lines: []}; + } else if ((match = /^$/.exec(line))) { + const [, name] = match; + if (!doc) { + throw new Error(`orphaned jsdocEnd directive on line ${i}: ${line}`); + } + if (doc.name !== name) { + throw new Error(`mismatched jsdocEnd ${doc.name} directive on line ${i}: ${line}`); + } + docmap.set(doc.name, doc.lines); + doc = null; + } else { + throw new Error(`malformed jsdoc directive on line ${i}: ${line}`); + } + } else if (doc) { + doc.lines.push(line); + } +} + +// Make relative and anchor links absolute. +for (const lines of docmap.values()) { + for (let i = 0, n = lines.length; i < n; ++i) { + lines[i] = lines[i] + .replace(/\]\(#([^)]+)\)/g, "](./README.md#$1)") + .replace(/\]\(\.\/([^)]+)\)/g, "](https://github.com/observablehq/plot/blob/main/$1)"); + } +} + +// Copy files from build/ to dist/, replacing /** @jsdoc name */ directives. +const unused = new Set(docmap.keys()); +for (const file of glob.sync("build/**/*.js")) { + process.stdout.write(`\x1b[2m${file}\x1b[0m`); + const lines = readFileSync(file, "utf-8").split("\n"); + let count = 0; + for (let i = 0, n = lines.length; i < n; ++i) { + let match: RegExpExecArray | null; + if ((match = /^\/\*\*\s+@jsdoc\s+(\w+)\s+\*\/$/.exec(lines[i]))) { + const [, name] = match; + const docs = docmap.get(name); + if (!docs) throw new Error(`missing @jsdoc definition: ${name}`); + if (!unused.has(name)) throw new Error(`duplicate @jsdoc reference: ${name}`); + unused.delete(name); + ++count; + lines[i] = docs + .map((line, i, lines) => (i === 0 ? `/** ${line}` : i === lines.length - 1 ? ` * ${line}\n */` : ` * ${line}`)) + .join("\n"); + } + } + const ofile = file.replace(/^build\//, "dist/"); + process.stdout.write(` → \x1b[36m${ofile}\x1b[0m${count ? ` (${count} jsdoc${count === 1 ? "" : "s"})` : ""}\n`); + const odir = dirname(ofile); + mkdirp.sync(odir); + writeFileSync(ofile, lines.join("\n"), "utf-8"); +} + +for (const name of unused) { + console.warn(`\x1b[33m[warning] unused @jsdoc directive:\x1b[0m ${name}`); +} diff --git a/src/format.ts b/src/format.ts index 6cd153d2a2..def579bd04 100644 --- a/src/format.ts +++ b/src/format.ts @@ -20,19 +20,7 @@ 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*. - */ +/** @jsdoc formatMonth */ export function formatMonth( locale = "en-US", format: "numeric" | "2-digit" | "long" | "short" | "narrow" | undefined = "short" @@ -42,32 +30,14 @@ export function formatMonth( i != null && !isNaN((i = +new Date(Date.UTC(2000, +i)))) ? fmt.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*. - */ +/** @jsdoc formatWeekday */ 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)))) ? fmt.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"`. - */ +/** @jsdoc formatIsoDate */ export function formatIsoDate(date: Date): string { return isoFormat(date, "Invalid Date"); } diff --git a/src/legends.js b/src/legends.js index 5136bb9248..2a8319d064 100644 --- a/src/legends.js +++ b/src/legends.js @@ -11,30 +11,7 @@ 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" - * } - * }) - * ``` - */ +/** @jsdoc legend */ export function legend(options = {}) { for (const [key, value] of legendRegistry) { const scale = options[key]; diff --git a/src/marks/area.js b/src/marks/area.js index 1234b16e2c..ba113090fa 100644 --- a/src/marks/area.js +++ b/src/marks/area.js @@ -71,93 +71,19 @@ 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/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. - */ +/** @jsdoc area */ export function area(data, options) { if (options === undefined) return areaY(data, {x: first, y: second}); 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/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. - * - * ```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. - */ +/** @jsdoc areaX */ export function areaX(data, options) { const {y = indexOf, ...rest} = maybeDenseIntervalY(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/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. - * - * ```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. - */ +/** @jsdoc areaY */ export function areaY(data, options) { const {x = indexOf, ...rest} = maybeDenseIntervalX(options); return new Area(data, maybeStackY(maybeIdentityY({...rest, x1: x, x2: undefined}))); diff --git a/src/marks/arrow.js b/src/marks/arrow.js index 79fbefa392..369fa23f52 100644 --- a/src/marks/arrow.js +++ b/src/marks/arrow.js @@ -176,13 +176,7 @@ function circleCircleIntersect([ax, ay, ar], [bx, by, br], sign) { return [ax + (dx * x + dy * y) / d, ay + (dy * x - dx * y) / d]; } -/** - * ```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*. - */ +/** @jsdoc arrow */ export function arrow(data, options = {}) { let {x, x1, x2, y, y1, y2, ...remainingOptions} = options; [x1, x2] = maybeSameValue(x, x1, x2); diff --git a/src/marks/bar.js b/src/marks/bar.js index 0a3874f981..144d58cb37 100644 --- a/src/marks/bar.js +++ b/src/marks/bar.js @@ -129,81 +129,12 @@ 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/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). - */ +/** @jsdoc barX */ 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/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/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* - * - * If the **x** channel is not specified, the bar will span the full horizontal - * extent of the plot (or facet). - */ +/** @jsdoc barY */ 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 6f18204d57..dcf15af8fd 100644 --- a/src/marks/box.js +++ b/src/marks/box.js @@ -7,16 +7,7 @@ import {dot} from "./dot.js"; import {ruleX, ruleY} from "./rule.js"; import {tickX, tickY} from "./tick.js"; -/** - * ```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. - */ +/** @jsdoc boxX */ 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. @@ -40,16 +31,7 @@ export function boxX(data, options = {}) { ); } -/** - * ```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. - */ +/** @jsdoc boxY */ 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. diff --git a/src/marks/cell.js b/src/marks/cell.js index 6df7557432..92722d588d 100644 --- a/src/marks/cell.js +++ b/src/marks/cell.js @@ -24,52 +24,21 @@ export class Cell extends AbstractBar { } } -/** - * ```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₂*, …]. - */ +/** @jsdoc cell */ export function cell(data, options = {}) { let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); return new Cell(data, {...remainingOptions, x, y}); } -/** - * ```js - * 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₂*, …]. - */ +/** @jsdoc cellX */ 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, {...remainingOptions, x, fill, stroke}); } -/** - * ```js - * 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₂*, …]. - */ +/** @jsdoc cellY */ export function cellY(data, options = {}) { let {y = indexOf, fill, stroke, ...remainingOptions} = options; if (fill === undefined && maybeColorChannel(stroke)[0] === undefined) fill = identity; diff --git a/src/marks/delaunay.js b/src/marks/delaunay.js index e80603db4f..ff3472621f 100644 --- a/src/marks/delaunay.js +++ b/src/marks/delaunay.js @@ -279,74 +279,27 @@ 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. - */ +/** @jsdoc delaunayLink */ 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. - */ +/** @jsdoc delaunayMesh */ 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. - */ +/** @jsdoc hull */ 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. - */ +/** @jsdoc voronoi */ 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. - */ +/** @jsdoc voronoiMesh */ export function voronoiMesh(data, options) { return delaunayMark(VoronoiMesh, data, options); } diff --git a/src/marks/density.js b/src/marks/density.js index a8d9cf2d93..b637d7055e 100644 --- a/src/marks/density.js +++ b/src/marks/density.js @@ -65,26 +65,7 @@ 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. - * - * 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. - */ +/** @jsdoc density */ export function density(data, options = {}) { let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); diff --git a/src/marks/dot.js b/src/marks/dot.js index 6b3e380cc5..543c8b3a8d 100644 --- a/src/marks/dot.js +++ b/src/marks/dot.js @@ -113,76 +113,31 @@ export class Dot extends Mark { } } -/** - * ```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₂*, …]. - */ +/** @jsdoc dot */ export function dot(data, options = {}) { let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); return new Dot(data, {...remainingOptions, x, y}); } -/** - * ```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*. - */ +/** @jsdoc dotX */ export function dotX(data, options = {}) { const {x = identity, ...remainingOptions} = options; return new Dot(data, maybeIntervalMidY({...remainingOptions, x})); } -/** - * ```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*. - */ +/** @jsdoc dotY */ export function dotY(data, options = {}) { const {y = identity, ...remainingOptions} = 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*. - */ +/** @jsdoc 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*. - */ +/** @jsdoc hexagon */ export function hexagon(data, options) { return dot(data, {...options, symbol: "hexagon"}); } diff --git a/src/marks/frame.js b/src/marks/frame.js index d97e671f66..a4ed54600c 100644 --- a/src/marks/frame.js +++ b/src/marks/frame.js @@ -37,13 +37,7 @@ export class Frame extends Mark { } } -/** - * ```js - * Plot.frame({stroke: "red"}) - * ``` - * - * Returns a new frame with the specified *options*. - */ +/** @jsdoc frame */ export function frame(options) { return new Frame(options); } diff --git a/src/marks/hexgrid.js b/src/marks/hexgrid.js index 337a67c786..5661b91de8 100644 --- a/src/marks/hexgrid.js +++ b/src/marks/hexgrid.js @@ -12,11 +12,7 @@ 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. - */ +/** @jsdoc hexgrid */ export function hexgrid(options) { return new Hexgrid(options); } diff --git a/src/marks/image.js b/src/marks/image.js index efe203a40f..6d2673b81e 100644 --- a/src/marks/image.js +++ b/src/marks/image.js @@ -109,16 +109,7 @@ export class Image extends Mark { } } -/** - * ```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₂*, …]. - */ +/** @jsdoc image */ export function image(data, options = {}) { let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); diff --git a/src/marks/line.js b/src/marks/line.js index a4db931266..42a572eaad 100644 --- a/src/marks/line.js +++ b/src/marks/line.js @@ -70,79 +70,20 @@ export class Line extends Mark { } } -/** - * ```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₂*, …]. - */ +/** @jsdoc line */ export function line(data, options = {}) { let {x, y, ...remainingOptions} = options; [x, y] = maybeTuple(x, y); return new Line(data, {...remainingOptions, 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. - */ +/** @jsdoc lineX */ export function lineX(data, options = {}) { const {x = identity, y = indexOf, ...remainingOptions} = options; return new Line(data, maybeDenseIntervalY({...remainingOptions, 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. - */ +/** @jsdoc lineY */ export function lineY(data, options = {}) { const {x = indexOf, y = identity, ...remainingOptions} = options; return new Line(data, maybeDenseIntervalX({...remainingOptions, x, y})); diff --git a/src/marks/linearRegression.js b/src/marks/linearRegression.js index 7cdd44902d..1438b5d659 100644 --- a/src/marks/linearRegression.js +++ b/src/marks/linearRegression.js @@ -122,14 +122,7 @@ class LinearRegressionY extends LinearRegression { } } -/** - * ```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. - */ +/** @jsdoc linearRegressionX */ export function linearRegressionX(data, options = {}) { const { y = indexOf, @@ -141,14 +134,7 @@ export function linearRegressionX(data, options = {}) { return new LinearRegressionX(data, maybeDenseIntervalY({...remainingOptions, 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. - */ +/** @jsdoc linearRegressionY */ export function linearRegressionY(data, options = {}) { const { x = indexOf, diff --git a/src/marks/link.js b/src/marks/link.js index b4d9529c7d..b57f2da1f7 100644 --- a/src/marks/link.js +++ b/src/marks/link.js @@ -58,13 +58,7 @@ export class Link extends Mark { } } -/** - * ```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*. - */ +/** @jsdoc link */ export function link(data, options = {}) { let {x, x1, x2, y, y1, y2, ...remainingOptions} = options; [x1, x2] = maybeSameValue(x, x1, x2); diff --git a/src/marks/rect.js b/src/marks/rect.js index b7a18b0192..db65a45189 100644 --- a/src/marks/rect.js +++ b/src/marks/rect.js @@ -88,49 +88,17 @@ 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*. - */ +/** @jsdoc rect */ 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. - */ +/** @jsdoc rectX */ 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. - */ +/** @jsdoc rectY */ 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 caedd29d89..5dd281ecf3 100644 --- a/src/marks/rule.js +++ b/src/marks/rule.js @@ -107,75 +107,14 @@ 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*. - */ +/** @jsdoc ruleX */ 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*. - */ +/** @jsdoc ruleY */ export function ruleY(data, options) { let {y = identity, x, x1, x2, ...rest} = maybeIntervalX(options); [x1, x2] = maybeOptionalZero(x, x1, x2); diff --git a/src/marks/text.js b/src/marks/text.js index 344e89c68f..939b181801 100644 --- a/src/marks/text.js +++ b/src/marks/text.js @@ -157,45 +157,20 @@ 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₂*, …]. - */ +/** @jsdoc text */ export function text(data, options = {}) { let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); return new Text(data, {...remainingOptions, x, y}); } -/** - * 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*. - */ +/** @jsdoc textX */ export function textX(data, options = {}) { const {x = identity, ...remainingOptions} = options; return new Text(data, maybeIntervalMidY({...remainingOptions, x})); } -/** - * 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*. - */ +/** @jsdoc textY */ export function textY(data, options = {}) { const {y = identity, ...remainingOptions} = options; return new Text(data, maybeIntervalMidX({...remainingOptions, y})); diff --git a/src/marks/tick.js b/src/marks/tick.js index 9303b7c46d..acac004c14 100644 --- a/src/marks/tick.js +++ b/src/marks/tick.js @@ -100,46 +100,13 @@ export class TickY extends AbstractTick { } } -/** - * ```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). - */ +/** @jsdoc tickX */ export function tickX(data, options = {}) { const {x = identity, ...remainingOptions} = options; return new TickX(data, {...remainingOptions, x}); } -/** - * ```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). - */ +/** @jsdoc tickY */ export function tickY(data, options = {}) { const {y = identity, ...remainingOptions} = options; return new TickY(data, {...remainingOptions, y}); diff --git a/src/marks/tree.js b/src/marks/tree.js index 736c5d2338..326f47f586 100644 --- a/src/marks/tree.js +++ b/src/marks/tree.js @@ -6,43 +6,7 @@ import {dot} from "./dot.js"; 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: - * - * * **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. - */ +/** @jsdoc tree */ export function tree(data, options = {}) { let { fill, @@ -103,12 +67,7 @@ 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. - */ +/** @jsdoc cluster */ export function cluster(data, options) { return tree(data, {...options, treeLayout: Cluster}); } diff --git a/src/marks/vector.js b/src/marks/vector.js index 7dc00e5700..bb0125d930 100644 --- a/src/marks/vector.js +++ b/src/marks/vector.js @@ -74,39 +74,20 @@ export class Vector extends Mark { } } -/** - * ```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₂*, …]. - */ +/** @jsdoc vector */ export function vector(data, options = {}) { let {x, y, ...remainingOptions} = options; if (options.frameAnchor === undefined) [x, y] = maybeTuple(x, y); return new Vector(data, {...remainingOptions, x, y}); } -/** - * 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₂*, …]. - */ +/** @jsdoc vectorX */ export function vectorX(data, options = {}) { const {x = identity, ...remainingOptions} = options; return new Vector(data, {...remainingOptions, x}); } -/** - * 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₂*, …]. - */ +/** @jsdoc vectorY */ export function vectorY(data, options = {}) { const {y = identity, ...remainingOptions} = options; return new Vector(data, {...remainingOptions, y}); diff --git a/src/options.js b/src/options.js index 94ca533c1a..b0164f9b41 100644 --- a/src/options.js +++ b/src/options.js @@ -5,31 +5,7 @@ import {color, descending, quantile} from "d3"; 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 *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* - * * 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. - */ +/** @jsdoc valueof */ export function valueof(data, value, type) { const valueType = typeof value; return valueType === "string" @@ -217,21 +193,7 @@ export function maybeInput(key, options) { return options[key]; } -/** - * 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. - */ +/** @jsdoc column */ export function column(source) { // Defines a column whose values are lazily populated by calling the returned // setter. If the given source is labeled, the label is propagated to the diff --git a/src/plot.js b/src/plot.js index 0e53efdce0..33b80e48d7 100644 --- a/src/plot.js +++ b/src/plot.js @@ -24,351 +24,7 @@ import {basic, initializer} from "./transforms/basic.js"; import {maybeInterval} from "./transforms/interval.js"; import {consumeWarnings, warn} 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](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({ - * 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](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: - * - * ```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](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: - * - * * *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. - * - * 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"] - * ``` - */ +/** @jsdoc plot */ export function plot(options = {}) { const {facet, style, caption, ariaLabel, ariaDescription} = options; @@ -707,13 +363,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. - */ +/** @jsdoc marks */ export function marks(...marks) { marks.plot = Mark.prototype.plot; return marks; diff --git a/src/scales.js b/src/scales.js index fe153c92f7..e1e4160e18 100644 --- a/src/scales.js +++ b/src/scales.js @@ -471,43 +471,7 @@ 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. - */ +/** @jsdoc scale */ export function scale(options = {}) { let scale; for (const key in options) { diff --git a/src/transforms/basic.js b/src/transforms/basic.js index 7c6176bed5..6cbc7097f7 100644 --- a/src/transforms/basic.js +++ b/src/transforms/basic.js @@ -2,16 +2,7 @@ import {randomLcg} from "d3"; 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. - */ +/** @jsdoc transform */ export function basic(options = {}, transform) { let {filter: f1, sort: s1, reverse: r1, transform: t1, initializer: i1, ...remainingOptions} = options; // If both t1 and t2 are defined, returns a composite transform that first @@ -30,10 +21,7 @@ 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. - */ +/** @jsdoc initializer */ 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 @@ -75,15 +63,7 @@ function apply(options, t) { return (options.initializer != null ? initializer : basic)(options, t); } -/** - * ```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. - */ +/** @jsdoc filter */ export function filter(test, options) { return apply(options, filterTransform(test)); } @@ -95,13 +75,7 @@ function filterTransform(value) { }; } -/** - * ```js - * Plot.reverse(options) // reverse the input order - * ``` - * - * Reverses the order of the data. - */ +/** @jsdoc reverse */ export function reverse(options) { return {...apply(options, reverseTransform), sort: null}; } @@ -110,31 +84,13 @@ function reverseTransform(data, facets) { return {data, facets: facets.map((I) => I.slice().reverse())}; } -/** - * ```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. - */ +/** @jsdoc shuffle */ export function shuffle(options = {}) { const {seed, ...remainingOptions} = options; return {...apply(remainingOptions, sortValue(seed == null ? Math.random : randomLcg(seed))), sort: null}; } -/** - * ```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. - */ +/** @jsdoc sort */ export function sort(order, options) { return { ...(isOptions(order) && order.channel !== undefined ? initializer : apply)(options, sortTransform(order)), diff --git a/src/transforms/bin.js b/src/transforms/bin.js index 91ab1666d5..1e664a15a0 100644 --- a/src/transforms/bin.js +++ b/src/transforms/bin.js @@ -30,16 +30,7 @@ import { import {maybeInsetX, maybeInsetY} from "./inset.js"; import {maybeInterval} from "./interval.js"; -/** - * ```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 - */ +/** @jsdoc binX */ export function binX(outputs = {y: "count"}, options = {}) { // Group on {z, fill, stroke}, then optionally on y, then bin x. [outputs, options] = mergeOptions(outputs, options); @@ -47,14 +38,7 @@ export function binX(outputs = {y: "count"}, options = {}) { return binn(maybeBinValue(x, options, identity), null, null, y, outputs, maybeInsetX(options)); } -/** - * ```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. - */ +/** @jsdoc binY */ export function binY(outputs = {x: "count"}, options = {}) { // Group on {z, fill, stroke}, then optionally on x, then bin y. [outputs, options] = mergeOptions(outputs, options); @@ -62,16 +46,7 @@ export function binY(outputs = {x: "count"}, options = {}) { return binn(null, maybeBinValue(y, options, identity), x, null, outputs, maybeInsetY(options)); } -/** - * ```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 - */ +/** @jsdoc bin */ export function bin(outputs = {fill: "count"}, options = {}) { // Group on {z, fill, stroke}, then bin on x and y. [outputs, options] = mergeOptions(outputs, options); diff --git a/src/transforms/dodge.js b/src/transforms/dodge.js index 513a7b1efa..070d9bbb70 100644 --- a/src/transforms/dodge.js +++ b/src/transforms/dodge.js @@ -15,17 +15,7 @@ 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 - */ +/** @jsdoc dodgeX */ export function dodgeX(dodgeOptions = {}, options = {}) { if (arguments.length === 1) [dodgeOptions, options] = mergeOptions(dodgeOptions); let {anchor = "left", padding = 1} = maybeAnchor(dodgeOptions); @@ -45,17 +35,7 @@ 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 - */ +/** @jsdoc dodgeY */ export function dodgeY(dodgeOptions = {}, options = {}) { if (arguments.length === 1) [dodgeOptions, options] = mergeOptions(dodgeOptions); let {anchor = "bottom", padding = 1} = maybeAnchor(dodgeOptions); diff --git a/src/transforms/group.js b/src/transforms/group.js index 5aa82be38a..788467430d 100644 --- a/src/transforms/group.js +++ b/src/transforms/group.js @@ -32,31 +32,13 @@ import { } from "../options.js"; import {basic} from "./basic.js"; -/** - * ```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 - */ +/** @jsdoc groupZ */ export function groupZ(outputs, options) { // Group on {z, fill, stroke}. return groupn(null, null, outputs, options); } -/** - * ```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 - */ +/** @jsdoc groupX */ export function groupX(outputs = {y: "count"}, options = {}) { // Group on {z, fill, stroke}, then on x. const {x = identity} = options; @@ -64,15 +46,7 @@ export function groupX(outputs = {y: "count"}, options = {}) { return groupn(x, null, outputs, options); } -/** - * ```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 - */ +/** @jsdoc groupY */ export function groupY(outputs = {x: "count"}, options = {}) { // Group on {z, fill, stroke}, then on y. const {y = identity} = options; @@ -80,16 +54,7 @@ export function groupY(outputs = {x: "count"}, options = {}) { return groupn(null, y, outputs, options); } -/** - * ```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 - */ +/** @jsdoc group */ export function group(outputs = {fill: "count"}, options = {}) { // Group on {z, fill, stroke}, then on x and y. let {x, y} = options; diff --git a/src/transforms/hexbin.js b/src/transforms/hexbin.js index ebd0e50dc8..ad67601fea 100644 --- a/src/transforms/hexbin.js +++ b/src/transforms/hexbin.js @@ -12,46 +12,7 @@ import {hasOutput, maybeGroup, maybeOutputs, maybeSubgroup} from "./group.js"; 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. - * - * 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. - */ +/** @jsdoc hexbin */ export function hexbin(outputs = {fill: "count"}, options = {}) { // TODO filter e.g. to show empty hexbins? // TODO disallow x, x1, x2, y, y1, y2 reducers? diff --git a/src/transforms/map.js b/src/transforms/map.js index b62ea564c0..db0365f5d2 100644 --- a/src/transforms/map.js +++ b/src/transforms/map.js @@ -2,16 +2,7 @@ import {count, group, rank} from "d3"; import {maybeZ, take, valueof, maybeInput, column} from "../options.js"; import {basic} from "./basic.js"; -/** - * ```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 - */ +/** @jsdoc mapX */ export function mapX(map, options = {}) { return mapAlias( Object.fromEntries(["x", "x1", "x2"].filter((key) => options[key] != null).map((key) => [key, map])), @@ -19,16 +10,7 @@ export function mapX(map, 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 - */ +/** @jsdoc mapY */ export function mapY(map, options = {}) { return mapAlias( Object.fromEntries(["y", "y1", "y2"].filter((key) => options[key] != null).map((key) => [key, map])), @@ -36,18 +18,7 @@ export function mapY(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 - */ +/** @jsdoc map */ export function map(outputs = {}, options = {}) { const z = maybeZ(options); const channels = Object.entries(outputs).map(([key, map]) => { diff --git a/src/transforms/normalize.js b/src/transforms/normalize.js index a997095ab9..d5b1ca4181 100644 --- a/src/transforms/normalize.js +++ b/src/transforms/normalize.js @@ -3,42 +3,19 @@ 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*. - */ +/** @jsdoc normalizeX */ 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*. - */ +/** @jsdoc normalizeY */ 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. - */ +/** @jsdoc normalize */ export function normalize(basis) { if (basis === undefined) return normalizeFirst; if (typeof basis === "function") return normalizeBasis((I, S) => basis(take(S, I))); diff --git a/src/transforms/select.js b/src/transforms/select.js index 5f972851d3..27f9557d3a 100644 --- a/src/transforms/select.js +++ b/src/transforms/select.js @@ -2,45 +2,7 @@ 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"}) - * ``` - */ +/** @jsdoc select */ 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. @@ -78,44 +40,32 @@ function maybeSelector(selector) { throw new Error(`unknown selector: ${selector}`); } -/** - * Selects the first point of each series according to input order. - */ +/** @jsdoc selectFirst */ export function selectFirst(options) { return selectChannel(null, selectorFirst, options); } -/** - * Selects the last point of each series according to input order. - */ +/** @jsdoc selectLast */ export function selectLast(options) { return selectChannel(null, selectorLast, options); } -/** - * Selects the leftmost point of each series. - */ +/** @jsdoc selectMinX */ export function selectMinX(options) { return selectChannel("x", selectorMin, options); } -/** - * Selects the lowest point of each series. - */ +/** @jsdoc selectMinY */ export function selectMinY(options) { return selectChannel("y", selectorMin, options); } -/** - * Selects the rightmost point of each series. - */ +/** @jsdoc selectMaxX */ export function selectMaxX(options) { return selectChannel("x", selectorMax, options); } -/** - * Selects the highest point of each series. - */ +/** @jsdoc selectMaxY */ export function selectMaxY(options) { return selectChannel("y", selectorMax, options); } diff --git a/src/transforms/stack.js b/src/transforms/stack.js index 7018ff5ce9..7cfe6e5fef 100644 --- a/src/transforms/stack.js +++ b/src/transforms/stack.js @@ -3,16 +3,7 @@ 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 - */ +/** @jsdoc stackX */ export function stackX(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x, ...rest} = options; // note: consumes x! @@ -20,18 +11,7 @@ export function stackX(stack = {}, 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](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 - */ +/** @jsdoc stackX1 */ export function stackX1(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x} = options; @@ -39,18 +19,7 @@ export function stackX1(stack = {}, options = {}) { return {...transform, y1, y: Y, x: X}; } -/** - * ```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. - * - * @link https://github.com/observablehq/plot/blob/main/README.md#stack - */ +/** @jsdoc stackX2 */ export function stackX2(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {y1, y = y1, x} = options; @@ -58,21 +27,7 @@ export function stackX2(stack = {}, 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 - */ +/** @jsdoc stackY */ export function stackY(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y, ...rest} = options; // note: consumes y! @@ -80,18 +35,7 @@ export function stackY(stack = {}, 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](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 - */ +/** @jsdoc stackY1 */ export function stackY1(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y} = options; @@ -99,18 +43,7 @@ export function stackY1(stack = {}, options = {}) { return {...transform, x1, x: X, y: Y}; } -/** - * ```js - * 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. - * - * @link https://github.com/observablehq/plot/blob/main/README.md#stack - */ +/** @jsdoc stackY2 */ export function stackY2(stack = {}, options = {}) { if (arguments.length === 1) [stack, options] = mergeOptions(stack); const {x1, x = x1, y} = options; diff --git a/src/transforms/tree.js b/src/transforms/tree.js index 830d6a5c6c..a485cd2a4c 100644 --- a/src/transforms/tree.js +++ b/src/transforms/tree.js @@ -3,30 +3,7 @@ import {ascendingDefined} from "../defined.js"; 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). - * - * 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. - */ +/** @jsdoc treeNode */ export function treeNode(options = {}) { let { path = identity, // the delimited path @@ -80,39 +57,7 @@ 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). - * - * 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. - */ +/** @jsdoc treeLink */ export function treeLink(options = {}) { let { path = identity, // the delimited path diff --git a/tsconfig.json b/tsconfig.json index c14e12eff1..21fa249eca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "lib": ["esnext", "dom"], "strict": true, "stripInternal": true, - "outDir": "dist", + "outDir": "build", "allowJs": true, "declaration": true, "declarationDir": "types", diff --git a/yarn.lock b/yarn.lock index b28b267a1c..be2639481a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -225,14 +225,6 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-typescript@^8.3.2": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" - integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== - dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" - "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -252,16 +244,6 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@ts-morph/common@~0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.16.0.tgz#57e27d4b3fd65a4cd72cb36679ed08acb40fa3ba" - integrity sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw== - dependencies: - fast-glob "^3.2.11" - minimatch "^5.1.0" - mkdirp "^1.0.4" - path-browserify "^1.0.1" - "@types/d3-array@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.3.tgz#87d990bf504d14ad6b16766979d04e943c046dac" @@ -890,11 +872,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -code-block-writer@^11.0.0: - version "11.0.3" - resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.3.tgz#9eec2993edfb79bfae845fbc093758c0a0b73b76" - integrity sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1661,7 +1638,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.11, fast-glob@^3.2.9: +fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== @@ -2304,7 +2281,7 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.0: +minimatch@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== @@ -2497,11 +2474,6 @@ parse5@6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -path-browserify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2949,24 +2921,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -ts-morph@^15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-15.1.0.tgz#53deea5296d967ff6eba8f15f99d378aa7074a4e" - integrity sha512-RBsGE2sDzUXFTnv8Ba22QfeuKbgvAGJFuTN7HfmIRUkgT/NaVLfDM/8OFm2NlFkGlWEXdpW5OaFIp1jvqdDuOg== - dependencies: - "@ts-morph/common" "~0.16.0" - code-block-writer "^11.0.0" - tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"