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:
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:
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.
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: