Skip to content

Sort bars! #106

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

Closed
wants to merge 3 commits into from
Closed

Sort bars! #106

wants to merge 3 commits into from

Conversation

mbostock
Copy link
Member

If you add sort = true (short for "descending") to Plot.barX or Plot.barY, the bars will now be automatically sorted by their upper value (y sorted by x2, or x sorted by y2, respectively). This can be used in conjunction with the Plot.groupX or Plot.groupY mark.

@mbostock mbostock requested review from Fil and shancarter January 23, 2021 04:22
@Fil
Copy link
Contributor

Fil commented Jan 23, 2021

Yes! Super useful!

Just for fun I've tried:

      Plot.barX(alphabet, {x: "frequency", y: "letter", sort: "ascending"}),
      Plot.barX(alphabet, {x: "frequency", y: "letter", sort: "descending"}),

As expected, the second mark encounters a y scale which already has a domain, and is not allowed to sort it differently. This is fine and logical (it's my approach also on setting the projection's extent in Plot.carto), but then I don't really get why we couldn't also have a "sort: as-is" option, where the domain would be in the order the data feeds into it? Also, is this a case in which we'd want to throw an error or a warning of some sort? (These comments are very secondary.)

@mbostock
Copy link
Member Author

Yeah, I agree it’s a little confusing that sort: false will still sort the inferred domain by default in natural order (just not sort the bars). I’ve thought about having a sort property on the channel, too. I may think a little more before landing.

@Fil
Copy link
Contributor

Fil commented Jan 23, 2021

nice!

@mbostock
Copy link
Member Author

I’ve added sort = "input" for indicating that you want the bars to appear in input order.

At first I thought it would be more appropriate to put a sort property on the scale definition, e.g., y: {sort: "input"} (or even y: {sort: null}). But then the trouble is that the order must be aggregated across multiple channels, so its behavior depends on the order of marks and the order of channels within each mark. You can’t always change the order of marks or channels, so it felt better to apply sort = "input" to a specific mark: you’re saying the input data of this specific mark is the one that determines the domain order.

This all seems good for bars, which have a more obvious interpretation of order (by bar length), but I realize now this is only a partial solution to the ordering problem: what if, for example, you want a dot mark to imply an order for an associated ordinal domain? With a bar, one dimension is always ordinal and the other quantitative, but with other marks there’s not always an obvious interpretation of “order”. And strictly speaking, even with bars, barY can have y1 and y2, and barY can be pointing up or down, so, there are other potential ambiguities.

Continuing…

@mbostock
Copy link
Member Author

I’m now wondering if this an ordinal domain’s order can be specified as part of the scale definition, but with some way to refer to a specific channel in another mark. For example, instead of:

Plot.plot({
  marks: [
    Plot.barX(alphabet, {x: "frequency", y: "letter", sort: "ascending"})
  ]
})

maybe you’d say

Plot.plot({
  y: {
    sort: "bar.x2"
  },
  marks: [
    Plot.barX(alphabet, {x: "frequency", y: "letter", name: "bar"})
  ]
})

which would mean “sort the y-scale’s domain based on the natural ascending order of the associated value in the bar mark’s x2 channel”. Because you need a way to refer to a specific channel on a specific mark. But of course it’s awkward that now you need to give a name to your marks, and also understand the internal implicit mapping from barX’s x channel to x1 and x2.

@mbostock
Copy link
Member Author

Another quick idea, perhaps all marks could take a sort object whose keys are scales and whose values are channel names?

Plot.plot({
  marks: [
    Plot.barX(alphabet, {x: "frequency", y: "letter", sort: {y: "x"}})
  ]
})

Though you’d need something like Vega-Lite’s -x to indicate descending order:

Plot.plot({
  marks: [
    Plot.barX(alphabet, {x: "frequency", y: "letter", sort: {y: "-x"}})
  ]
})

And Plot.barX would have to remap the "x" to "x2", but that’s certainly doable.

@Fil
Copy link
Contributor

Fil commented Jan 23, 2021

I'm content with sort = "input" ; with bars we're working on a derived structure (counts by x), so in a way it has simplified the question: we have a map x => count, and we can sort it as-is ("input" or "no sort"), by x (asc or desc), or by count (asc or desc). The only missing piece might be "input reversed".

@mbostock mbostock mentioned this pull request Jan 23, 2021
@mbostock
Copy link
Member Author

I posted the more general alternative approach in #108. Ruminating on that now. Would love feedback.

@mbostock
Copy link
Member Author

Closing in favor of #108.

@mbostock mbostock closed this Jan 23, 2021
@mbostock mbostock deleted the mbostock/bar-sort branch January 23, 2021 20:37
@Fil Fil mentioned this pull request Apr 28, 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.

2 participants