From 556bccad4a51a197d0d1560e3f802c4b8f53eba0 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 18 Jan 2022 08:48:41 -0800 Subject: [PATCH] propagate title and href by default (#674) * propagate title and href by default * DRY * count other * better other summarization --- src/transforms/group.js | 23 +++++++-- test/output/penguinMassSpecies.svg | 77 +++++++++++++++++++++++------- test/plots/penguin-mass-species.js | 2 +- 3 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/transforms/group.js b/src/transforms/group.js index c2da19acc6..32f6cb0049 100644 --- a/src/transforms/group.js +++ b/src/transforms/group.js @@ -1,6 +1,6 @@ -import {group as grouper, sort, sum, deviation, min, max, mean, median, mode, variance, InternSet, minIndex, maxIndex} from "d3"; +import {group as grouper, sort, sum, deviation, min, max, mean, median, mode, variance, InternSet, minIndex, maxIndex, rollup} from "d3"; import {ascendingDefined, firstof} from "../defined.js"; -import {valueof, maybeColorChannel, maybeInput, maybeTuple, maybeLazyChannel, lazyChannel, first, identity, take, labelof, range} from "../options.js"; +import {valueof, maybeColorChannel, maybeInput, maybeTuple, maybeLazyChannel, lazyChannel, first, identity, take, labelof, range, second} from "../options.js"; import {basic} from "./basic.js"; // Group on {z, fill, stroke}. @@ -135,7 +135,11 @@ export function hasOutput(outputs, ...names) { } export function maybeOutputs(outputs, inputs) { - return Object.entries(outputs).map(([name, reduce]) => { + const entries = Object.entries(outputs); + // Propagate standard mark channels by default. + if (inputs.title != null && outputs.title === undefined) entries.push(["title", reduceTitle]); + if (inputs.href != null && outputs.href === undefined) entries.push(["href", reduceFirst]); + return entries.map(([name, reduce]) => { return reduce == null ? {name, initialize() {}, scope() {}, reduce() {}} : maybeOutput(name, reduce, inputs); @@ -266,6 +270,19 @@ const reduceFirst = { } }; +const reduceTitle = { + reduce(I, X) { + const n = 5; + const groups = sort(rollup(I, V => V.length, i => X[i]), second); + const top = groups.slice(-n).reverse(); + if (top.length < groups.length) { + const bottom = groups.slice(0, 1 - n); + top[n - 1] = [`… ${bottom.length.toLocaleString("en-US")} more`, sum(bottom, second)]; + } + return top.map(([key, value]) => `${key} (${value.toLocaleString("en-US")})`).join("\n"); + } +}; + const reduceLast = { reduce(I, X) { return X[I[I.length - 1]]; diff --git a/test/output/penguinMassSpecies.svg b/test/output/penguinMassSpecies.svg index 997f981cf2..5c84da473e 100644 --- a/test/output/penguinMassSpecies.svg +++ b/test/output/penguinMassSpecies.svg @@ -84,22 +84,67 @@ Body mass (g) → - - - - - - - - - - - - - - - - + + Adelie FEMALE (6) + Adelie null (1) + + + Adelie FEMALE (42) + Adelie MALE (3) + Adelie null (2) + + + Adelie MALE (32) + Adelie FEMALE (25) + Adelie null (1) + + + Adelie MALE (30) + Adelie null (1) + + + Adelie MALE (8) + + + Chinstrap FEMALE (2) + + + Chinstrap FEMALE (11) + Chinstrap MALE (4) + + + Chinstrap FEMALE (20) + Chinstrap MALE (15) + + + Chinstrap MALE (12) + Chinstrap FEMALE (1) + + + Chinstrap MALE (3) + + + Gentoo FEMALE (1) + + + Gentoo FEMALE (14) + Gentoo null (1) + + + Gentoo FEMALE (35) + Gentoo null (3) + Gentoo MALE (2) + + + Gentoo MALE (26) + Gentoo FEMALE (8) + + + Gentoo MALE (29) + + + Gentoo MALE (4) + diff --git a/test/plots/penguin-mass-species.js b/test/plots/penguin-mass-species.js index e2089acfa2..375e3037c9 100644 --- a/test/plots/penguin-mass-species.js +++ b/test/plots/penguin-mass-species.js @@ -12,7 +12,7 @@ export default async function() { grid: true }, marks: [ - Plot.rectY(data, Plot.binX({y: "count"}, {x: "body_mass_g", fill: "species"})), + Plot.rectY(data, Plot.binX({y: "count"}, {x: "body_mass_g", fill: "species", title: d => `${d.species} ${d.sex}`})), Plot.ruleY([0]) ] });