Skip to content

Commit 22fd001

Browse files
committed
x and y reducers for hexbin
1 parent d794d5e commit 22fd001

File tree

5 files changed

+301
-18
lines changed

5 files changed

+301
-18
lines changed

src/transforms/group.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ function invalidReduce(reduce) {
287287
throw new Error(`invalid reduce: ${reduce}`);
288288
}
289289

290-
function maybeGroupOutputs(outputs, inputs) {
290+
export function maybeGroupOutputs(outputs, inputs) {
291291
return maybeOutputs(outputs, inputs, maybeGroupOutput);
292292
}
293293

src/transforms/hexbin.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {ChannelReducers, ChannelValue} from "../channel.js";
22
import type {Initialized} from "./basic.js";
3+
import type {GroupReducer} from "./group.js";
34

45
/** Options for the hexbin transform. */
56
export interface HexbinOptions {
@@ -43,4 +44,4 @@ export interface HexbinOptions {
4344
*
4445
* To draw empty hexagons, see the hexgrid mark.
4546
*/
46-
export function hexbin<T>(outputs?: ChannelReducers, options?: T & HexbinOptions): Initialized<T>;
47+
export function hexbin<T>(outputs?: ChannelReducers<GroupReducer>, options?: T & HexbinOptions): Initialized<T>;

src/transforms/hexbin.js

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {map, number, valueof} from "../options.js";
22
import {applyPosition} from "../projection.js";
33
import {sqrt3} from "../symbol.js";
44
import {initializer} from "./basic.js";
5-
import {hasOutput, maybeGroup, maybeOutputs, maybeSubgroup} from "./group.js";
5+
import {hasOutput, maybeGroup, maybeGroupOutputs, maybeSubgroup} from "./group.js";
66

77
// We don’t want the hexagons to align with the edges of the plot frame, as that
88
// would cause extreme x-values (the upper bound of the default x-scale domain)
@@ -16,9 +16,8 @@ export function hexbin(outputs = {fill: "count"}, {binWidth, ...options} = {}) {
1616
const {z} = options;
1717

1818
// TODO filter e.g. to show empty hexbins?
19-
// TODO disallow x, x1, x2, y, y1, y2 reducers?
2019
binWidth = binWidth === undefined ? 20 : number(binWidth);
21-
outputs = maybeOutputs(outputs, options);
20+
outputs = maybeGroupOutputs(outputs, options);
2221

2322
// A fill output means a fill channel; declaring the channel here instead of
2423
// waiting for the initializer allows the mark constructor to determine that
@@ -65,15 +64,15 @@ export function hexbin(outputs = {fill: "count"}, {binWidth, ...options} = {}) {
6564
const binFacet = [];
6665
for (const o of outputs) o.scope("facet", facet);
6766
for (const [f, I] of maybeGroup(facet, G)) {
68-
for (const bin of hbin(I, X, Y, binWidth)) {
67+
for (const {index: b, extent} of hbin(data, I, X, Y, binWidth)) {
6968
binFacet.push(++i);
70-
BX.push(bin.x);
71-
BY.push(bin.y);
72-
if (Z) GZ.push(G === Z ? f : Z[bin[0]]);
73-
if (F) GF.push(G === F ? f : F[bin[0]]);
74-
if (S) GS.push(G === S ? f : S[bin[0]]);
75-
if (Q) GQ.push(G === Q ? f : Q[bin[0]]);
76-
for (const o of outputs) o.reduce(bin);
69+
BX.push(extent.x);
70+
BY.push(extent.y);
71+
if (Z) GZ.push(G === Z ? f : Z[b[0]]);
72+
if (F) GF.push(G === F ? f : F[b[0]]);
73+
if (S) GS.push(G === S ? f : S[b[0]]);
74+
if (Q) GQ.push(G === Q ? f : Q[b[0]]);
75+
for (const o of outputs) o.reduce(b, extent);
7776
}
7877
}
7978
binFacets.push(binFacet);
@@ -106,7 +105,7 @@ export function hexbin(outputs = {fill: "count"}, {binWidth, ...options} = {}) {
106105
});
107106
}
108107

109-
function hbin(I, X, Y, dx) {
108+
function hbin(data, I, X, Y, dx) {
110109
const dy = dx * (1.5 / sqrt3);
111110
const bins = new Map();
112111
for (const i of I) {
@@ -127,11 +126,10 @@ function hbin(I, X, Y, dx) {
127126
const key = `${pi},${pj}`;
128127
let bin = bins.get(key);
129128
if (bin === undefined) {
130-
bins.set(key, (bin = []));
131-
bin.x = (pi + (pj & 1) / 2) * dx + ox;
132-
bin.y = pj * dy + oy;
129+
bin = {index: [], extent: {data, x: (pi + (pj & 1) / 2) * dx + ox, y: pj * dy + oy}};
130+
bins.set(key, bin);
133131
}
134-
bin.push(i);
132+
bin.index.push(i);
135133
}
136134
return bins.values();
137135
}

test/output/hexbinFillX.svg

Lines changed: 273 additions & 0 deletions
Loading

test/plots/hexbin.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,14 @@ export async function hexbin() {
1111
]
1212
});
1313
}
14+
15+
export async function hexbinFillX() {
16+
const penguins = await d3.csv<any>("data/penguins.csv", d3.autoType);
17+
return Plot.plot({
18+
marks: [
19+
Plot.hexgrid(),
20+
Plot.frame(),
21+
Plot.dot(penguins, Plot.hexbin({r: "count", fill: "x"}, {x: "culmen_depth_mm", y: "culmen_length_mm"}))
22+
]
23+
});
24+
}

0 commit comments

Comments
 (0)