Skip to content

Sort bars! - #106

Closed
mbostock wants to merge 3 commits into
mainfrom
mbostock/bar-sort
Closed

mbostock wants to merge 3 commits into
mainfrom
mbostock/bar-sort

Conversation

@mbostock

Copy link
Copy Markdown
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

Fil commented Jan 23, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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

Fil commented Jan 23, 2021

Copy link
Copy Markdown
Contributor

nice!

@mbostock

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

Fil commented Jan 23, 2021

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Member Author

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

@mbostock

Copy link
Copy Markdown
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