Creating a bar chart using d3 - javascript

So I'm trying to create a bar chart using d3 and here's my code, for some reason, the bar chart won't show up and I'm getting an error saying:
remote-work.js:14 Uncaught (in promise) TypeError: Cannot read property 'linear' of undefined
at drawBars (remote-work.js:14)
Can anyone advise me on where I'm going wrong it would be greatly appreciated.
async function drawBars() {
// 1. Access data
const data = await d3.csv("./../remote_work.csv")
console.log(data);
//chart = {
const margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom
const x = d => d.Benefits
const y = d3.scale.linear()
.range(height, 0);
const xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
const yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
const 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 + ")");
x.domain(data.map(function(d) { return d.Benefits; }));
y.domain([0, d3.max(data, function(d) { return d.Percentage; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
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("Percentage ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.Benefits); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.Percentage); })
.attr("height", function(d) { return height - y(d.Percentage); });
}
drawBars()

Related

Can't draw rectangular in a barchart

The following code in d3js is supposed to draw a bar chart. It draws the axis and adds the labels to them. But it doesn't draw the bars. My guess is it can't find the svg.selectAll(".bar") but I don't know how I should fix it.
P.S. I was following this tutorial: https://github.com/colorfest/d3js/blob/master/js/bargraph/bargraph.js
function drawBarchart(geography){
var x_labels = ["Very Good", "Good", "Fair", "Poor", "Mentioned", "Not Mentioned"];
var margin = {top: 20, right: 20, bottom: 100, left: 60},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
x = d3.scale.ordinal().rangeRoundBands([0,width], 0.5),
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")
.ticks(5)
var svg = d3.select("#barchart")
.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 + ")");
d3.json("data.json", function (data)
{
x.domain(x_labels.map(function (l)
{
return l;
}));
y.domain([0,300]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-0.5em")
.attr("dy", "-.55em")
.attr("y", 30)
.attr("transform", "rotate(-45)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", "0.8em")
.attr("text-anchor", "end")
.text("Score");
svg.selectAll(".bar")
.data(data[6][geography.id])
.enter()
.append("rect")
.style("fill", "orange")
.attr("x", function(d)
{
return x(d.name);
})
.attr("width", x.rangeBand())
.attr("y", function (d)
{
return y(d["Health"][0]);
})
.attr("height", function (d)
{
return height;
});
}
I updated part of you code block. You were correct with it no finding .bar as the rects were not given the bar class.
svg.selectAll(".bar")
.data(data[6][geography.id])
.enter()
.append("rect")
.attr("class", "bar")
.style("fill", "orange")
.attr("x", function(d)
{
return x(d.name);
})
.attr("width", x.rangeBand())
.attr("y", function (d)
{
return y(d["Health"][0]);
})
.attr("height", function (d)
{
return height;
});
}

Cannot read property 'getHours' of undefined

I am trying to build a simple bar chart with some financial data in json format.
I am having this strange error popup when I try to use my parsed time. When I console log it shows the data to be parsed ok - it doesn't return null or anything.
d3.v3.min.js:1 Uncaught TypeError: Cannot read property 'getHours' of undefined
at H (d3.v3.min.js:1)
at SVGTextElement.t (d3.v3.min.js:1)
at SVGTextElement.arguments.length.each.function.n.textContent (d3.v3.min.js:3)
at d3.v3.min.js:3
at Y (d3.v3.min.js:1)
at Array.Co.each (d3.v3.min.js:3)
at Array.Co.text (d3.v3.min.js:3)
at SVGGElement.<anonymous> (d3.v3.min.js:5)
at d3.v3.min.js:3
at Y (d3.v3.min.js:1)
Its hard to troubleshoot this as I cannot understand which part of my code is responsible for causing this problem because it points toward d3.v3.min.js. I assume that the problem is in d3.extent as I used it to replace d3.max for the x-axis.
This is the code that gives the error:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%H:%M:%S.%L").parse;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%H:%M:%S.%L"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
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 + ")");
d3.json("https://gist.githubusercontent.com/kvyb/cba0e652b7fd9349604cf45ced75fbf9/raw/3f824e76c38479a1a327abbb5c85a5962fec6f21/schudata.json", function(error, data) {
data["bboList"].forEach(function(d) {
d.timeStr = parseDate(d.timeStr);
d.value = +d.ask;
console.log(d.timeStr)
});
x.domain(d3.extent(data, function(d) { return d.timeStr; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
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("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.timeStr); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
});
</script>
</body>
P.S.
There is another issue which is outside the bounds of this question, but needs to be included.
In order to get the bars to render also replaced
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
with
var x = d3.time.scale().range([0, width]);
and changing the width for the bars to a fixed value.
I assume that the problem is in d3.extent...
Yes, you are correct, and the explanation is simple: d3.extent accepts an array as the first argument. However, your data is not an array, but an object with two properties (each one having one array).
Thus, you should select one of them to set your domains (and your bar's data as well):
x.domain(d3.extent(data["bboList"], function(d) {
return d.timeStr;
}));
y.domain([0, d3.max(data["bboList"], function(d) {
return d.value;
})]);
Here is your working code:
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%H:%M:%S.%L").parse;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%H:%M:%S.%L"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
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 + ")");
d3.json("https://gist.githubusercontent.com/kvyb/cba0e652b7fd9349604cf45ced75fbf9/raw/3f824e76c38479a1a327abbb5c85a5962fec6f21/schudata.json", function(error, data) {
data["bboList"].forEach(function(d) {
d.timeStr = parseDate(d.timeStr);
d.value = +d.ask;
});
x.domain(d3.extent(data["bboList"], function(d) { return d.timeStr; }));
y.domain([0, d3.max(data["bboList"], function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
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("Value ($)");
svg.selectAll("bar")
.data(data["bboList"])
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.timeStr); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
});
path, line {
fill: none;
stroke: black;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
PS: Have in mind that I'm only answering your question ("Cannot read property 'getHours' of undefined"). You still have some problems drawing those bars.

Creating A Bar chart in d3.js from an JSON

I have this JSON data
{"1960":"133.55501327769","1961":"134.159118941963","1962":"134.857912280869","1963":"134.504575565342","1964":"134.105211273476","1965":"133.569625896451","1966":"132.675635192775","1967":"131.665502129354","1968":"129.190980115918","1969":"126.736756382819","1970":"124.382808900193","1971":"122.133431342027","1972":"120.020185557559","1973":"118.087531093609","1974":"116.132988067096","1975":"114.100918174437","1976":"111.980005447216","1977":"109.783821762662","1978":"106.033489239906","1979":"102.341720681455","1980":"98.7390023274647","1981":"95.2412508672801","1982":"91.7911923993222","1983":"88.0011769487606","1984":"84.2072557839419","1985":"80.3593225600132","1986":"76.4415956498419","1987":"72.5145803648751","1988":"71.1706639452677","1989":"69.8887679924858","1990":"69.0044133814268","1991":"67.7559924352118","1992":"66.9284506867798","1993":"64.9489678572737","1994":"62.9227777228154","1995":"60.7070695260477","1996":"58.5966308804751","1997":"56.4401276304142","1998":"55.5315395528949","1999":"54.6587808352011","2000":"53.8314102398679","2001":"52.9015276443892","2002":"51.9907926813042","2003":"51.5228563035101","2004":"51.1032496482833","2005":"50.7325902239383","2006":"50.3291352282938","2007":"49.9998514069402","2008":"49.8870459355469","2009":"49.7812066054555","2010":"49.6729747116906","2011":"49.5360469363113","2012":"49.3837446924523","2013":"48.7965576984378","2014":"48.1964180547578","2015":"47.5501940499984","Country Name":"Arab World","Country Code":"ARB","Indicator Name":"Adolescent fertility rate (births per 1,000 women ages 15-19)","Indicator Code":"SP.ADO.TFRT","":""}
which I convert from CSV to JS object.
How do I build a bar chart with d3.js where the key is the X axis and the values are the Y axis?
here's what I came up with
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// set the ranges
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
// define the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
// add the SVG element
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 + ")");
d3.queue()
.defer(d3.csv, "http://localhost:8000/HNP_Data.csv")
.defer(d3.csv, "http://localhost:8000/HNP_Series.csv")
.await(analyze);
var dataToVis;
var array = []
function analyze(error,data,series) {
dataToVis = data[2];
console.log(JSON.stringify(dataToVis));
for (var key in dataToVis) {
if(!isNaN(key) && key.length >0) {
var p = new Object;
p.year = key;
p.frequency = p(dataToVis[key]);
array.push(p);
}
}
array.forEach(function(d){
d.year = d.year;
d.frequency = +d.frequency;
});
x.domain(data.map(function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
// add axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 5)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
// Add bar chart
svg.selectAll("bar")
.data(array)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.year); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
}
but I end up with this error.
Error: <rect> attribute y: Expected length, "NaN".
Error: <rect> attribute height: Expected length, "NaN".

D3 barchart: first bar overlaps axis label

I have built a barchart with D3 but I'm running into these 2 minor issues which I am not be able to fix.
1) The first bar of the barchart overlaps the y axis
2) The labels of the y axis ticks are disappearing on the left side when the number(Volume) exceeds 1 million.
Here is a snapshot of what is happening:
Here's my D3 code:
function drawGraph(dataGraph){
if (dataGraph.length == 0){
$('#graph-container').append('<h3>No Data Match</h3>');
return;
}
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 900 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
padding = 15;
var max = d3.max(dataGraph, function(d) { return +d.VOLUME;} );
var maxDate = d3.max(dataGraph, function(d) { return +d.AS_OF_DATE;} );
var x = d3.scale.ordinal()
.domain(dataGraph.map(function(d) {return d.AS_OF_DATE;}))
.rangeRoundBands([0, (width- padding )], .1);
var y = d3.scale.linear()
.domain([0,max])
.range([height,0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(10);
var svg = d3.select("#graph-container")
.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 + ")");
svg.append('g')
.attr('class', 'x axis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.style("color", "#333")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style("color", "#333")
.text("Volume");
svg.selectAll('.bar')
.data(dataGraph)
.enter().append('rect')
.attr('class', 'bar')
.attr("x", function(d) { return x(d.AS_OF_DATE); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.VOLUME); })
.attr("height", function(d) { return height - y(d.VOLUME); });
}

Colour of points repeat after 10 data sets on D3.js scatter plot

Problem:
I've got a D3.js scatter plot that has 16 different data sets, but it seems like D3 has only 10 different colours built-in before it repeats. You can see what I mean by clicking that link.
Code:
function updatePlot() {
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.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.sepalLength = +d.sepalLength;
d.sepalWidth = +d.sepalWidth;
});
x.domain(d3.extent(data, function(d) { return d.sepalWidth; })).nice();
y.domain(d3.extent(data, function(d) { return d.sepalLength; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("HPF/LPF Intensity Ratio");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("HPF Intensity (relative units)")
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.sepalWidth); })
.attr("cy", function(d) { return y(d.sepalLength); })
.style("fill", function(d) { return color(d.species); });
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
}
(The code is pretty much copy/paste from here with a little customisation in the areas of D3 that I understand)
Thanks!
This part of the docs has the answer: Ordinal-Scales#categorical-colors. Thanks to user and Lars Kotthoff!
Simply replaced category10 with category20.

Categories