2017-02-22 14 views
0

Итак, попытаемся создать сложную гистограмму в D3.js. У меня есть оси, но графические данные не отображаются, какие-либо идеи, в которых я ошибаюсь?Сложенная гистограмма в D3, используя внутреннюю переменную JSON

JS:

var svg = d3.select("#recovery__table"), 
    margin = {top: 20, right: 20, bottom: 30, left: 40}, 
    width = +svg.attr("width") - margin.left - margin.right, 
    height = +svg.attr("height") - margin.top - margin.bottom, 
    aspect = width/height, 
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

var x = d3.scaleBand() 
    .rangeRound([0, width]) 
    .padding(0.1) 
    .align(0.1); 

var y = d3.scaleLinear() 
    .rangeRound([height, 0]); 

var z = d3.scaleOrdinal() 
    .range(["#717C8B", "#7FDDC3", "#39B3CD"]); 

var stack = d3.stack(); 

data.forEach(function(d) { 
    d.year = d['trades.closed_half_year_year']; 
    d.loss = d['loss']; 
    d.recovered = d['recovered']; 
    d.recovery = d['in_recovery']; 
    d.total = d.loss + d.recovery + d.recovered; 
}); 

var div = d3.select("body").append("div") 
    .attr("class", "tooltip3") 
    .style("opacity", "0"); 

x.domain(data.map(function(d) { return d.year; })); 
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice(); 
z.domain(d3.keys(data[0]).filter(function(key){ return key == 'loss' && key == 'recovered' && key == 'in_recovery' })); 

g.selectAll(".serie") 
     .data(data) 
     .enter().append("rect") 
     .attr("class", "bar") 
     .attr("fill", function(d){ return z(d.keys); }) 
     .attr("x", function(d) { return x(d.year); }) 
     .attr("width", x.bandwidth()) 
     .attr("y", function(d) { return y(d.total); }) 
     .attr("height", function(d) { return y[0] - y[1]; }) 
     .on("mouseover", function(d) { 
     var value = parseInt($(this).attr('data-value')); 
     div.transition() 
      .duration(200) 
      .style("opacity", .5); 
     div.html(d.data.year + "<br/>£" + total.formatMoney()) 
      .style("left", (d3.event.pageX) + "px") 
      .style("top", (d3.event.pageY - 28) + "px"); 
     }) 
     .on("mouseout", function(d) { 
     div.transition() 
      .duration(500) 
      .style("opacity", 0); 
     }); 
    ; 

g.append("g") 
     .attr("class", "axis axis--x") 
     .attr("transform", "translate(0," + height + ")") 
     .attr('x', 20) 
     .call(d3.axisBottom(x)); 

g.append("g") 
     .attr("class", "axis axis--y") 
     .call(d3.axisLeft(y).ticks(5, "s")) 
     .append("text") 
     .attr("x", 2) 
     .attr("y", y(y.ticks(10).pop())) 
     .attr("dy", "0.35em") 
     .attr("text-anchor", "start") 
     .attr("fill", "#000"); 

var legend = g.selectAll(".legend") 
     .data(data) 
     .enter().append("g") 
     .attr('width', 100) 
     .attr("class", "legend") 
     .attr('transform', function(d, i) { 
     var horz = 100*i;      // NEW 
     var vert = 0; 
     if (horz >= width) { 
      horz = 100 * (i - 3); 
      vert = 40; 
     } 

     return 'translate(' + horz + ',' + vert + ')';  // NEW 
     }) 
     .style("font", "10px sans-serif"); 

legend.append("rect") 
     .attr("x", "33%") 
     .attr("width", 18) 
     .attr("height", 18) 
     .attr("fill", z); 

legend.append("text") 
     .attr("x", "43%") 
     .attr("y", 9) 
     .attr("dy", ".35em") 
     .attr("text-anchor", "end") 
     .text(function(d) { return d; }); 

JSON Пример

[{"trades.closed_half_year_year":"2017","auctioncollectioninfos.total_advanced_amount_delinquent_and_collection_completed_gbp_daily":"£0.00","auctioncollectioninfos.total_crystallized_loss_gbp_daily":"£0.00","auctioncollectioninfos.total_outstanding_amount_delinquent_gbp_daily":"£","auctioncollectioninfos.total_advanced_amount_delinquent_gbp_daily":"£0.00","loss":"£0.00","recovered":"£0.00","in_recovery":"£0"}, 
{"trades.closed_half_year_year":"2016","auctioncollectioninfos.total_advanced_amount_delinquent_and_collection_completed_gbp_daily":"£123,456.78","auctioncollectioninfos.total_crystallized_loss_gbp_daily":"£0.00","auctioncollectioninfos.total_outstanding_amount_delinquent_gbp_daily":"£1,234,234","auctioncollectioninfos.total_advanced_amount_delinquent_gbp_daily":"£1,321,245.56","loss":"£0.00","recovered":"£457,468.31","in_recovery":"£1,890,567"}, 
{"trades.closed_half_year_year":"2015","auctioncollectioninfos.total_advanced_amount_delinquent_and_collection_completed_gbp_daily":"£3,345,768.54","auctioncollectioninfos.total_crystallized_loss_gbp_daily":"£555,555.08","auctioncollectioninfos.total_outstanding_amount_delinquent_gbp_daily":"£321,321","auctioncollectioninfos.total_advanced_amount_delinquent_gbp_daily":"£3,321,321.32","loss":"£456,324.33","recovered":"£2,324,234.345","in_recovery":"£333,333"}] 

По существу, нуждаются потери, восстановление и отводили в стек на графике, но нет загрузки данных на графике как уже упоминалось выше.

Любые идеи?

ответ

1

Существует небольшая проблема, данные, которые вы используете, - это JSON, поэтому объект получает значения в виде строк, вы должны правильно их разбирать в числа. Простой способ разобрать строку в число является следующее:

d.loss = +d['loss']; 

Но даже если бы мы сделали, что мы по-прежнему есть проблемы с вашими данными. Зачем? Поскольку некоторые из чисел в наборе данных отформатированы:

"loss":"£456,324.33" 

так, если вы пытаетесь сделать что-то вроде этого:

d.total = d.loss + d.in_recovery + d.recovered; 

вы получите недопустимое значение, так как мы можем выдавать операции, как следующее:

d.total = "£456,324.33" + 0 + "£4,324.33" // "£456,324.330£4,324.33" 

Это завинчивает весы на нашей карте.

y.domain([0, d3.max(data, function(d) { 
    return d.total; 
})]).nice(); // spooky domain here :S 

Давайте заботиться о форматировании ваших значений (предполагается, что значения всегда отформатированы путь, представленный в формате JSON вы предоставили):

data.forEach(function(d) { 
    d.year = +d['trades.closed_half_year_year']; 
    d.loss = typeof d.loss === 'number' ? d.loss : +d['loss'].replace(/£|,/g, '') 
    d.recovered = typeof d.recovered === 'number' ? d.recovered : +d['recovered'].replace(/£|,/g, ''); 
    d.in_recovery = typeof d.in_recovery === 'number' ? d.in_recovery : +d['in_recovery'].replace(/£|,/g, ''); 
    d.total = d.loss + d.in_recovery + d.recovered; 
}); 

Теперь, когда у нас есть правильный набор данных, мы должны быть готовы чтобы начать использовать d3 и stack расположение:

var keys = ['loss', 'recovered', 'in_recovery']; // Declare the keys we will want in our stack 
z.domain(keys); // Set them as our z domain so we can retrieve our fill color 
var stackLayout = d3.stack().keys(keys)(data); // Create our stack layout 

который создаст следующую структуру:

[ 
    [ 
     [ 
     0, 
     0 
     ], 
     [ 
     0, 
     0 
     ], 
     [ 
     0, 
     456324.33 
     ] 
     // key: loss 
    ], 
    [ 
     [ 
     0, 
     0 
     ], 
     [ 
     0, 
     457468.31 
     ], 
     [ 
     456324.33, 
     2780558.6750000003 
     ] 
     // key: recovered 
    ], 
    [ 
     [ 
     0, 
     0 
     ], 
     [ 
     457468.31, 
     2348035.31 
     ], 
     [ 
     2780558.6750000003, 
     3113891.6750000003 
     ] 
     // key: in_recovery 
    ] 
] 

Со структурой, приведенной выше, мы теперь можем создавать наши столбцы по блоку клавиш, так как вы можете видеть, что каждый массив имеет три значения и ключ. Нам нужно будет создать группу элемент для каждого элемента массива:

g.selectAll(".serie") 
    .data(stackLayout) // Set stack layout as data 
    .enter() 
    .append("g") // Creating group for each key 
    .attr("fill", function(d) { return z(d.key); }) // Fill inner elements with the color provided by our z Scale 
    .selectAll("rect") 
    .data(function(d) { // Use the inner array to create our rects 
    return d; 
    }) 
    .enter().append("rect") 
    .attr("x", function(d) { // Position by our x Scale 
    return x(d.data.year); 
    }) 
    .attr("y", function(d) { // Position by our y Scale 
    return y(d[1]); 
    }) 
    .attr("height", function(d) { // Find the height value by using the values provided in the inner arrays 
    return y(d[0]) - y(d[1]); 
    }) 
    .attr("width", x.bandwidth()); 

Мы также должны немного изменить метки:

var legend = g.selectAll(".legend") 
    .data(keys.reverse()) // Use our keys 
    .enter().append("g") 
    .attr("class", "legend") 
    .attr('transform', function(d, i) { 
    var horz = width - margin.right - (100 * i); // NEW 
    var vert = 0; 
    return 'translate(' + horz + ',' + vert + ')'; // NEW 
    }) 
    .style("font", "10px sans-serif"); 

legend.append("text") 
    .attr("x", "-5") 
    .attr("y", 9) 
    .attr("dy", ".35em") 
    .attr("text-anchor", "end") 
    .text(function(d) { 
    return d; 
    }); 

Работа plnkr: https://plnkr.co/edit/eTKsOz8jlaqm1Mf3Esej?p=preview