Skip to content

explicit facet option in a mark #450

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 13 commits into from
Jul 10, 2021
Merged

explicit facet option in a mark #450

merged 13 commits into from
Jul 10, 2021

Conversation

Fil
Copy link
Contributor

@Fil Fil commented Jul 8, 2021

you can enable or disable faceting explicitly on a mark with the facet option, which accepts the following values:

  • "auto" (default) - enable faceting if the mark's data is === to the facet data
  • null (or false) - disable faceting for this mark
  • "include" (or true) - enable faceting for this mark
  • "exclude" - enable exclusion faceting for this mark (each facet receives all the data except those of the facet’s subset)

See https://observablehq.com/@fil/plot-explicit-facet-option

@Fil Fil requested review from enjalot, zanarmstrong and mbostock July 8, 2021 19:53
Copy link
Member

@mbostock mbostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. I was thinking we should do something like this. My feedback is mostly on how to make the facet option more consistent with other Plot options.

src/mark.js Outdated
const names = new Set();
this.data = data;
this.facet = facet;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do some validation and coercion here.

I also think we can unify the types a little more. Currently:

  • "exclude" is a string,
  • null and undefined are equivalent, and
  • true and false are booleans.

If we want to be more analogous to the scale.axis option, perhaps:

  • "exclude"
  • "include", and true is shorthand for "include"
  • undefined is "include" if data is strict equal, and otherwise null to disable faceting
  • null or any other falsey value is mapped to null to disable faceting

Any other truthy value, after being coerced to a string, would throw an error. We’d use the keyword helper:

export function keyword(input, name, allowed) {

Fil and others added 2 commits July 9, 2021 19:31
Co-authored-by: Mike Bostock <[email protected]>
- "include" (shorthand: true)
- "exclude"
- null (or falsy)

undefined is the default test on data === facet.data.
@Fil Fil marked this pull request as ready for review July 9, 2021 19:41
src/facet.js Outdated
const markFacets = mark.facet == null ? mark.data === this.data ? facetsIndex : undefined
: mark.facet === "exclude" ? facetsIndex.map(facet => Uint32Array.from(difference(index, facet)))
: mark.facet === true ? facetsIndex
const facet = mark.facet && keyword(mark.facet, "facet", ["include", "exclude"]);
Copy link
Member

@mbostock mbostock Jul 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (validation and type coercion of the facet option via keyword) needs to happen in the mark constructor (as I said previously), not here in initialize.

Copy link
Contributor Author

@Fil Fil Jul 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I understood your comment just after pushing the previous commit 8-)
I think it's worth adding an "auto" option value (rather than undefined), which makes the code easier to read, and would maybe clarify the documentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I wasn’t sure whether we needed "auto" in addition to undefined, but sure, if you want.

src/facet.js Outdated
const facet = mark.facet && keyword(mark.facet, "facet", ["include", "exclude"]);
const markFacets = facet === undefined ? mark.data === this.data ? facetsIndex : undefined
: facet === "include" ? facetsIndex
: facet === "exclude" ? facetsIndex.map(f => Uint32Array.from(difference(index, f)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably shouldn’t bother with this optimization, but: this could be extracted out of the for loop using a lazily-constructed variable so that it can be shared across multiple excluded marks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's cheap so I added it too

adds an "auto" symbol as default value

computes the facet exclusion index only once.
Copy link
Member

@mbostock mbostock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@zanarmstrong
Copy link

Wow!

@Fil Fil merged commit 8aafa7c into main Jul 10, 2021
@Fil Fil deleted the fil/explicit-facets branch July 10, 2021 07:08
@mbostock mbostock modified the milestone: 0.2 Jul 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants