javascript - d3.js 堆叠条形图排序/更改堆栈中单个值的顺序
问题描述
在我之前的问题:d3.js 堆积条形图 - 修改堆栈顺序逻辑中,我了解到 d3 没有现成的解决方案来按堆栈中的单个值进行排序。D3stack.order()
只能对整个系列进行排序。我还了解到,我需要编写一个自定义函数来实现这一点。
在我进入这个函数之前,让我解释一下这里的动机。我想在总和旁边显示比例,这是 d3 堆叠条形图擅长的。但是,我希望重点放在排名上。这就是为什么我需要更改单个矩形在每个堆栈中的排序顺序,以反映排名的任何变化。最大值应该在栈顶,最小值应该出现在栈底。如上一篇文章所述,堆叠条形图的典型输出仅根据初始顺序将所有矩形集中到堆栈中。
再次代码供参考:
var margins = {
top: 100,
bottom: 300,
left: 100,
right: 100
};
var height = 600;
var width = 900;
var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(" + margins.left + "," + margins.top + ")");
var data = [{
period: 't1',
fmc1: 2,
fmc2: 5,
fmc3: 6,
fmc4: 9,
fmc5: 10
},
{
period: 't2',
fmc1: 3,
fmc2: 4,
fmc3: 9,
fmc4: 8,
fmc5: 11
},
{
period: 't3',
fmc1: 3,
fmc2: 5,
fmc3: 15,
fmc4: 12,
fmc5: 10
},
];
var groups = d3.map(data, function(d) {
return (d.period)
}).keys();
var subgroups = Object.keys(data[0]).slice(1);
var stackedData = d3.stack()
.keys(subgroups)
(data);
//console.log(stackedData);
var yScale = d3.scaleLinear()
.domain([0, 80])
.range([height, 0]);
var xScale = d3.scaleBand()
.domain(['t1', 't2', 't3'])
.range([0, width])
.padding([.5]);
var colorScale = d3.scaleOrdinal()
.domain(subgroups)
.range(["#003366", "#366092", "#4f81b9", "#95b3d7", "#b8cce4", "#e7eef8", "#a6a6a6", "#d9d9d9", "#ffffcc", "#f6d18b", "#e4a733", "#b29866", "#a6a6a6", "#d9d9d9", "#e7eef8", "#b8cce4", "#95b3d7", "#4f81b9", "#366092", "#003366"].reverse());
graphGroup.append("g")
.selectAll("g")
.data(stackedData)
.enter().append("g")
.attr("fill", function(d) {
return colorScale(d.key);
})
.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return xScale(d.data.period);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("height", function(d) {
return yScale(d[0]) - yScale(d[1]);
})
.attr("width", xScale.bandwidth());
<script src="https://d3js.org/d3.v5.min.js"></script>
调用后数据结构变得更加复杂,d3.stack()
这对我来说是一个挑战。该任务类似于基于嵌套数组中的对象值对整个数组数组进行排序。
我最好的尝试是:
var sortTest = stackedData.sort(stackedData[1].sort((a, b) => a[0] - b[1]));
哪个返回了错误:
排序函数必须是函数或未定义
结果证明这比我想象的更具挑战性,因此我尝试在堆栈之前对数据进行排序,但这导致堆栈不准确,关系丢失。
这个类似的问题带来了一线希望:对 d3.js 堆叠条形图进行排序
但是,由于该方法没有使用d3.stack()
,因此该解决方案不适用于我的特定情况或任何使用d3.stack()
.
问题
当数据为d3.stack()
格式时,按单个值对数据进行排序的解决方案是什么?
解决方案
我会使用自定义堆栈函数,如下所示。我试图获得与堆积条形图类似的数据结构,只是我按键而不是按索引对值进行分组。这样,我可以更轻松地对它们进行排序。
var margins = {
top: 30,
bottom: 50,
left: 30,
right: 30
};
var height = 300;
var width = 600;
var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(" + margins.left + "," + margins.top + ")");
var data = [{
period: 't1',
fmc1: 2,
fmc2: 5,
fmc3: 6,
fmc4: 9,
fmc5: 10
},
{
period: 't2',
fmc1: 3,
fmc2: 4,
fmc3: 9,
fmc4: 8,
fmc5: 11
},
{
period: 't3',
fmc1: 3,
fmc2: 5,
fmc3: 15,
fmc4: 12,
fmc5: 10
},
];
var groups = data.map(function(d) {
return d.period;
});
var subgroups = Object.keys(data[0]).slice(1);
var stackedData = data.map(function(d) {
var orderedKeys = subgroups.slice().sort(function(a, b) {
return d[a] - d[b];
});
var bottom = 0;
var result = orderedKeys.map(function(key) {
var value = d[key];
var result = [bottom, bottom + value];
result.data = d;
result.key = key;
bottom += value;
return result;
});
result.key = d.period;
return result;
});
console.log(stackedData);
var yScale = d3.scaleLinear()
.domain([0, 80])
.range([height, 0]);
var xScale = d3.scaleBand()
.domain(['t1', 't2', 't3'])
.range([0, width])
.padding([.5]);
var colorScale = d3.scaleOrdinal()
.domain(subgroups)
.range(["#003366", "#366092", "#4f81b9", "#95b3d7", "#b8cce4", "#e7eef8", "#a6a6a6", "#d9d9d9", "#ffffcc", "#f6d18b", "#e4a733", "#b29866", "#a6a6a6", "#d9d9d9", "#e7eef8", "#b8cce4", "#95b3d7", "#4f81b9", "#366092", "#003366"].reverse());
graphGroup.append("g")
.selectAll("g")
.data(stackedData)
.enter()
.append("g")
.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("fill", function(d) {
return colorScale(d.key);
})
.attr("x", function(d) {
return xScale(d.data.period);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("height", function(d) {
return yScale(d[0]) - yScale(d[1]);
})
.attr("width", xScale.bandwidth());
<script src="https://d3js.org/d3.v5.js"></script>
推荐阅读
- python - 如何使用 os.mkdir 在文件夹文件上编写 ñ 而不是奇怪的符号
- jenkins - Github 设置和 Jenkins 缺少预提交检查或远程运行
- apache-kafka - Kafka 控制台消费者在 HDP 3 中读取 avro 消息
- flutter - Flutter BottomNavigationBar 等待异步函数
- r - 是否有 R 函数可以使 x 行等于特定行并重复操作?
- angular - 与 *ngIf 一起显示时,mat-select 未设置值
- javascript - 如何使用分配的特定删除按钮删除数组中的特定对象
- c# - 如何用数据填充 DataGridView?
- python - ModuleNotFoundError:Flask 中没有名为“apscheduler”的模块
- javascript - 单击时如何防止按钮移动?