Buduję zgrupowany wykres słupkowy zagnieżdżając plik .csv. Wykres będzie również widoczny jako wykres liniowy, więc chcę struktury zagnieżdżania, która pasuje do obiektu linii. Mój oryginalny .csv wygląda następująco:d3 uzyskiwanie dostępu do zagnieżdżonych danych w zgrupowanym wykresie słupkowym
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
i mój zagnieżdżenia jest tak:
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]),
};
}),
};
});
Kod budować mój wykres jest:
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); });
Elementy G są w porządku i poszczególne paski są na nich odwzorowywane, przy czym wartość x i klasa są poprawnie stosowane.
Mój problem polega na uzyskaniu dostępu do danych dla "stawki" dla wysokości i wartości y słupków. W powyższym formularzu daje NaN. Ja również próbowałem przy użyciu danych kategorii, do dołączania elementów G i następnie dołączając prostokąty, z:
.data(function(d) { return d.values })
To pozwala mi na dostęp do danych stóp, ale odwzorowuje wszystkie 36 barów do każdego z rangeBands.
To również działa dobrze w bardziej płaskiej strukturze danych, ale nie mogę go użyć, gdy jest zagnieżdżony na dwa poziomy w dół, mimo że przegląda wiele przykładów i pytań SO.
Jak uzyskać dostęp do danych o kursie?
W odpowiedzi na wniosek Cyryla, oto pełny kod:
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);
});
Aktualizacja 18 lutego '16.
Wygląda na to, że nie wyjaśniłem, co starałem się zrobić wystarczająco dobrze. Wersje liniowe i słupkowe wykresu będą widoczne osobno, tj. Użytkownicy zobaczą jeden zgodnie z wprowadzeniem do elementu wyboru. Zauważ też, że nie mam kontroli nad tym, w jaki sposób dane przychodzą na początku.
mam a version of exactly how it should work here.
Ta kwestia została podniesiona kiedy nadal działa przez niego, ale nigdy nie rozwiązało problemu - Kiedyś obejście prowadzenia dwóch oddzielnych gniazd danych.
czy możesz opublikować swój pełny kod ... i myślisz, że jest problem z yscale. – Cyril
Cześć Cyril, Założę pełny kod za chwilę, ale jestem pewien, że nie jest to kwestia YScale. Sprawdziłem, wstawiając numer w miejsce d.rate, a także sprawiłem, że działa, gdy zagnieżdżone dane są bardziej płaskie. Skala jest poniżej. var yScale = d3.scale.linear(). Range ([h, 0]) .nice(); – tgerard
@tgerard Aby potwierdzić - czy chcesz pogrupować wykres słupkowy z datami jako osią x i wartościami jako oś y? Jeśli tak, to nie jest jasne, w jaki sposób planujesz użyć obiektu kategorii. Podany kod znajduje się w obiekcie kategorii dla d.rate. Ale obiekt kategorii ma kształt: {nazwa: "Rzeczywisty", wartości: [{data: "", stawka: 0}]}. Więc nie będzie miał na nim właściwości d.rate. Czy możesz potwierdzić, co próbujesz zbudować, i mogę doradzić, jak najlepiej to rozwiązać? –