Skip to content

Commit

Permalink
dotbin
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Dec 5, 2020
1 parent 73840b0 commit 8832a80
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/defined.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ export function filter(index, ...channels) {
}
return index;
}

export function positive(x) {
return x > 0 ? x : NaN;
}
4 changes: 2 additions & 2 deletions src/marks/dot.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ascending} from "d3-array";
import {create} from "d3-selection";
import {filter, nonempty} from "../defined.js";
import {filter, nonempty, positive} from "../defined.js";
import {Mark, indexOf, identity, first, second, maybeColor, maybeNumber} from "../mark.js";
import {Style, applyDirectStyles, applyIndirectStyles, applyBandTransform} from "../style.js";

Expand Down Expand Up @@ -48,7 +48,7 @@ export class Dot extends Mark {
{x, y, r, color},
{x: X, y: Y, z: Z, r: R, title: L, fill: F, stroke: S}
) {
const index = filter(I, X, Y, R, F, S);
const index = filter(I, X, Y, F, S).filter(i => positive(R[i]));
if (Z) index.sort((i, j) => ascending(Z[i], Z[j]));
return create("svg:g")
.call(applyIndirectStyles, this)
Expand Down
5 changes: 4 additions & 1 deletion src/scales/quantitative.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
} from "d3-scale-chromatic";
import {scaleDiverging, scaleLinear, scaleLog, scalePow, scaleSymlog} from "d3-scale";
import {registry, radius, color} from "./index.js";
import {positive} from "../defined.js";

const constant = x => () => x;
const flip = i => t => i(1 - t);
Expand Down Expand Up @@ -213,9 +214,11 @@ function inferDomain(channels) {
];
}

// We don’t want the upper bound of the radial domain to be zero, as this would
// be degenerate, so we ignore nonpositive values.
function inferRadialDomain(channels) {
return [
0,
quantile(channels, 0.5, ({value}) => value === undefined ? value : quantile(value, 0.25))
quantile(channels, 0.5, ({value}) => value === undefined ? NaN : quantile(value, 0.25, positive))
];
}
13 changes: 7 additions & 6 deletions src/transforms/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ export function bin1(options = {}) {
}

export function bin2({x = {}, y = {}, domain, thresholds} = {}) {
if (isvalue(x)) x = {value: x};
if (isvalue(y)) y = {value: y};
const binX = bin1({domain, thresholds, value: first, ...x});
const binY = bin1({domain, thresholds, value: second, ...y});
const binX = bin1({domain, thresholds, value: first, ...maybeValue(x)});
const binY = bin1({domain, thresholds, value: second, ...maybeValue(y)});
return data => {
return cross(binX(data), binY(data).map(binset), (x, y) => {
return {
Expand All @@ -43,8 +41,11 @@ export function bin2({x = {}, y = {}, domain, thresholds} = {}) {
};
}

function isvalue(x) {
return typeof x === "string" || typeof x === "function";
// The value may be defined as a string or function, rather than an object with
// a value property. TODO Allow value to be specified as array, too? This would
// require promoting the array to an accessor for compatibility with d3.bin.
function maybeValue(x) {
return typeof x === "string" || typeof x === "function" ? {value: x} : x;
}

function binset(bin) {
Expand Down
32 changes: 32 additions & 0 deletions test/diamonds-carat-price-dots.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="https://cdn.jsdelivr.net/npm/d3@6"></script>
<script src="../dist/@observablehq/plot.umd.js"></script>
<script>

d3.csv("data/diamonds.csv", d3.autoType).then(data => {
document.body.appendChild(Plot.plot({
height: 640,
grid: true,
x: {
label: "Carats →"
},
y: {
label: "↑ Price ($)"
},
r: {
domain: [0, 100]
},
marks: [
Plot.dot(data, {
transform: Plot.bin2({x: "carat", y: "price", thresholds: 100}),
x: d => (d.x0 + d.x1) / 2,
y: d => (d.y0 + d.y1) / 2,
r: "length"
})
]
}));
});

</script>

0 comments on commit 8832a80

Please sign in to comment.