1

I am fairly new to using D3 and have created a stacked horizontal bar chart based off of https://observablehq.com/@d3/stacked-horizontal-bar-chart.

This is my chart:

Graph

I am currently able to sort the chart by ascending and descending. What I need to do now is to first sort the graph alphabetically by the y-axis label, then ascendingly by the value on the x-axis and I unsure how to do this.

The chart needs to look something like this: Graph2

Where the chart is sorted first alphabetically by the scale name on the y-axis, then descending or ascendingly by the value on the x-axis.

This is the code I use to generate the chart and sort it ascendingly/descendingly:

chartGenerate = stackedBarChart(pandas, 
  {
    x: d => d.value,
    y: d => d.scale,
    z: d => d.key,
    yDomain: d3.groupSort(pandas, 
      D => d3.sum(D, d => (order === "ascending" ? -d.value : d.value)), 
      d => d.scale),
    zDomain: key,
  }
)

Where value is the value of one of the bars within the big bar on the x-axis, scale is the label on the y-axis, and key is the array:

var key = ["punnets_underweight", "punnets_overweight", "punnets_on_weight"]

Order is a state that changes between values 'ascending' and 'descending' when a button is pressed.

Is there anyway to modify the groupSort function so that it can sort over 2 object properties? I might have left out a lot of information out because I wasn't sure how to phrase everything as a whole, but this is the core of my issue.

alanders
  • 35
  • 5

1 Answers1

1

You wrote:

What I need to do now is to first sort the graph alphabetically by the y-axis label, then ascendingly by the value on the x-axis

If I understand your example correctly, though, you don't want to sort on the complete y-label (for that would fully sort the yDomain) but just on the first portion - perhaps just up to Line ${n} or something. You then want to sort the within those groups based on size?

I'm sure this could be done with d3.groupSort, using a more general comparator as the second argument. Sometimes, I find it difficult to find that special incantation for more specialized, higher level functions, though. This can definitely be done with d3.group and then d3.sort.

Since I don't have access to your data, I forked the Observable notebook you referenced and built an example there. In the example, I broke the states up rather arbitrarily into the first half of the alphabet and then the second. Then, I sorted by population within those two groups. The code there looks like so:

// An Array of objects with {state, population} keys:
state_populations = Array.from(
  d3.group(stateages, (o) => o.state).values()
).map((a) => ({
  state: a[0].state,
  population: d3.sum(a, (o) => o.population)
}));

/// The yDomain
yDomain = d3
  .sort(
    state_populations,
    (s) => (s.state < "M" ? 1 : 0),
    (s) => s.population
  )
  .map((s) => s.state)

You can see more details in the notebook. Here's the top portion of the resulting image:

enter image description here

Mark McClure
  • 4,862
  • 21
  • 34