Skip to content

dense interval for line and area #792

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,14 @@ 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.

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.

#### Plot.areaY(*data*, *options*)

```js
Expand All @@ -717,6 +725,14 @@ 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.

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.

### Arrow

[<img src="./img/arrow.png" width="320" height="198" alt="a scatterplot with arrows">](https://observablehq.com/@observablehq/plot-arrow)
Expand Down Expand Up @@ -1004,15 +1020,31 @@ Returns a new line with the given *data* and *options*. If neither the **x** nor
Plot.lineX(aapl.map(d => d.Close))
```

Equivalent 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.

```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.

#### Plot.lineY(*data*, *options*)

```js
Plot.lineY(aapl.map(d => d.Close))
```

Equivalent 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.

```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.

### Link

Expand Down
11 changes: 7 additions & 4 deletions src/marks/area.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Curve} from "../curve.js";
import {Mark} from "../plot.js";
import {first, indexOf, maybeZ, second} from "../options.js";
import {applyDirectStyles, applyIndirectStyles, applyTransform, applyGroupedChannelStyles, groupIndex} from "../style.js";
import {maybeDenseIntervalX, maybeDenseIntervalY} from "../transforms/bin.js";
import {maybeIdentityX, maybeIdentityY} from "../transforms/identity.js";
import {maybeStackX, maybeStackY} from "../transforms/stack.js";

Expand Down Expand Up @@ -60,10 +61,12 @@ export function area(data, options) {
return new Area(data, options);
}

export function areaX(data, {y = indexOf, ...options} = {}) {
return new Area(data, maybeStackX(maybeIdentityX({...options, y1: y, y2: undefined})));
export function areaX(data, options) {
const {y = indexOf, ...rest} = maybeDenseIntervalY(options);
return new Area(data, maybeStackX(maybeIdentityX({...rest, y1: y, y2: undefined})));
}

export function areaY(data, {x = indexOf, ...options} = {}) {
return new Area(data, maybeStackY(maybeIdentityY({...options, x1: x, x2: undefined})));
export function areaY(data, options) {
const {x = indexOf, ...rest} = maybeDenseIntervalX(options);
return new Area(data, maybeStackY(maybeIdentityY({...rest, x1: x, x2: undefined})));
}
5 changes: 3 additions & 2 deletions src/marks/line.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Curve} from "../curve.js";
import {Mark} from "../plot.js";
import {indexOf, identity, maybeTuple, maybeZ} from "../options.js";
import {applyDirectStyles, applyIndirectStyles, applyTransform, applyGroupedChannelStyles, offset, groupIndex} from "../style.js";
import {maybeDenseIntervalX, maybeDenseIntervalY} from "../transforms/bin.js";
import {applyGroupedMarkers, markers} from "./marker.js";

const defaults = {
Expand Down Expand Up @@ -60,9 +61,9 @@ export function line(data, {x, y, ...options} = {}) {
}

export function lineX(data, {x = identity, y = indexOf, ...options} = {}) {
return new Line(data, {...options, x, y});
return new Line(data, maybeDenseIntervalY({...options, x, y}));
}

export function lineY(data, {x = indexOf, y = identity, ...options} = {}) {
return new Line(data, {...options, x, y});
return new Line(data, maybeDenseIntervalX({...options, x, y}));
}
14 changes: 13 additions & 1 deletion src/transforms/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {bin as binner, extent, thresholdFreedmanDiaconis, thresholdScott, thresh
import {valueof, range, identity, maybeLazyChannel, maybeTuple, maybeColorChannel, maybeValue, mid, labelof, isTemporal} from "../options.js";
import {coerceDate, coerceNumber} from "../scales.js";
import {basic} from "./basic.js";
import {hasOutput, maybeEvaluator, maybeGroup, maybeOutput, maybeOutputs, maybeReduce, maybeSort, maybeSubgroup, reduceCount, reduceIdentity} from "./group.js";
import {hasOutput, maybeEvaluator, maybeGroup, maybeOutput, maybeOutputs, maybeReduce, maybeSort, maybeSubgroup, reduceCount, reduceFirst, reduceIdentity} from "./group.js";
import {maybeInsetX, maybeInsetY} from "./inset.js";
import {maybeInterval} from "./interval.js";

Expand All @@ -27,6 +27,18 @@ export function bin(outputs = {fill: "count"}, options = {}) {
return binn(x, y, null, null, outputs, maybeInsetX(maybeInsetY(options)));
}

function maybeDenseInterval(bin, k, options) {
return options?.interval == null ? options : bin({[k]: options?.reduce === undefined ? reduceFirst : options.reduce, filter: null}, options);
}

export function maybeDenseIntervalX(options) {
return maybeDenseInterval(binX, "y", options);
}

export function maybeDenseIntervalY(options) {
return maybeDenseInterval(binY, "x", options);
}

function binn(
bx, // optionally bin on x (exclusive with gx)
by, // optionally bin on y (exclusive with gy)
Expand Down
2 changes: 1 addition & 1 deletion src/transforms/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export const reduceIdentity = {
}
};

const reduceFirst = {
export const reduceFirst = {
reduce(I, X) {
return X[I[0]];
}
Expand Down
Loading