cant figure out d3.js error - javascript

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.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
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 temp = [ {"Gender":"Male","count":5}, {"Gender":"Female","count":2}];
var data=[]
data.push(temp);
x.domain(data.map(function(d) { return d.Gender; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(data) { return x(data.Gender); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(data.count); })
.attr("height", function(d) { return height - y(data.count); });
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.call(d3.axisLeft(y));
<style> /* set the CSS */
.bar { fill: steelblue; }
</style>
<!DOCTYPE html>
<body><script src="//d3js.org/d3.v4.min.js"></script></body>
I want a graph to plot male and female count. count in y-axis and gender in x-axis
my d3.js index.html
where data_json is the this data
[ {"Gender":"Male","count":5}, {"Gender":"Female","count":2}];
im getting graph for only 1 set i.e {"Gender":"Male","count":5} if i set data_json to the same else only axis is displayed.
but not together in same graph. Im new to d3.js and unable to figure out the solution. please help.

Your error is stems from this:
var temp = [ {"Gender":"Male","count":5}, {"Gender":"Female","count":2}];
var data=[]
data.push(temp);
The d3 .data method takes an array. Combined with an enter selection, one element can be appended per item in the array. temp is already an array, by pushing it to data you are making an array like the follows:
[[ {"Gender":"Male","count":5}, {"Gender":"Female","count":2}]]
This array has only one item, a sub-array. The sub-array is really what you want though. This is also creates problems when using the scales:
x.domain(data.map(function(d) { return d.Gender; }));
As each datum (and there is only one) comprises of an array, d.Gender will be undefined, d[1] will be defined.
Instead, use your temp array as your dataset without pushing it into a new array. Then modify the y, x, and height values of each rect to access d.count or d.Gender rather than data.Gender or data.count (as data.count is undefined, and also not datum specific, while d.count is the count associated with the datum bound to each rect).
Take a look at the snippet below which makes these changes:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
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 data = [ {"Gender":"Male","count":5}, {"Gender":"Female","count":2}];
x.domain(data.map(function(d) { return d.Gender; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(data) { return x(data.Gender); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); });
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.call(d3.axisLeft(y));
<style> /* set the CSS */
.bar { fill: steelblue; }
</style>
<!DOCTYPE html>
<body><script src="//d3js.org/d3.v4.min.js"></script></body>

Related

group Barchart gives an errror in d3

This is my expected result
Please Click to see Image
This is the data I am using: -
year,co2,ghg
2000,15.34,34.86
2001,15.54,34.86
2002,16.53,34.06
2003,17.03,29.74
2004,17.48,31.97
2005,16.98,29.66
2006,17.62,31.52
2007,19.82,30.91
2008,17.24,29.93
2009,17.66,29.14
2010,17.31,27.13
2011,17.43,28.61
2012,17.91,28.08
2013,16.67,23.88
2014,16.8,24
2015,16.6,23.67
2016,16.04,23.8
2017,15.78,25.34
2018,15.2,24.87
When I use it in HTML like this It works completely fine
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="main.css">
<title>Stacked Bar Graph</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.js"></script>
</head>
<body>
<script>
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
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(data) {
var subgroups = data.columns.slice(1);
var groups = d3.map(data, function(d){return(d.year)}).keys()
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(5));
var y = d3.scaleLinear()
.domain([0, 40])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y).ticks(8));
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a'])
svg.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.year) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });
})
</script>
</body>
</html>
But when I use it in a separate Js file like this
function GroupBar() {
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
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 + ")");
// Parse the Data
d3.csv("src/data/co2_vs_ghg.csv", function(data) {
// List of subgroups = header of the csv files = soil condition here
var subgroups = data.columns.slice(1);
// List of groups = species here = value of the first column called group -> I show them on the X axis
var groups = d3.map(data, function(d){return(d.year)}).keys()
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSize(5));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, 40])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y).ticks(8));
// Another scale for subgroup position?
var xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, x.bandwidth()])
.padding([0.05])
// color palette = one color per subgroup
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a'])
// Show the bars
svg.append("g")
.selectAll("g")
// Enter in data = loop group per group
.data(data)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + x(d.year) + ",0)"; })
.selectAll("rect")
.data(function(d) { return subgroups.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return xSubgroup(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", xSubgroup.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.key); });
});
}
window.addEventListener("load", GroupBar);
It gives me an error
shown here
I have tried some alternate methods online but none of them work I think I am missing a minute detail but cannot figure out what if anyone can double check this it would be great

D3 Area Chart with a Single Value Not Rendering

When building an area chart in D3.js, when you have only a single value the chart does not render.
For demonstration purposes, I modified the following example: https://d3-graph-gallery.com/graph/area_basic.html to illustrate the problem.
<script>
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 50},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered_comma.csv",
// When reading the csv, I must format variables:
function(d){
return { date : d3.timeParse("%Y-%m-%d")(d.date), value : d.value }
},
// Now I can use this dataset:
function(data) {
data = [data[0]]
// Add X axis --> it is a date format
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) { return d.date; }))
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.value; })])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// Add the area
svg.append("path")
.datum(data)
.attr("fill", "#cce5df")
.attr("stroke", "#69b3a2")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) { return x(d.date) })
.y0(y(0))
.y1(function(d) { return y(d.value) })
)
})
</script>
I would expect that chart to look something like:
If you inspect the element the path element you can see it is rendering, just 0 width/height:

How to combine two CSV files into the same graph with d3js

I have a csv file "data_good.csv" with the simple format : date,value ("date" here is just a number, not date format.)
I've just started using d3js and here is the code i'm using for the graph :
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// get the data
d3.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
});
It's working as expected with several green bars.
But now i have another csv file ("tmp/data_bad.csv") and i would like to have it as a red bar on top of the green one. According to their key "date". (There is no date without value : data_good.csv and data_bad.csv have the same number of lines.)
I think that you should separate logic which draws chart and logic which loads a file.
Also you have to group bars and separate drawing axes from bars.
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_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 drawAxes = function(data) {
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
var drawChart = function(data) {
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
// append the rectangles for the bar chart
svg
.append('g')
.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
}
// get the data
d3.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
drawChart(data);
});
Then if you want to preserve domain scaling to be defined only by "good data" you can abstract it too and use only in "good data reader".
Remember to draw axes when changing scales.
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = screen.width - 200,
height = 200;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#body_d3_graph").append("svg").attr("class","my_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 drawAxes = function(data) {
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
var setDomains = function(data) {
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
drawAxes();
}
var drawChart = function(data) {
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.style("fill", "green")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
}
// get the data
d3.csv("tmp/data_good.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
setDomains(data);
drawChart(data);
});
d3.csv("tmp/data_bad.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
});
drawChart(data);
});

Unable to get stacks in d3 bar chart

I would like to know what i am doing wrong i think its in the axis as the stacked data as well as well as the rect elements are showing. Been stuck now for 2 hours.
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 760 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
innerheight=height + margin.top + margin.bottom
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
// var subgroups = data.columns.slice(1);
// console.log(subgroups);
var subgroups = d3.map(data, function(d){ return(d.Segment)}).keys();
var groups = d3.map(data, function(d){ return(d.Category)}).keys();
console.log(groups);
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSizeOuter(0));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.Sales; })])
.range([ height , 0 ]);
console.log(y.domain())
console.log(y.range())
svg.append("g")
.call(d3.axisLeft(y));
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a']);
ymax= d3.max(data, function(d) { return d.Sales; });
console.log(subgroups);
var stackedData = d3.stack().keys(subgroups);
stackedData = stackedData(data)
console.log(stackedData);
svg.append("g")
.selectAll("g")
// Enter in the stack data = loop key per key = group per group
.data(stackedData)
.enter().append("g")
.attr("fill", function (d) { return (color(d.key)); })
.selectAll("rect")
// enter a second time = loop subgroup per subgroup to add all rectangles
.data(function (d) { return d; })
.enter().append("rect")
.attr("x", function (d) { return x(d.data.Category); })
.attr("y", function (d) { return y(d.data.Sales); })
.attr("height", function (d) { return (height - y(d.data.Sales)) ; })
.attr("width", x.bandwidth())
});
csv is in this format
Row ID,Segment,Category,Sales,Total
1,Consumer,Accessories,84754.742,1007859.426
2,Consumer,Appliances,42852.68,1007859.426
31,Corporate,Phones,89019.204,588163.8014
34,Corporate,Tables,57226.0385,588163.8014
37,Home Office,Art,4799.502,373365.8573

Bar Chart Labels stuck in top left corner

I am trying to make simple chart right now importing data from a CSV. Everything on the chart is working great except for the labels. In element inspect I can see that they are being appended and that their x and y coordinates are even correct, but for some reason they are all trapped in the top left corner in the SVG itself.
I have tried changing the x placement function at first because I thought it just wasn't giving the labels a x position, but upon further inspection the labels have the correct metadata.
//Graph Dimensions
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Set Ranges
var x_scale = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y_scale = d3.scaleLinear()
.range([height, 0]);
//Create SVG object
var svg = d3.select('#chart')
.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 + ")");
//Retrieve data
d3.csv('sales.csv').then(function(data){
//Set domains based on data
x_scale.domain(data.map(function(d) { return d.month; }));
y_scale.domain([0, d3.max(data, function(d) { return d.sales; })]);
//Create bars
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x_scale(d.month); })
.attr("width", x_scale.bandwidth())
.attr("y", function(d) { return y_scale(d.sales); })
.attr("height", function(d) { return height - y_scale(d.sales); });
//Create labels
svg.selectAll('text')
.data(data)
.enter().append('text')
.attr('class', 'label')
.attr("x", function(d) { return x_scale(d.month); })
.attr("y", function(d) { return y_scale(d.sales); })
.attr( 'font-size', 14 )
.attr( 'fill', '#555555' )
.attr( 'text-anchor', 'middle' );
//Add Axes
svg.append("g") //X Axis
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x_scale));
svg.append("g") //Y Axis
.call(d3.axisLeft(y_scale));
})
The only thing im looking for is the labels actually appearing. I can change their location later if needed.

Categories