Сортировка столбцов D3 - график обновляется только один раз - PullRequest
0 голосов
/ 27 декабря 2018

Я работаю над визуализацией 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 голосов
/ 27 декабря 2018

Основная проблема - определение вашей функции заказа

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.Зачем?(единственное отличие - это пробел)

...