From f164bd09502058dc981ef53e928a58881732ab58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 1 May 2023 15:37:12 -0700 Subject: [PATCH 1/6] document interval aware transforms --- docs/features/scales.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/features/scales.md b/docs/features/scales.md index 18aae3bbc0..0df2486455 100644 --- a/docs/features/scales.md +++ b/docs/features/scales.md @@ -600,10 +600,6 @@ Plot.plot({ ``` ::: -:::warning CAUTION -[Mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning). In this case the mark transforms will see the values prior to the scale transform as input, and the scale transform will apply to the *output* of the mark transform. -::: - The **interval** scale option sets an ordinal scale’s **domain** to the start of every interval within the extent of the data. In addition, it implicitly sets the **transform** of the scale to *interval*.floor, rounding values down to the start of each interval. For example, below we generate a time-series bar chart; when an **interval** is specified, missing days are visible.

@@ -634,6 +630,8 @@ Plot.plot({ ``` ::: +While [mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning), they have access to the plot’s options and can decide to apply any of these options themselves. The stack and group transforms, in particular, are interval-aware: they will apply any scale interval before aggregating values. In any event, the scale transform will also apply to the *output* of the mark transform. + :::tip As an added bonus, the **fontVariant** and **type** options are no longer needed because Plot now understands that the *x* scale, despite being *ordinal*, represents daily observations. ::: From 19f9d0001dd8cfc7d924f11254871269b006eb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 1 May 2023 15:39:37 -0700 Subject: [PATCH 2/6] API doc --- docs/features/transforms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/transforms.md b/docs/features/transforms.md index aea8fb4891..999d8b1728 100644 --- a/docs/features/transforms.md +++ b/docs/features/transforms.md @@ -175,7 +175,7 @@ Plot.plot({ ``` ::: -The **transform** function is passed two arguments, *data* and *facets*, representing the mark’s data and facet indexes; it must then return a {*data*, *facets*} object with the transformed data and facet indexes. The *facets* are represented as a nested array of arrays such as [[0, 1, 3, …], [2, 5, 10, …], …]; each element in *facets* specifies the zero-based indexes of elements in *data* that are in a given facet (*i.e.*, have a distinct value in the associated *fx* or *fy* dimension). +The **transform** function is passed three arguments, *data*, *facets*, and *options* representing the mark’s data and facet indexes, and the plot’s options; it must then return a {*data*, *facets*} object with the transformed data and facet indexes. The *facets* are represented as a nested array of arrays such as [[0, 1, 3, …], [2, 5, 10, …], …]; each element in *facets* specifies the zero-based indexes of elements in *data* that are in a given facet (*i.e.*, have a distinct value in the associated *fx* or *fy* dimension). If the **transform** option is specified, it supersedes any basic transforms (*i.e.*, the **filter**, **sort** and **reverse** options are ignored). However, the **transform** option is rarely used directly; instead one of Plot’s built-in transforms are used, and these transforms automatically compose with the basic **filter**, **sort** and **reverse** transforms. From cbce8f09c3215fdb053f170c72499013cc53e374 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 1 May 2023 18:40:08 -0400 Subject: [PATCH 3/6] Update scales.md --- docs/features/scales.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/scales.md b/docs/features/scales.md index 0df2486455..2a9acbcc62 100644 --- a/docs/features/scales.md +++ b/docs/features/scales.md @@ -630,7 +630,7 @@ Plot.plot({ ``` ::: -While [mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning), they have access to the plot’s options and can decide to apply any of these options themselves. The stack and group transforms, in particular, are interval-aware: they will apply any scale interval before aggregating values. In any event, the scale transform will also apply to the *output* of the mark transform. +While [mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning), they have access to the plot’s options and can decide to apply any of these options themselves. The [stack](../transforms/stack.md), [bin](../transforms/bin.md), and [group](../transforms/group.md) transforms, in particular, are interval-aware: they will apply the scale interval before aggregating values. In any event, the scale transform will also apply to the *output* of the mark transform. :::tip As an added bonus, the **fontVariant** and **type** options are no longer needed because Plot now understands that the *x* scale, despite being *ordinal*, represents daily observations. From 7629928ad522eaa26d7a04099c5dbbf7185cff54 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 1 May 2023 15:44:39 -0700 Subject: [PATCH 4/6] restore caution --- docs/features/scales.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/features/scales.md b/docs/features/scales.md index 2a9acbcc62..e03857db0c 100644 --- a/docs/features/scales.md +++ b/docs/features/scales.md @@ -600,6 +600,10 @@ Plot.plot({ ``` ::: +:::warning CAUTION +[Mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning). In this case the mark transforms will see the values prior to the scale transform as input, and the scale transform will apply to the *output* of the mark transform. +::: + The **interval** scale option sets an ordinal scale’s **domain** to the start of every interval within the extent of the data. In addition, it implicitly sets the **transform** of the scale to *interval*.floor, rounding values down to the start of each interval. For example, below we generate a time-series bar chart; when an **interval** is specified, missing days are visible.

From fb265563f45670ab9f20691d15c312f21ac4135e Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Mon, 1 May 2023 15:48:24 -0700 Subject: [PATCH 5/6] describe interval-aware transforms --- docs/features/scales.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/scales.md b/docs/features/scales.md index e03857db0c..b0e1075e39 100644 --- a/docs/features/scales.md +++ b/docs/features/scales.md @@ -634,12 +634,12 @@ Plot.plot({ ``` ::: -While [mark transforms](./transforms.md) typically consume values *before* they are passed through scales (_e.g._, when binning), they have access to the plot’s options and can decide to apply any of these options themselves. The [stack](../transforms/stack.md), [bin](../transforms/bin.md), and [group](../transforms/group.md) transforms, in particular, are interval-aware: they will apply the scale interval before aggregating values. In any event, the scale transform will also apply to the *output* of the mark transform. - :::tip As an added bonus, the **fontVariant** and **type** options are no longer needed because Plot now understands that the *x* scale, despite being *ordinal*, represents daily observations. ::: +While the example above relies on the **interval** being promoted to the scale’s **transform**, the [stack](../transforms/stack.md), [bin](../transforms/bin.md), and [group](../transforms/group.md) transforms are also interval-aware: they apply the scale’s **interval**, if any, *before* grouping values. (This results in the interval being applied twice, both before and after the mark transform, but the second application has no effect since interval application is idempotent.) + The **interval** option can also be used for quantitative and temporal scales. This enforces uniformity, say rounding timed observations down to the nearest hour, which may be helpful for the [stack transform](../transforms/stack.md) among other uses. ## Scale options From 34e43c163c796a861ba2b48db10b4d1914f69f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 1 May 2023 16:04:05 -0700 Subject: [PATCH 6/6] types for custom transform functions --- src/transforms/basic.d.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/transforms/basic.d.ts b/src/transforms/basic.d.ts index 204acf39bc..3aee313b62 100644 --- a/src/transforms/basic.d.ts +++ b/src/transforms/basic.d.ts @@ -1,17 +1,22 @@ +import type {PlotOptions} from "../plot.js"; import type {ChannelName, Channels, ChannelValue} from "../channel.js"; import type {Context} from "../context.js"; import type {Dimensions} from "../dimensions.js"; import type {ScaleFunctions} from "../scales.js"; /** - * A mark transform function is passed the mark’s *data* and a nested index into - * the data, *facets*. The transform function returns new mark data and facets; - * the returned **data** defaults to the passed *data*, and the returned - * **facets** defaults to the passed *facets*. The mark is the *this* context. - * Transform functions can also trigger side-effects, say to populate - * lazily-derived columns; see also Plot.column. + * A mark transform function is passed the mark’s *data*, a nested index into + * the data, *facets*, and the plot’s *options*. The transform function returns + * new mark data and facets; the returned **data** defaults to the passed + * *data*, and the returned **facets** defaults to the passed *facets*. The mark + * is the *this* context. Transform functions can also trigger side-effects, say + * to populate lazily-derived columns; see also Plot.column. */ -export type TransformFunction = (data: any[], facets: number[][]) => {data?: any[]; facets?: number[][]}; +export type TransformFunction = ( + data: any[], + facets: number[][], + options?: PlotOptions +) => {data?: any[]; facets?: number[][]}; /** * A mark initializer function is passed the mark’s (possibly transformed)