D3: <rect> attribute x: Expected length, "NaN". error - javascript

I have to implement a bar chart in D3, but my values on the x axis are of type Date, data type which the D3 library should accept, but it seems to give me an error like this: attribute x: Expected length, "NaN".
This is the code for my bar chart:
//this holds the data that will be drawn
var data = [ {"yy":12,"mm":01,ppm_value:90000}, {"yy":11,"mm":02,ppm_value:50000}];;
//formats the date
var format = d3.time.format("%Y-%m-%d");
//define margins, height and width
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
w = 1000 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
/*var x = d3.time.scale()
//.domain([0, d3.max(data, function(d) { return d.date; })])
.range([0, w], 0.6);*/
var x = d3.time.scale()
.range([0, w]);
var y = d3.scale.linear()
//.domain([0, d3.max(data, function(d) { return d.ppm_value;})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
//.tickFormat(d3.time.format("%Y/%m"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var zoom = d3.behavior.zoom()
.scaleExtent([1, 1])
.x(x)
.on("zoom", zoomed);
//create the svg
var chart = d3.select("#testChart").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var rect = chart.append("rect")
.attr("width", w)
.attr("height", h)
.style("fill", "none")
.style("pointer-events", "all");
//loops through data
data.forEach(function (d) {
//coerce to number
d.ppm_value = +d.ppm_value;
d.yy = +d.yy;
d.mm = +d.mm;
d.date = new Date("20" + d.yy + "/" +d.mm);
var dateTick = format(d.date);
d.date = dateTick;
console.log(d.ppm_value);
//console.log(dateTick);
//console.log(d.date);
});
//var dates = data.map(function(d){ return new Date("2016/01/03"); });
//console.log(formDate);
//map values onto x axis
x.domain([d3.min(data, function(d) { return d.date; }), d3.max(data, function(d) { return d.date; })])
// x.domain([0, d3.max(function(d) { return d.date; })]);
//map values onto y axis
y.domain([0, d3.max(data, function(d) { return d.ppm_value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bars = chart.append("g")
.attr("class", "chartobjects");
bars.selectAll(".rect")
.data(data)
.enter().append("rect")
.attr("class", "rectBar")
.on("click",hello)
.attr('x', function(d) {
console.log(d.date);
return x(new Date(d.date));
})
.attr("y", function(d) {
return y(d.ppm_value);
console.log(d.ppm_value);
})
.attr("height", function(d) {
return h - y(d.ppm_value);
})
.attr("width", 15)
.attr("fill", function(d) {
return d.ppm_value > 6 ? "blue" : "red"
});
function hello() {
alert("Hello world!!")
}
function zoomed() {
console.log("Entered zoom function!!!");
var tx = Math.max(0, d3.event.translate[0])
//ty = Math.min(0, d3.event.translate[1]);
zoom.translate([tx]);
bars.attr("transform", "translate(" + [tx] + ")scale(" + d3.event.scale + ")");
chart.select(".x.axis")
.call(xAxis);
//chart.select(".y.axis")
// .call(yAxis);
}
I can not seem to get to the root of the problem. Am I doing something wrong ? What is the problem in your opinion ?
Thanks in advance!

Related

D3 Bar Chart Columns Overflowing

I am having an issue using d3 to create a bar chart where the column overflows the and the y axis labels are incorrect. Here is a fiddle http://jsfiddle.net/fajgvj9v/ that shows the issue. The data is parsing the <pre id="data"> tag to simulate a CSV I am using. If you change the value of 'a' to 5000 from 4000 it renders as expected.
<pre id="data">
name,count
a, 4000
b, 500
</pre>
yTitle = "Items";
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var y = d3.scale.linear()
.rangeRound([height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#malware_by_os").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = d3.csv.parse( d3.select("pre#data").text() );
console.log(data);
var xLbl = d3.keys(data[0])[0];
var yLbl = d3.keys(data[0])[1];
data.sort(function(a, b) {
return b[yLbl] - a[yLbl];
});
x.domain(data.map(function(d) {
return d[xLbl];
}));
y.domain([0, d3.max(data, function(d) {
return d[yLbl];
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yTitle);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d[xLbl]);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d[yLbl]);
})
.attr("height", function(d) {
return height - y(d[yLbl]);
});
The max function needs to convert to a number using +
y.domain([0, d3.max(data, function(d) {
return d[yLbl];
})]);
should be
y.domain([0, d3.max(data, function(d) {
return +d[yLbl];
})]);

D3.js Stream Graph json data format

I'm trying to generate a D3 Stream Graph similar to this beautiful example using some activity tracker data.
I've referenced this example - http://jsfiddle.net/NTJPB/1/light
My JSFiddle - http://jsfiddle.net/Nyquist212/00war1o6/ is telling me I have something wrong with my json format (despite trying to model it on the examples).
The core of my code looks something like this -
data.forEach(function(d){
parseDate = d3.time.format("%m/%d/%Y").parse;
d.date = parseDate(d.date);
d.value = Math.round(+d.value);
});
var maxval = d3.max(data, function(d){ return d.value; });
// Nest the json by key
var nest = d3.nest()
.key(function(d) { return d.key; })
.entries(data);
var margin = {top: 50, right: 50, bottom: 50, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.linear()
.range(["#0A3430", "#1E5846", "#3E7E56", "#6BA55F", "#A4CA64", "#E8ED69"]);
var x = d3.scale.linear()
.range([0, width])
.domain([0, data[0].length]);
var y = d3.scale.linear()
.range([height, 0])
.domain([0, maxval]);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var stack = d3.layout.stack()
.offset("wiggle");
var layers = stack([data]);
var area = d3.svg.area()
.interpolate('cardinal')
.x(function (d, i) { return x(i); })
.y0(function (d) { return y(d.y0);})
.y1(function (d) { return y(d.y0 + d.y);});
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function (d) {return area(d);})
.style("fill", function (d, i) {
return color(i);
});
var layers = stack([nest]);
var area = d3.svg.area()
.interpolate('cardinal')
.x(function (d, i) { return x(i); })
.y0(function (d) { return y(d.y0);})
.y1(function (d) { return y(d.y0 + d.y);});
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function (d) {return area(d);})
.style("fill", function (d, i) {
return color(i);
});
What is the preferred/desired json format I need to massage my data into? Is this even my problem?
Thanks
I refactored things and managed to get it working... amongst other things I think I was polluting my global variable namespace.
http://jsfiddle.net/Nyquist212/00war1o6/
margin = {top: 20, right: 20, bottom: 20, left: 30};
width = 950 - margin.left - margin.right;
height = 250 - margin.top - margin.bottom;
colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
parseDate = d3.time.format("%m/%d/%Y").parse;
x = d3.time.scale().range([margin.left, width]);
y = d3.scale.linear().range([height, 0]);
z = d3.scale.ordinal().range(colorrange);
xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.years);
yAxis = d3.svg.axis().scale(y);
nest = d3.nest()
.key(function(d) { return d.key; });
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value= +d.value;
});
stack = d3.layout.stack()
.offset("silhouette")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
layers = stack(nest.entries(data));
area = d3.svg.area()
//.interpolate("cardinal")
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
svg = d3.select("#streamgraph").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis.orient("right"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis.orient("left"));
svg.selectAll(".layer")
.attr("opacity", 1)
.on("mouseover", function(d, i) {
svg.selectAll(".layer").transition()
.duration(250)
.attr("opacity", function(d, j) {
return j != i ? 0.6 : 1;
})
})

Bar chart scale

I have this bar chart on D3.js... It works fine..
But I'm having problems with the scale... When a data series has a value much grater than the others, the <rect> does not fit into the scale.
Any idea how to solve this matter?
Here is the code:
var data = [
{"Anio":"1999","CONTRAVENCIONAL":"78484","PENAL":"0","FALTAS":"0","MULTAS":"0","OTROS":"0","TOTAL":"78484"},
{"Anio":"2000","CONTRAVENCIONAL":"92879","PENAL":"0","FALTAS":"0","MULTAS":"0","OTROS":"0","TOTAL":"92879"},
{"Anio":"2001","CONTRAVENCIONAL":"100018","PENAL":"0","FALTAS":"1818","MULTAS":"0","OTROS":"0","TOTAL":"101836"},
{"Anio":"2002","CONTRAVENCIONAL":"101380","PENAL":"0","FALTAS":"3692","MULTAS":"0","OTROS":"0","TOTAL":"105072 "},
{"Anio":"2003","CONTRAVENCIONAL":"86791","PENAL":"0","FALTAS":"7417","MULTAS":"0","OTROS":"0","TOTAL":"94208"},
{"Anio":"2004","CONTRAVENCIONAL":"47870","PENAL":"255","FALTAS":"1105","MULTAS":"1811","OTROS":"0","TOTAL":"51041"},
{"Anio":"2005","CONTRAVENCIONAL":"33013","PENAL":"348","FALTAS":"1473","MULTAS":"634","OTROS":"0","TOTAL":"35468"},
];
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 860 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var arr_data = [];
for (var i = 0; i < data.length; i++) {
var o = {'name':data[i].Anio,'value':data[i].TOTAL};
arr_data.push(o);
};
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(arr_data.map(function(d) { return d.name; }));
y.domain([0, d3.max(arr_data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(arr_data)
.enter().append("g");
bar.append("rect")
.attr("class", "rect")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value) ; })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand())
.attr("fill","#632423")
.on('mouseover',function(d){
var a = d3.select(this)
.attr("fill","#733A39");
var a = d3.select("#tooltip")
.style("left","100px")
.style("top","20px")
.select("#value")
.text(d.value);
}).on('mouseout',function(d){
var a = d3.select(this)
.attr("fill","#632423"); //old color: #790018
});
bar.append("text")
.attr("class", "label")
.text(function(d) { return d.value; })
.attr("x", function(d) { return x(d.name)+4; })
.attr("y", function(d) { return y(d.value)+20 ; });
Your scale will actually handle series with larger values for you because you're dynamically setting it to go from 0 to the max value in your data:
y.domain([0, d3.max(arr_data, function(d) { return d.value; })]);
However, you're not converting your data to numbers from strings, so right now the scale is from [0, 94208] instead of [0, 105072]. This is because the character "9" is greater than "1". You can fix this by converting the values to numbers when you construct your arr_data, like this:
var o = {'name':data[i].Anio,'value':+data[i].TOTAL}; // <---- notice the '+'
This produces a better looking graph:

D3js graph not generating: no errors on console

I am new to svg and d3js and am following the tutorials on https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-starting-with-a-basic-graph
$('#to').on('change',function() {
var endDate = new Date(Date.parse( $('#to').val()
));
var width= 1024;
var height= 500;
var margin = {top:20, right:20, top:20, bottom:20};
var parseDate = d3.time.format("%x %H").parse;
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(30);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(30);
var valueline = d3.svg.line()
.x(function(d) { return x(d.timestamp); })
.y(function(d) { return y(d.phaseOne); return y(d.phaseTwo); return y(d.phaseThree);});
var svg = d3.select("#graph")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
d3.json('<?php echo base_url(); ?>uploads/<?php echo $username; ?>.json',function(error,data){
data.forEach(function(d){
d.timestamp = parseDate(d.timestamp);
d.pday_chan1 = +d.day_chan1;
d.d.day_chan2 = +d.day_chan2;
d.d.day_chan3 = +d.day_chan3;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.day_chan1; })]);
y.domain([0, d3.max(data, function(d) { return d.day_chan2; })]);
y.domain([0, d3.max(data, function(d) { return d.day_chan3; })]);
svg.append("path")
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
});
Instead of generating one line I am trying to generate three lines. The code doesn't show any errors, and I am confused on where I should start debugging. Any pointers would be appreciated.

Trying to add multiple D3 graphs

I'm trying to figure out how to include 2 or more graphs on my website. I am currently using the Area Graph and the Pie Graph. If I disable one of them, then the other works fine, but when I try to use both at the same time, it stops working.
JS code for the Area Graph
var margin = { top: 0, right: 0, bottom: 30, left: 50 },
width = 670 - margin.left - margin.right,
height = 326 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#watLenYearGra").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var area = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(d.close); });
$("#watLenYear").click(function () {
$("#watLenYearGra").slideToggle("fast");
});
d3.csv("data.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.close; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
});
JS code for the Pie Graph
var width = 670,
height = 326,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.population; });
var svg = d3.select("#watLenSzGra").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
$("#watLenSz").click(function () {
$("#watLenSzGra").slideToggle("fast");
});
d3.csv("data1.csv", function (error, data) {
data.forEach(function (d) {
d.population = +d.population;
});
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) { return color(d.data.age); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.age; });
});
I think the problem lies with the scope of the variables (specifically the svg variable since they have the same name). Right now I have these sets of code in separate JS files, and I link them at the bottom of my body tag in the HTML file.
How can I limit these variables' scope to their files? Is that even the problem? Thanks
It should work if you use closures:
(function() {
var margin = { top: 0, right: 0, bottom: 30, left: 50 },
width = 670 - margin.left - margin.right,
height = 326 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#watLenYearGra").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var area = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(d.close); });
$("#watLenYear").click(function () {
$("#watLenYearGra").slideToggle("fast");
});
d3.csv("data.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.close; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
});
})();
(function() {
var width = 670,
height = 326,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.population; });
var svg = d3.select("#watLenSzGra").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
$("#watLenSz").click(function () {
$("#watLenSzGra").slideToggle("fast");
});
d3.csv("data1.csv", function (error, data) {
data.forEach(function (d) {
d.population = +d.population;
});
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) { return color(d.data.age); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.age; });
});
})();

Categories