Skip to content

preserve group input order #1959

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
Dec 27, 2023
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
1 change: 1 addition & 0 deletions docs/transforms/bin.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ The following named reducers are supported:
* *y* - the middle of the bin’s *y* extent (when binning on *y*)
* *y1* - the lower bound of the bin’s *y* extent (when binning on *y*)
* *y2* - the upper bound of the bin’s *y* extent (when binning on *y*)
* *z* <VersionBadge pr="1959" /> - the bin’s *z* value (*z*, *fill*, or *stroke*)

In addition, a reducer may be specified as:

Expand Down
3 changes: 2 additions & 1 deletion docs/transforms/group.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Although barX applies an implicit stackX transform, [textX](../marks/text.md) do

## Group options

Given input *data* = [*d₀*, *d₁*, *d₂*, …], by default the resulting grouped data is an array of arrays where each inner array is a subset of the input data such as [[*d₁*, *d₂*, …], [*d₀*, …], …]. Each inner array is in input order. The outer array is in natural ascending order according to the associated dimension (*x* then *y*).
Given input *data* = [*d₀*, *d₁*, *d₂*, …], by default the resulting grouped data is an array of arrays where each inner array is a subset of the input data such as [[*d₁*, *d₂*, …], [*d₀*, …], …]. Each inner array is in input order. The outer array is in input order according to the first element of each group.

By specifying a different reducer for the **data** output, as described below, you can change how the grouped data is computed. The outputs may also include **filter** and **sort** options specified as reducers, and a **reverse** option to reverse the order of generated groups. By default, empty groups are omitted, and non-empty groups are generated in ascending (natural) order.

Expand Down Expand Up @@ -370,6 +370,7 @@ The following named reducers are supported:
* *identity* - the array of values
* *x* <VersionBadge version="0.6.12" pr="1916" /> - the group’s *x* value (when grouping on *x*)
* *y* <VersionBadge version="0.6.12" pr="1916" /> - the group’s *y* value (when grouping on *y*)
* *z* <VersionBadge pr="1959" /> - the group’s *z* value (*z*, *fill*, or *stroke*)

In addition, a reducer may be specified as:

Expand Down
4 changes: 3 additions & 1 deletion src/transforms/bin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export interface BinOptions {
* - *y* - the middle of the bin’s **y** extent (when binning on **y**)
* - *y1* - the lower bound of the bin’s **y** extent (when binning on **y**)
* - *y2* - the upper bound of the bin’s **y** extent (when binning on **y**)
* - *z* - the bin’s **z** value (when grouping on **z**, **fill**, or **stroke**)
* - a function that takes an array of values and returns the reduced value
* - an object that implements the *reduceIndex* method
*
Expand All @@ -132,7 +133,8 @@ export type BinReducer =
| "x2"
| "y"
| "y1"
| "y2";
| "y2"
| "z";

/**
* A shorthand functional bin reducer implementation: given an array of input
Expand Down
8 changes: 6 additions & 2 deletions src/transforms/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import {
maybeSubgroup,
reduceCount,
reduceFirst,
reduceIdentity
reduceIdentity,
reduceZ
} from "./group.js";
import {maybeInsetX, maybeInsetY} from "./inset.js";

Expand Down Expand Up @@ -180,6 +181,7 @@ function binn(
for (const [f, I] of maybeGroup(facet, G)) {
for (const [k, g] of maybeGroup(I, K)) {
for (const [b, extent] of bin(g)) {
if (G) extent.z = f;
if (filter && !filter.reduce(b, extent)) continue;
groupFacet.push(i++);
groupData.push(reduceData.reduceIndex(b, data, extent));
Expand All @@ -190,7 +192,7 @@ function binn(
if (BX1) BX1.push(extent.x1), BX2.push(extent.x2);
if (BY1) BY1.push(extent.y1), BY2.push(extent.y2);
for (const o of outputs) o.reduce(b, extent);
if (sort) sort.reduce(b);
if (sort) sort.reduce(b, extent);
}
}
}
Expand Down Expand Up @@ -355,6 +357,8 @@ function maybeBinReduceFallback(reduce) {
return reduceY1;
case "y2":
return reduceY2;
case "z":
return reduceZ;
}
throw new Error(`invalid bin reduce: ${reduce}`);
}
Expand Down
3 changes: 2 additions & 1 deletion src/transforms/group.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ export interface GroupOutputOptions<T = Reducer> {
* - a generic reducer name, such as *count* or *first*
* - *x* - the group’s **x** value (when grouping on **x**)
* - *y* - the group’s **y** value (when grouping on **y**)
* - *z* - the group’s **z** value (when grouping on **z**, **fill**, or **stroke**)
* - a function that takes an array of values and returns the reduced value
* - an object that implements the *reduceIndex* method
*
* When a reducer function or implementation is used with the group transform,
* it is passed the group extent {x, y} as an additional argument.
*/
export type GroupReducer = Reducer | GroupReducerFunction | GroupReducerImplementation | "x" | "y";
export type GroupReducer = Reducer | GroupReducerFunction | GroupReducerImplementation | "x" | "y" | "z";

/**
* A shorthand functional group reducer implementation: given an array of input
Expand Down
17 changes: 10 additions & 7 deletions src/transforms/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
import {ascendingDefined} from "../defined.js";
import {
column,
first,
identity,
isObject,
isTemporal,
Expand Down Expand Up @@ -137,6 +136,7 @@ function groupn(
const extent = {data};
if (X) extent.x = x;
if (Y) extent.y = y;
if (G) extent.z = f;
if (filter && !filter.reduce(g, extent)) continue;
groupFacet.push(i++);
groupData.push(reduceData.reduceIndex(g, data, extent));
Expand Down Expand Up @@ -230,12 +230,7 @@ export function maybeEvaluator(name, reduce, inputs, asReduce = maybeReduce) {
}

export function maybeGroup(I, X) {
return X
? sort(
grouper(I, (i) => X[i]),
first
)
: [[, I]];
return X ? grouper(I, (i) => X[i]) : [[, I]];
}

export function maybeReduce(reduce, value, fallback = invalidReduce) {
Expand Down Expand Up @@ -309,6 +304,8 @@ function maybeGroupReduceFallback(reduce) {
return reduceX;
case "y":
return reduceY;
case "z":
return reduceZ;
}
throw new Error(`invalid group reduce: ${reduce}`);
}
Expand Down Expand Up @@ -437,6 +434,12 @@ const reduceY = {
}
};

export const reduceZ = {
reduceIndex(I, X, {z}) {
return z;
}
};

export function find(test) {
if (typeof test !== "function") throw new Error(`invalid test function: ${test}`);
return {
Expand Down
32 changes: 16 additions & 16 deletions test/output/athletesBirthdays.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading