Skip to content

Commit

Permalink
checkpoint docs
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Oct 24, 2023
1 parent 0775c7e commit 79ceefe
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 19 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default defineConfig({
{text: "Contour", link: "/marks/contour"},
{text: "Delaunay", link: "/marks/delaunay"},
{text: "Density", link: "/marks/density"},
{text: "Difference", link: "/marks/difference"},
{text: "Dot", link: "/marks/dot"},
{text: "Frame", link: "/marks/frame"},
{text: "Geo", link: "/marks/geo"},
Expand Down
96 changes: 96 additions & 0 deletions docs/marks/difference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script setup>

import * as Plot from "@observablehq/plot";
import * as d3 from "d3";
import {ref, shallowRef, onMounted} from "vue";

const shift = ref(0);
const aapl = shallowRef([]);
const goog = shallowRef([]);

onMounted(() => {
d3.csv("../data/aapl.csv", d3.autoType).then((data) => (aapl.value = data));
d3.csv("../data/goog.csv", d3.autoType).then((data) => (goog.value = data));
});

const offset = (date) => d3.utcDay.offset(date, shift.value);

</script>

# Difference mark

The **difference mark** compares a primary metric to a secondary metric.

:::plot
```js
Plot.plot({
y: {grid: true},
marks: [
Plot.ruleY([1]),
Plot.differenceY(aapl, Plot.normalizeY({
x: "Date",
y1: "Close",
y2: Plot.valueof(goog, "Close"),
tip: true
}))
]
})
```
:::

:::plot
```js
Plot.plot({
y: {grid: true},
marks: [
Plot.differenceY(aapl, {
x1: "Date",
x2: (d) => d3.utcYear.offset(d.Date),
y: "Close",
tip: true
})
]
})
```
:::

<p>
<label class="label-input" style="display: flex;">
<span style="display: inline-block; width: 7em;">Shift:</span>
<input type="range" v-model.number="shift" min="0" max="1000" step="1">
<span style="font-variant-numeric: tabular-nums;">{{shift}}</span>
</label>
</p>

:::plot
```js
Plot.plot({
x: {label: "Date"},
y: {grid: true},
marks: [
Plot.differenceY(aapl, {
x1: (d, i, data) => d.Date < offset(data[0].Date) ? null : d.Date,
x2: (d, i, data) => data.at(-1).Date < offset(d.Date) ? null : offset(d.Date),
y: "Close"
})
]
})
```
:::

:::plot
```js
Plot.plot({
x: {label: "Date"},
y: {grid: true},
marks: [
Plot.differenceY(aapl, {
x: (d, i) => i < shift ? null : d.Date,
y1: "Close",
y2: (d, i) => aapl[i - shift]?.Close,
tip: true
})
]
})
```
:::
6 changes: 3 additions & 3 deletions src/marks/difference.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function differenceY(
y2,
fill: positiveColor,
fillOpacity: positiveOpacity,
render: composeRender(render, renderDifference(true)),
render: composeRender(render, clipDifference(true)),
...options
}),
{ariaLabel: "positive difference"}
Expand All @@ -50,7 +50,7 @@ export function differenceY(
y2,
fill: negativeColor,
fillOpacity: negativeOpacity,
render: composeRender(render, renderDifference(false)),
render: composeRender(render, clipDifference(false)),
...options
}),
{ariaLabel: "negative difference"}
Expand Down Expand Up @@ -95,7 +95,7 @@ function memo(v) {
return {transform: (data) => V || (V = valueof(data, value)), label};
}

function renderDifference(positive) {
function clipDifference(positive) {
return (index, scales, channels, dimensions, context, next) => {
const clip = getClipId();
const clipPath = create("svg:clipPath", context).attr("id", clip).node();
Expand Down
34 changes: 18 additions & 16 deletions test/plots/difference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function differenceY() {
const x = aapl.map((d) => d.Date);
const y1 = aapl.map((d, i, data) => d.Close / data[0].Close);
const y2 = goog.map((d, i, data) => d.Close / data[0].Close);
return Plot.differenceY(aapl, {x: {value: x, label: "Date"}, y1: {value: y1, label: "Close"}, y2, tip: true}).plot();
return Plot.differenceY(aapl, {x, y1: {value: y1, label: "Close"}, y2, tip: true}).plot();
}

export async function differenceYRandom() {
Expand Down Expand Up @@ -48,21 +48,23 @@ export async function differenceYVariable() {
// before and the year after the dataset, x1 and x2 are padded with NaN.
export async function differenceY1() {
const aapl = await d3.csv<any>("data/aapl.csv", d3.autoType);
const interval = d3.utcYear;
const start = interval.offset(aapl[0].Date, 1);
const end = interval.offset(aapl[aapl.length - 1].Date, -1);
const x1 = aapl.map((d) => (d.Date < start ? NaN : d.Date));
const x2 = aapl.map((d) => (d.Date > end ? NaN : interval.offset(d.Date, 1)));
const y = aapl.map((d) => d.Close);
return Plot.differenceY(aapl, {
x1,
x2,
y,
positiveOpacity: 0.2,
positiveColor: "currentColor",
negativeOpacity: 0.8,
negativeColor: "red"
}).plot();
return Plot.differenceY(aapl, shiftX(d3.utcYear, {x: "Date", y: "Close"})).plot({y: {grid: true}});
}

function shiftX(interval, options) {
return Plot.map(
{
x1(D) {
const min = interval.offset(d3.min(D), 1);
return D.map((d) => (d < min ? null : d));
},
x2(D) {
const max = interval.offset(d3.max(D), -1);
return D.map((d) => (max < d ? null : interval.offset(d, 1)));
}
},
options
);
}

export async function differenceFilterX() {
Expand Down

0 comments on commit 79ceefe

Please sign in to comment.