Я строю сгруппированную гистограмму, вставляя CSV файл. Диаграмма также будет отображаться в виде линейной диаграммы, поэтому я хочу, чтобы структура вложенности соответствовала объекту линии. Мой оригинальный .csv выглядит следующим образом:
Month,Actual,Forecast,Budget
Jul-14,200000,-,74073.86651
Aug-14,198426.57,-,155530.2499
Sep-14,290681.62,-,220881.4631
Oct-14,362974.9,-,314506.6437
Nov-14,397662.09,-,382407.67
Dec-14,512434.27,-,442192.1932
Jan-15,511470.25,511470.25,495847.6137
Feb-15,-,536472.5467,520849.9105
Mar-15,-,612579.9047,596957.2684
Apr-15,-,680936.5086,465313.8723
May-15,-,755526.7173,739904.081
Jun-15,-,811512.772,895890.1357
и моя вложенность такова:
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name, // "name": the csv headers except month
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
Код для построения моей диаграммы:
var bars = svg.selectAll(".barGroup")
.data(data) // Select nested data and append to new svg group elements
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name); });
Элементы g являются точными, а отдельные столбцы сопоставляются с ними, причем значение x и класс применяются правильно.
Моя проблема связана с доступом к данным для "rate" для значений высоты и y. В приведенной выше форме дается NaN. Я также попытался использовать данные категории, чтобы добавить g-элементы, а затем добавить исправления с помощью:
.data(function(d) { return d.values })
Это позволяет мне получить доступ к данным скорости, но отображает все 36 баров в каждую из диапазонов диапазонов.
Он также отлично работает в более плотной структуре данных, но я не могу использовать его, когда он вложен на два уровня, несмотря на то, что он просматривает множество примеров и вопросов SO.
Как мне получить доступ к данным скорости?
В ответ на запрос Кирилла, здесь полный код:
var margin = {top: 20, right: 18, bottom: 80, left: 50},
w = parseInt(d3.select("#bill").style("width")) - margin.left - margin.right,
h = parseInt(d3.select("#bill").style("height")) - margin.top - margin.bottom;
var customTimeFormat = d3.time.format.multi([
[".%L", function(d) { return d.getMilliseconds(); }],
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }],
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
["%b %d", function(d) { return d.getDate() != 1; }],
["%b", function(d) { return d.getMonth(); }],
["%Y", function() { return true; }]
]);
var parseDate = d3.time.format("%b-%y").parse;
var displayDate = d3.time.format("%b %Y");
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);
var xScale1 = d3.scale.linear()
.domain([0, 2]);
var yScale = d3.scale.linear()
.range([h, 0])
.nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(customTimeFormat)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-w)
.outerTickSize(0);
var svg = d3.select("#svgCont")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var thous = d3.format(",.0f")
var lineClass = d3.scale.ordinal().range(["actual", "forecast", "budget"]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<p id='date'>" + displayDate(d.date) + "</p><p id='value'>$" + thous(d.rate);
})
d3.csv("data/net.csv", function(error, data) {
if (error) throw error;
var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});
data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};
});
var min = d3.min(categories, function(d) {
return d3.min(d.values, function(d) {
return d.rate;
});
});
var max = d3.max(categories, function(d) {
return d3.max(d.values, function(d) {
return d.rate;
});
});
var minY = min < 0 ? min * 1.2 : min * 0.8;
xScale.domain(data.map(function(d) { return d.month; }));
yScale.domain([minY, (max * 1.1)]);
var barWidth = headers.length > 2 ? xScale.rangeBand() / 2 : xScale.rangeBand() ;
svg.call(tip);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var bars = svg.selectAll(".barGroup")
.data(data)
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });
bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name) + " bar"; });
var legend = svg.selectAll(".legend")
.data(headers)
.enter()
.append("g")
.attr("class", "legend");
legend.append("line")
.attr("class", function(d) { return lineClass(d); })
.attr("x1", 0)
.attr("x2", 40)
.attr("y1", function(d, i) { return (h + 30) + (i *14); })
.attr("y2", function(d, i) { return (h + 30) + (i *14); });
legend.append("text")
.attr("x", 50)
.attr("y", function(d, i) { return (h + 32) + (i *14); })
.text(function(d) { return d; });
svg.selectAll(".bar")
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
Обновление 18 февраля '16.
Кажется, я не объяснил, что я пытался сделать достаточно хорошо. Линейные и штриховые варианты диаграммы будут видны отдельно, то есть пользователи могут видеть один из них в соответствии с вводом элемента выбора. Также обратите внимание, что я не контролирую, как данные поступают изначально.
У меня версия того, как она должна работать здесь.
Этот вопрос был поднят, когда я все еще работал над ним, но я никогда не решал проблему - я использовал обходное решение для создания двух отдельных гнезд данных.