我正在使用堆叠条形图进行D3可视化。我正在尝试添加动画,以便读者可以按特定子栏的值的降序对所有栏进行排序,并且所选子栏最靠近轴。最终我想附上类似图片的东西。
每次我调用我的排序方法时,对条形进行整体排序都可以正常工作,但是对子条形进行排序以使所选度量最接近轴时,只能更新一次。我不知道为什么,但是我环顾四周,认为这可能与关键功能有关,但是我不确定如何将其与堆栈布局集成。
这就是现在的样子-如您所见,堆叠的条形图的实际顺序仅重新排列一次。
数据来自csv文件-这是我的代码:
var dataset;
var causes = ["f_trans_rate", "f_violence_rate", "f_fireExp_rate", "f_fallSlipTrip_rate", "f_exposure_rate", "f_contact_rate", "f_allOther_rate"]
var firstCause = 0;
var margin = { top: 20, right: 20, bottom: 30, left: 300 };
var width = 1000;
var height = 10000;
svg = d3.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var y = d3.scaleBand().range([0, height]) // set y scale
var x = d3.scaleLinear().range([0, width]); // set x scale
// set the colors
var z = d3.scaleOrdinal().range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
// load the csv and create the chart
d3.csv("data/data.csv", function (data, i) {
// calculate rates per 100k
data.id = +i;
data.f_violence_rate = +data.f_violence / +data.totEmp * +100000
data.f_trans_rate = +data.f_trans / +data.totEmp * +100000
data.f_fireExp_rate = +data.f_fireExp / +data.totEmp * +100000
data.f_fallSlipTrip_rate = +data.f_fallSlipTrip / +data.totEmp * +100000
data.f_exposure_rate = +data.f_exposure / +data.totEmp * +100000
data.f_contact_rate = +data.f_contact / +data.totEmp * +100000
data.f_allOther_rate = +data.f_allOther / +data.totEmp * +100000
data.f_total_rate = +data.f_violence_rate + +data.f_trans_rate + +data.f_fireExp_rate +
+data.f_fallSlipTrip_rate + +data.f_exposure_rate + +data.f_contact_rate + +data.f_allOther_rate;
return data;
}, function (error, data) {
if (error) throw error;
data.sort((a, b) => d3.descending(a.f_total_rate, b.f_total_rate));
y.domain(data.map(function (d) { return d.occupation; }));
x.domain([0, d3.max(data, function (d) { return d.f_total_rate; })]).nice();
z.domain(causes);
dataset = data;
drawChart();
});
function order(data) {
order = [+0, +1, +2, +3, +4, +5, +6]
order.splice(firstCause, 1); // remove
order.unshift(firstCause) // put at front
return order;
}
function update(fCause) {
firstCause = fCause;
dataset = [...dataset];
var sortFn;
switch (firstCause){
case +0: {
sortFn = (a, b) => d3.descending(a.f_trans_rate, b.f_trans_rate);
break;
}
case +1: {
sortFn = (a, b) => d3.descending(a.f_violence_rate, b.f_violence_rate);
break;
}
case +2: {
sortFn = (a, b) => d3.descending(a.f_fireExp_rate, b.f_fireExp_rate);
break;
}
case +3: {
sortFn = (a, b) => d3.descending(a.f_fallSlipTrip_rate, b.f_fallSlipTrip_rate);
break;
}
case +4: {
sortFn = (a, b) => d3.descending(a.f_exposure_rate, b.f_exposure_rate);
break;
}
case +5: {
sortFn = (a, b) => d3.descending(a.f_contact_rate, b.f_contact_rate);
break;
}
case +6: {
sortFn = (a, b) => d3.descending(a.f_allOther_rate, b.f_allOther_rate);
break;
}
default: console.log('default')
}
const yCopy = y.domain(dataset.sort(sortFn).map(d => d.occupation)).copy();
// define what will do the transition
const t = d3.transition().duration(1000);
const t2 = d3.transition().duration(1000);
const groups = d3.selectAll("g.bar-group")
.data(d3.stack().keys(causes).order(order)(dataset, firstCause)
.attr("fill", function (d) { return z(d.key); });
const bars = groups.selectAll(".bar")
.data(d => d, d => d.data.occupation)
.sort((a, b) => yCopy(a.data.occupation) - yCopy(b.data.occupation))
// sort order of stack
t.selectAll("g.bar-group")
.selectAll(".bar")
.attr("x", function (d) {
return x(d[0]);
})
.delay(0)
// sort data y axis - needs seperate transition so bars move first
t2.selectAll("g.bar-group")
.selectAll(".bar")
.attr("y", function (d) { return yCopy(d.data.occupation) })
.delay(1000)
// sort label y axis
t.select(".axis.y")
.call(d3.axisLeft(y))
.selectAll("g")
.delay(1000)
g.selectAll("g.bar-group").transition()
.filter(function() {
return !this.classList.contains('bar')
})
.duration(200)
.attr("opacity", 175)
}
function sortBy(i){console.log(i); update(i)} // i is 0-6
function drawChart() {
console.log(dataset)
g.append("g")
.selectAll("g")
.data(d3.stack().keys(causes)
.order(d3.stackOrderAscending)(dataset))
.enter().append("g")
.classed("bar-group", true)
.attr("fill", function (d) { return z(d.key); })
.selectAll("rect")
.data(function (d) { return d; })
.enter().append("rect")
.classed("bar", true)
.attr("y", function (d) {
return y(d.data.occupation);
})
.attr("x", function (d) {
return x(d[0]);
})
.attr("width", function (d) {
return x(d[1]) - x(d[0]);
})
.attr("height", y.bandwidth())
}
这有点冗长,但是本质上是将图表创建为堆叠的条形图,然后sort方法应该通过调用update来重新排列条形图,但是只能使用一次。
TIA寻求帮助或评论:)
答案 0 :(得分:0)
主要问题是订单功能的定义
function order(data) {
order = [+0, +1, +2, +3, +4, +5, +6]
order.splice(firstCause, 1); // remove
order.unshift(firstCause) // put at front
return order;
}
在1次调用之后,您已经重新定义了order
,它不再是一个函数。将其传递给stack.order(order)
无效。
在函数内部使用一个新变量,它可以与var order
一起使用,但这也会增加混乱。
function order(data) {
var orderNew = [+0, +1, +2, +3, +4, +5, +6]
orderNew.splice(firstCause, 1); // remove
orderNew.unshift(firstCause) // put at front
return orderNew;
}
您的数据集在occupation
列中有很多重复项。为什么? (唯一的区别是空格)