D3堆积条形排序-图表仅更新一次

时间:2018-12-27 05:33:01

标签: javascript d3.js visualization

我正在使用堆叠条形图进行D3可视化。我正在尝试添加动画,以便读者可以按特定子栏的值的降序对所有栏进行排序,并且所选子栏最靠近轴。最终我想附上类似图片的东西。

screen shot

每次我调用我的排序方法时,对条形进行整体排序都可以正常工作,但是对子条形进行排序以使所选度量最接近轴时,只能更新一次。我不知道为什么,但是我环顾四周,认为这可能与关键功能有关,但是我不确定如何将其与堆栈布局集成。

这就是现在的样子-如您所见,堆叠的条形图的实际顺序仅重新排列一次。 Here's a gif of it in action

数据来自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寻求帮助或评论:)

1 个答案:

答案 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列中有很多重复项。为什么? (唯一的区别是空格)