max and min values from json in d3 - json

I'm following a tutorial of d3 that uses tsv rather than json.
I'm more familiar with json so I wanted to apply my own json object to the example.
I found this and tried to apply it but i'm still getting errors:
How to use d3.min and d3.max within a d3.json command
Here is my code. I am getting the json object back with my first log. And the correct length with my second log.
Yet I get this error when trying to use d.value in d3.max()
Error: Invalid value for attribute width="NaN"
d3.json("sampledata.json", function(error, data) {
var maxx = d3.entries(data)
.sort(function(a,b){return d3.descending(a.value, b.value);})
console.log(maxx)
console.log(data.length);
mydata = data;
x.domain([0, d3.max(data, function(d) { return d.value; })])
chart.attr("height", barHeight * mydata.length);
var bar = chart.selectAll("g")
.data(mydata)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
// WHERE MY ERROR IS because there is no value for x
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
});

I think what you need to do, since x is a scale of some kind, is the following:
bar.append("rect")
.attr("width", function(d) {
return x(d.value);
})
.attr("height", barHeight - 1);
Otherwise it is just going to pass d into x, which isn't going to help you.

Related

Facing problem with : Error: <rect> attribute height: Expected length, "NaN" and Error: <rect> attribute y: Expected length, "NaN"

I am new in d3.js and right now trying to visualize a csv in a barchart. But facing an error like this : Error: attribute height: Expected length, "NaN" and Error: attribute y: Expected length, "NaN". My full code is below: need expert advice.
The csv data looks like as following :
year,value
AK,35
AK,55
AL,165
AL,323
AR,86
AR,155
AZ,409
AZ,719
CA,1891
CA,3390
CO,292
CO,442
CT,117
CT,220
DE,392
DE,1819
FL,1064
FL,1870
GA,423
GA,728
HI,53
HI,108
IA,75
IA,173
ID,62
ID,102
IL,551
IL,858
IN,529
IN,901
KS,102
KS,206
KY,162
KY,251
LA,255
LA,684
MA,350
MA,621
MD,244
MD,439
ME,71
ME,125
MI,353
MI,607
MN,221
MN,392
MO,184
<!doctype html>
<html>
<head>
<style>`enter code here`
.bar {
fill: steelblue;
}
.highlight {
fill: orange;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>
<script>
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
svg.append("text")
.attr("transform", "translate(100,0)")
.attr("x", 50)
.attr("y", 50)
.attr("font-size", "24px")
.text("Polarity visualization of csv data")
var x = d3.scaleBand().range([0, width]).padding(0.4),
y = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.csv("polarity.csv", function(error, data) {
if (error) {
throw error;
}
x.domain(data.map(function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("y", height - 250)
.attr("x", width - 100)
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Year");
g.append("g")
.call(d3.axisLeft(y).tickFormat(function(d){
return "$" + d;
}).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "-5.1em")
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Stock Price");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.on("mouseover", onMouseOver) //Add listener for the mouseover event
.on("mouseout", onMouseOut) //Add listener for the mouseout event
.attr("x", function(d) { return x(d.year); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(400)
.delay(function (d, i) {
return i * 50;
})
.attr("height", function(d) { return height - y(d.value); });
});
//mouseover event handler function
function onMouseOver(d, i) {
d3.select(this).attr('class', 'highlight');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth() + 5)
.attr("y", function(d) { return y(d.value) - 10; })
.attr("height", function(d) { return height - y(d.value) + 10; });
g.append("text")
.attr('class', 'val')
.attr('x', function() {
return x(d.year);
})
.attr('y', function() {
return y(d.value) - 15;
})
.text(function() {
return [ '$' +d.value]; // Value of the text
});
}
//mouseout event handler function
function onMouseOut(d, i) {
// use the text label class to remove label on mouseout
d3.select(this).attr('class', 'bar');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
d3.selectAll('.val')
.remove()
}
</script>
</body>
</html>
I am new in d3.js and right now trying to visualize a csv in a barchart. But facing an error like this : Error: attribute height: Expected length, "NaN" and Error: attribute y: Expected length, "NaN". My full code is below: need expert advice.
The problem seems to be that there is no type conversion, which makes every attribute in the data a string. Since every number is a string, you can't pass it to d3.max() or scale.range(), hence the errors.
To convert the type in d3.v4, you can use the second argument of d3.csv(). It is a function that maps each row of the CSV to another object before sending the data to the callback.
Let's add the second argument.
Currently, you have:
d3.csv("polarity.csv", function(error, data) {...})
Which is equivalent to:
d3.csv("polarity.csv", (d) => (d), function(error, data) {...})
The code above is equivalent because the second argument is just an identity function. Before calling the last callback, d3.csv() is taking every row and returning it without any change.
Going a step further, the same code is also equivalent to this one:
d3.csv("polarity.csv", (d) => ({... d}), function(error, data) {...})
The code above is equivalent because the {...d} is just ES6 object destructuring notation. The d3.csv() is still taking every row and returning it without any change.
Up until now, we just changed the notation without making any change in the data. Now it's the time to do the type conversions. If you know that d.value is a number, you can overwrite it and coerce the type:
d3.csv("polarity.csv", (d) => ({... d, value: +d.value}), function(error, data) {...})
The value: +d.value means that we are taking the d.value string, converting it to a number, and assigning it back to the value property.
Now the second argument is doing the adequate type conversions, and the data in the third callback will have d.value as a number, which will be compatible with d3.max() and scale.range()
Side Note:
This process was made easier in d3.v5 with the introduction with d3.autoType() and the use of Promises in d3.csv(). If you're learning D3 concepts in general, and you're not tied to d3.v4 for a specific project, I highly recommend reading about the changes between v4 and v5 (which are only a handful) and about the use of d3.autoType().

Bar graph is not rendering

I'm trying to duplicate this code found on bl.ocks.org, but I can't get my bar graph to render. I'm pulling from a csv instead of a tsv, but I don't think that would made a difference. There aren't any errors written to the console. Where am I going wrong?
<script type="text/javascript">
$( document ).ready(function() {
loadChart();
});
function loadChart() {
var svg = d3.select("svg");
var 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;
var x = d3.scaleBand().rangeRound([0,width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append(g).attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("Data/data.csv", function(d) {
d.NumProducts = +d.NumProducts;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Category; }));
y.domain([0, d3.max(data, function(d) { return d.NumProducts; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("Dy", "0.71em")
.attr("text-anchor", "end")
.text("Number of Products");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class" , "bar")
.attr("x", function(d) { return x(d.Category); })
.attr("y", function(d) { return y(d.NumProducts); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.NumProducts); });
});
}
</script>
The csv:
Category,NumProducts
Beverages,12
Condiments,12
Confections,13
Dairy Products,10
Grains/Cereals,7
Meat/Poultry,6
Produce,5
Seafood,12
The main issue that you have is down to missing speech marks.
var g = svg.append(g)
Should read:
var g = svg.append("g")
If you were to inspect your DOM you would see
<svg>
<undefined transform="translate(40,20)">
... (correct DOM)
</undefined>
</svg>
The reason that nothing renders after this point, is because the browser doesn't know how to render an <undefined> element, therefore it stops trying to render.
This immediately indicated that one of the append calls wasn't working because the DOM didn't understand the type of element given to it. Making the change produces this:

D3 linegraph using JSON data from API

I am working on making a linegraph in D3 that displays data in JSON format that is retrieved from an API.
I found an example line graph project on D3's website and it seems relatively straight forward. The main difference between the example and my project is that the example uses data from a local csv file instead of JSON from an API request.
// This needs to be changed to my API request
d3.tsv("data.tsv", function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
I changed the csv request part to this because I'm trying to load in my JSON from the API:
d3.json(url).get(function(d) {
for(var i = 0; i < d.Data.length; i++) {
coinValue.push(d.Data[i].close);
dates.push(convertUnixTime(d.Data[i].time));
}
return d;
}, function(error, data) {
if (error) throw error;
This isn't working. I'm confused about why there's a comma and then another function right after.
What is the proper way to make the exact same line graph but with a d3.json function/API request instead?
My JSON looks like this: https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG
The difference between d3.csv and d3.json is the accessor (2nd argument).
d3.csv: d3.csv(url[[, row], callback]) in which row is nothing but each row of the data fetched i.e. if you add a function as follows:
d3.csv(url, function(d) {
// each row of the data
}, callback)
d3.json: d3.json(url[, callback]) DOES NOT provide this each row accessor. But you can do that within the callback as follows:
d3.json(url, function(err, data) {
data.forEach(function(row) {
// parse each row as required
});
})
Using the above syntax along with the provided example code and JSON URL, here's a code snippet drawing a line chart:
// This needs to be changed to my API request
d3.json("https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG", function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
//console.log(data);
if (error) throw error;
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.close); });
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
Hope this helps.

Stacked Bar Chart with Time Series Data

I am new to D3.js and am using D3 v4 for this. I have already seen https://bl.ocks.org/mbostock/3886208.
Here is the .csv file (test_data.csv):
date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575
Here is page.html:
<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>
rect.bar { fill: steelblue; }
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<body>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime()
.domain([new Date(2012, 11, 1), new Date(2017, 11, 31)])
.range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal().range(["#F1F1F1", "#CE1126"])
// load .csv file
d3.csv("test_data.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data){
if (error) throw error;
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(data.map(function(d){ return d.date }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
console.log(d3.stack().keys(keys)(data));
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return console.log(x(d.data.date)); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
});
</script>
</body>
My aim is to create a stacked bar chart by month based on the link above (so each tick corresponds to a month), of the values in col_1 and col_2.
Some errors I've received:
console.log(x(d.data.date)), for some reason, does not return the actual date.
The following error:
Uncaught TypeError: x.bandwidth is not a function
at page.html:65
at Object. (d3.v4.js:11453)
at Dispatch.call (d3.v4.js:792)
at XMLHttpRequest.respond (d3.v4.js:11378)
I think this error might have something to do with the fact that I'm using scaleTime, rather than scaleBand, but I'm not sure.
There are some issues with your dates, which need to be in working order to use a time scale.
Firstly, Your date format is 4/1/2015 but you are parsing it with d3.timeParse("%Y-%m-%d");
You will only get null values parsing with this, to see this try:
data.forEach(function(d) {
d.date = parseDate(d.date);
console.log(d.date); // null
});
As you use slashes rather than dashes, and you use month, day, year, try:
d3.timeParse("%m/%d/%Y");
Secondly, and related to dates is that you set an initial domain based on hard coded minimum and maximum dates, that works, but you overwrite it with this:
x.domain(data.map(function(d){ return d.date }));
A timescale domain takes in a minimum and a maximum for the domain. You are providing an array of every value, instead, use:
x.domain(d3.extent( data, function(d){ return d.date }) );
On to the graph and bar width, .bandwidth() isn't a method of a continuous scale (hence your error: "Uncaught TypeError: x.bandwidth is not a function"). In a continuous scale the bars are placed based on their value, there is no natural bar width (bars are not placed with regular spacing unless the increment between each data value is the same).
As you have a data point for each month (there are no holes in the data), and each data point represents an equal length in time, we can set bar width to be a fraction of the plot area width, where the width of the plot area is split equally between each data point:
(width-margin.right-margin.left)/data.length
Here's your graph with these changes:
And here's a working example.
Lastly, note that you need to return a value here:
.attr("x", function(d) { return console.log(x(d.data.date)); })
Log before the return statement if you want to debug:
.attr("x", function(d) { console.log(x(d.date.date)); return x(d.data.date); })

Drawing voronoi diagram from a csv file with d3.js

I want to draw voronoi diagram using http://mbostock.github.com/d3/ex/voronoi.html from a set of points in a csv file.
I have modified the code:
var w = 1200,
h = 800;
d3.text("ReMeRi_4_2_2_0.csv", function(datasetText) {
var vertices = d3.csv.parseRows(datasetText);
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "BuPu");
svg.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("class", function(d, i) { return i ? "q" + (i % 9) + "-9" : null; })
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
svg.selectAll("circle")
.data(vertices.slice(1))
.enter().append("circle")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 2)
text1 = svg.append("svg:text")
.text("control")
.attr("x", 150)
.attr("y", 200)
.style("stroke", "orange")
.style("stroke-width", 0)
.style("font-size", "80%")
.style("fill", "orange");
});
The points are drawn correctly but the polygons of the tesselation are not.
I have tried to add header row and the csv.parse() function without success. At the beginning I was thinking the solution was to iterate over the array to parse to float, but I couldn't do it. If that is the reason why the points are drawn correctly anyway?.
The csv file looks like this:
0.0,0.0
116.78032769067718,0.0
193.02842412648215,78.92418723196411
323.01058809711515,54.15210221124609
378.8576448450217,202.5192012545716
...
I think it is, as you suggest, a problem with the numbers getting parsed as Strings rather than Numbers. Even if that's not what's breaking it, it'd be good to fix. This is one way of doing it (might be a more idiomatic way to do it, dunno):
var vertices = d3.csv.parseRows(
datasetText,
function(pt) { return [parseFloat(pt[0]), parseFloat(pt[1])]; })
);
That might fix your problem.