bar chart not coinciding with axes in d3.js - javascript

I am trying to create a bar chart in d3.js and unfortunately, the bars are not on the axes. They are appearing somewhere else. Can you please point out what I am doing wrong here.
I think it has do with the placing of axes and all but I am unable to figure it out.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="rer"></div>
<script>
var width = 400, height = 400;
var data1 = { 2011: 9, 2012: 12, 2013: 10, 2014: 8, 2015: 12, 2016: 20 }
data_ready = d3.entries(data1);
var barColor = '#50740a';
var svg = d3.select("#rer")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.domain(data_ready.map((function (d) { return d.key; })))
.range([0, width - 100]);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data_ready, function (d) { return d.value; })])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(100, 20)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(100, " + xAxisTranslate + ")")
.call(x_axis);
svg.selectAll(".bar")
.data(data_ready)
.enter().append("rect")
.attr('class', 'bar')
.attr("x", function (d) { return xscale(d.key); })
.attr("y", function (d) { return yscale(d.value); })
.attr("width", xscale.bandwidth())
.attr("height", function (d) { return height - yscale(d.value); })
.attr("fill", barColor)
</script>
</body>
</html>

I tried to correct it and got the graph as below. Can someone please explain the attr ( transform, translate ) thing. This seems to be the issue. I did a trial and error method to place the position of the graph. Is there a better way to do this ?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="rer"></div>
<script>
var width = 400,
height = 400;
var data1 = {
2011: 9,
2012: 12,
2013: 10,
2014: 8,
2015: 12,
2016: 20
}
data_ready = d3.entries(data1);
var barColor = '#50740a';
var svg = d3.select("#rer")
.append("svg")
.attr("width", width)
.attr("height", height);
var xscale = d3.scaleBand()
.domain(data_ready.map((function(d) {
return d.key;
})))
.range([0, width - 100])
.padding(0.1);
var yscale = d3.scaleLinear()
.domain([0, d3.max(data_ready, function(d) {
return d.value;
})])
.range([height / 2, 0]);
var x_axis = d3.axisBottom().scale(xscale);
var y_axis = d3.axisLeft().scale(yscale);
svg.append("g")
.attr("transform", "translate(100, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(100, " + xAxisTranslate + ")")
.call(x_axis);
svg.selectAll(".bar")
.data(data_ready)
.enter().append("rect")
.attr('class', 'bar')
.attr("x", function(d) {
return xscale(d.key);
})
.attr("y", function(d) {
return yscale(d.value);
})
.attr("width", xscale.bandwidth())
.attr("height", function(d) {
return height / 2 - yscale(d.value);
})
.attr("fill", barColor)
.attr("transform", "translate(100, 10)")
</script>
</body>
</html>

Related

Why are labels/text not rendering on this d3.js scatter plot?

I've been practicing using D3.js by making a simple scatter plot with some dummy data. Unfortunately after spending an hour of googling and looking at examples, I still can't figure out why my labels are not rendering. Could someone point me in the right direction on what I'm doing wrong?
I'm assuming the text is not rendering because when I put in dummy numbers for the X and Y attributes, I still don't get the labels to render on the page.
const points = [
[1,2],
[2,4],
[3,6],
[4,8],
[5,10]
]
const w = 500
const h = 500
const maxX = d3.max(points, (d) => d[0]);
const maxY = d3.max(points, (d) => d[1]);
console.log(maxX, maxY);
const scaleX = d3.scaleLinear()
.domain([0, maxX])
.range([0, w]);
const scaleY = d3.scaleLinear()
.domain([0, maxY])
.range([0,h]);
const svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h)
;
svg.selectAll("circle")
.data(points)
.enter()
.append('circle')
.attr('cx',(d) => scaleX(d[0]))
.attr('cy',(d) =>h - scaleY(d[1]))
.attr('r', 5)
.attr('fill', 'black')
// adding axae
const xAxis = d3.axisBottom(scaleX);
const yAxis = d3.axisLeft(scaleY);
svg.append("g")
.attr("transform", "translate(0," + (h -20) + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(" + 20 + ", 0)")
.call(yAxis);
// adding labels
svg.selectAll("text")
.data(points)
.enter()
.append("text")
.text((d) => (d[0] + "," + d[1]))
.attr("x", (d) => scaleX(d[0] + 10))
.attr("y", (d) => scaleY(d[1]))
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script src='app.js'></script>
</body>
</html>
The code was almost correct, whenever you want to insert(enter) some nodes you must check if there is no selection corresponding to that.
As you have already rendered the axes, so there are already text nodes in my chart but i wanted to create an empty selection. Always use a unique indentifier for that
Replace // adding labels
svg.selectAll("text")
with // adding labels
svg.selectAll(".mylables")
Change x and y coordinate as below, you can tweek accordingly.
.attr("x", (d) => scaleX(d[0]) + 10)
.attr("y", (d) => h - scaleY(d[1]))
const points = [
[1, 2],
[2, 4],
[3, 6],
[4, 8],
[5, 10]
]
const w = 500
const h = 500
const maxX = d3.max(points, (d) => d[0]);
const maxY = d3.max(points, (d) => d[1]);
console.log(maxX, maxY);
const scaleX = d3.scaleLinear()
.domain([0, maxX])
.range([0, w]);
const scaleY = d3.scaleLinear()
.domain([0, maxY])
.range([0, h]);
const svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
svg.selectAll("circle")
.data(points)
.enter()
.append('circle')
.attr('cx', (d) => scaleX(d[0]))
.attr('cy', (d) => h - scaleY(d[1]))
.attr('r', 5)
.attr('fill', 'black')
// adding axae
const xAxis = d3.axisBottom(scaleX);
const yAxis = d3.axisLeft(scaleY);
svg.append("g")
.attr("transform", "translate(0," + (h - 20) + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(" + 20 + ", 0)")
.call(yAxis);
// adding labels
svg.selectAll(".mylabels")
.data(points)
.enter()
.append("text")
.style('class', 'mylables')
.text((d) => {
debugger;
return (d[0] + "," + d[1])
})
.attr("x", (d) => scaleX(d[0]) + 10)
.attr("y", (d) => h - scaleY(d[1]))
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script src='app.js'></script>
</body>
</html>

D3js: Dropdown update in bar chart

I'm using d3js to build a bar chart from a .csv file, and as my programming knowledge is not good, I get an example from the web and start to working in. I can get a specific value from the .csv and draw the chart, but I would like to implement a dropdown list to select a specific value from the .csv as showed in the image below.
Imagem:
I'm unable to load this data from .csv. It doesn't seem complicated, but I tried it in some ways and it didn't work out. Could someone give me any tips on how to do this?
Best regards!
.csv file
country,year,hdi,gdp,unemp
Australia,2000,45,5.6,9
Austria,2000,54,6.1,8
Canada,2000,23,4.5,6
...
...
HTML + JS
<html>
<head>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
</style>
<script src="//d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div>
<select id="S_indicators"></select>
</div>
<svg width="560" height="300"></svg>
<script>
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.scaleBand()
.rangeRound([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
d3.csv("output.csv").then(function (data) {
var allGroup = ["gdp","hdi","unemp"]
d3.select("#S_indicators")
.selectAll('myOptions')
.data(allGroup)
.enter()
.append('option')
.text(function (d) { return d; })
.attr("value", function (d) { return d; })
x.domain(data.map(function (d) {
return d.year;
}));
y.domain([0, d3.max(data, function (d) {
return Math.max(d.unemp);
})]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
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")
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return x(d.year);
})
.attr("y", function (d) {
return y(Number(d.unemp));
})
.attr("width", x.bandwidth())
.attr("height", function (d) {
return height - y(Number(d.unemp));
});
});
</script>
</body>
</html>
-Add a dropdown to the html file (I named it dd):
<!DOCTYPE html>
<html lang="en">
<title>My Scatter2</title>
<body>
<select id="dd"></select>
<div id="wrap"></div>
<script src="https://d3js.org/d3.v5.js"></script>
<script src="scatterplot.js"></script>
<link rel='stylesheet' href='styles.css'>
</body>
</html>
Populate it in D3 via:
var specs = ['A','B','C']
var dropdown = d3.select('#dd')
dropdown.selectAll('option')
.data(specs)
.enter().append("option")
.attr("value",d=>d)
.text(d => d)
Or if you want an observeable example:
https://observablehq.com/#kickout/basic-scatterplot

d3js color scale from csv files

Can you show me how to set color scales with data from csv file?
var color = d3.scaleLinear()
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" type="text/css" href="main.css">
<title>Zadatak 2</title>
<style>
/*.bar {
fill: dodgerblue;
}*/
</style>
</head>
<body>
<svg width="800" height="700"></svg>
<script>
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.csv("XYZ.csv", function(error, data) {
if (error) {
throw error;
}
var color = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.value; })])
.range(["red", "blue"])
xScale.domain(data.map(function(d) { return d.fruit; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return d;
}).ticks(6));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.fruit); })
.attr("y", function(d) { return yScale(d.value); })
.attr("fill",function(d){ return color(d) })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); });
});
</script>
</body>
</html>
.domain([0, d3.max(data, function(d) { return d.value; })])
.range(["red", "blue"])
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.fruit); })
.attr("y", function(d) { return yScale(d.value); })
.attr("fill",function(d){ return color(d) })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); });
});
csv file data
fruit,value
Apple,2
Orange,4
Kiwi,3
Peach,6
Grape,2
this one dont work. pls help me. Thanks

D3 bar chart uncaught TypeError

I'm trying to make a d3 bar chart using d3 version 4 and it gives me the following error: Uncaught TypeError: d3.scaleOrdinal(...).domain(...).rangeBands is not a function, i added my code snippet, can anybody fix it for me?
var bardata = [20, 30, 95,15];
var height = 400,
width = 600,
barWidth = 50,
barOffset = 5;
var yScale = d3.scaleLinear()
.domain([0, d3.max(bardata)])
.range([0, height]);
var xScale = d3.scaleOrdinal()
.domain(d3.range(0, bardata.length))
.rangeBands([0, width])
d3.select('#chart').append('svg')
.attr('width', width)
.attr('height', height)
.style('background', '#C9D7D6')
.selectAll('rect').data(bardata)
.enter().append('rect')
.style('fill', '#C61C6F')
.attr('width', xScale.rangeBand())
.attr('height', function(d){
return yScale(d);
})
.attr('x', function(d,i){
return xScale(i);
})
.attr('y', function(d){
return height - yScale(d);
})
<!DOCTYPE html>
<html>
<head>
<title>Line Chart</title>
<meta charset="8-UTF">
<link rel="stylesheet" src="css/style.css"> </head>
<body>
<div class="container">
<h2>D3 Graphic</h2>
<div id="chart"></div>
</div>
<script src="js/d3.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
In the (not so) new D3 v4:
The ordinal.rangeBands and ordinal.rangeRoundBands methods have been replaced with a new subclass of ordinal scale: band scales. (source)
Thus, you have to do three changes here:
scaleBand instead of scaleOrdinal
xScale.bandwidth() instead of xScale.rangeBands()
setting the padding in the scale
With those changes, this is your code:
var bardata = [20, 30, 95, 15];
var height = 400,
width = 600,
barWidth = 50,
barOffset = 5;
var yScale = d3.scaleLinear()
.domain([0, d3.max(bardata)])
.range([0, height]);
var xScale = d3.scaleBand()
.domain(d3.range(0, bardata.length))
.padding(0.1)
.range([0, width])
d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.style('background', '#C9D7D6')
.selectAll('rect').data(bardata)
.enter().append('rect')
.style('fill', '#C61C6F')
.attr('width', xScale.bandwidth())
.attr('height', function(d) {
return yScale(d);
})
.attr('x', function(d, i) {
return xScale(i);
})
.attr('y', function(d) {
return height - yScale(d);
})
<script src="https://d3js.org/d3.v4.min.js"></script>

d3 v4 Why can't I get my x axis and bars to line up?

I've got as far as making my bar chart with an x axis but can't work out why I can't get the bars and the tick spacing on the x axis to line up exactly. At the moment the ticks are slightly to the right of center of the bar.
csv file example:
crop,records
CASSAVA,350
MAIZE,226
TOMATOES,137
code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {top: 20, right: 30, bottom: 100, left: 40};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
d3.csv("crops.csv", function(error, data) {
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w]);
// create the svg
var svg = d3.select("body")
.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 + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * xScale.bandwidth() + 5;
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth() - 5)
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
</script>
</body>
Am I doing it right with i * xScale.bandwidth() + 5 and xScale.bandwidth() - 5 in the x and width attributes of rect or is this not the correct way to do it? How do I change the spacing of the ticks on the x axis if I change the bars? They both use xScale so I feel it must be something to do with that.
I'd make the following changes.
First, introduce a padding to your xScale:
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
This will space the bars by a percent of the bandWidth.
Second, place your bars using your xScale:
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop); //<-- place by xScale
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth()) //<-- no -5 padding takes care of breaks
Runnable code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Abbie's attempt at D3</title>
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style>
svg {
background-color: rgba(227, 227, 227, 0.97);
}
.bar {
margin: 20px;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {
top: 20,
right: 30,
bottom: 100,
left: 40
};
var w = 500 - margin.left - margin.right;
var h = 500 - margin.top - margin.bottom;
var barPadding = 5;
var cropData;
// load the csv file
//d3.csv("crops.csv", function(error, data) {
var data = [{
"crop": "CASSAVA",
"records": "350"
}, {
"crop": "MAIZE",
"records": "226"
}, {
"crop": "TOMATOES",
"records": "137"
}];
data.forEach(function(d) {
d.records = +d.records;
});
cropData = data;
var arrayLength = cropData.length;
var yMax = d3.max(cropData, function(d) {
return d.records;
});
var yScale = d3.scaleLinear()
.domain([0, yMax])
.range([h, 0]);
var xScale = d3.scaleBand()
.domain(cropData.map(function(d) {
return d.crop;
}))
.rangeRound([0, w])
.padding(0.1);
// create the svg
var svg = d3.select("body")
.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 + ")");
// bars
svg.selectAll("rect")
.data(cropData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(d.crop);
})
.attr("y", function(d) {
return yScale(d.records);
})
.attr("width", xScale.bandwidth())
.attr("height", function(d) {
return h - yScale(d.records);
})
.attr("fill", "teal")
.attr("class", "bar");
// x Axis
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-60)"
});
// y Axis
var yAxis = d3.axisLeft(yScale);
svg.append("g")
.call(yAxis);
// });
</script>
</body>

Categories