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
Related
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:
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
i am using D3 to draw line graph in JavaScript. Line of Line graph is drawn right but date on x axis is not correct and it is also showing one extra tick on x axis. I had also tried changing tick format but i failed. I don't know where i am doing wrong. please help. here is my code
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right // Use the window's width
, height = window.innerHeight - margin.top - margin.bottom; // Use the window's height
// The number of datapoints
var n = 4;
var xScale = d3.scaleLinear()
.domain([0, n-1]) // input
.range([0, width]); // output
var parseTime = d3.timeParse("%d-%b-%y");
// 6. Y scale will use the randomly generate number
var yScale = d3.scaleLinear()
.domain([0, 100]) // input
.range([height, 0]); // output
d3.csv("Data_vis.csv", function(data){
//console.log(data);
var nov_11_percent= 22;
var oct_16_percent= 25;
var nov_13_percent= 24;
var oct_22_percent= 21;
var dataset2=[{"date":'2018-09-11', "value": nov_11_percent},
{"date":'2018-10-16', "value": oct_16_percent},
{"date":'2018-10-22', "value": oct_22_percent},
{"date":'2018-11-13', "value": nov_13_percent}];
dataset2.forEach(function(d) {
d.date = new Date(d.date);
});
var line = d3.line()
.x(function(d, i) {
//alert(parseTime(d.date));
return xScale(d.date);
})
.y(function(d,i ) {
return yScale(d.value);
})
.curve(d3.curveMonotoneX)
xScale.domain(d3.extent(dataset2, function(d) {
return d.date; }));
yScale.domain([0, d3.max(dataset2, function(d) {
return d.value;
})]);
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 + ")");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale)
.ticks(4)
.tickFormat(d3.timeFormat("%Y-%m-%d")));
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset2)
.attr("class", "line")
.attr("d", line);
svg.selectAll(".dot")
.data(dataset2)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d) { return xScale(d.date) })
.attr("cy", function(d) { return yScale(d.value) })
.attr("r", 10);
});
any help would be much appreciated. thank you
Use a point scale instead of a linear scale for x.
var xScale = d3
.scalePoint()
.range([0, width])
.domain(dataset2.map(i => i.date))
const margin = { top: 50, right: 50, bottom: 50, left: 50 }
const width = window.innerWidth - margin.left - margin.right // Use the window's width
const height = window.innerHeight - margin.top - margin.bottom // Use the window's height
const dataset2 = [{ date: "2018-09-11", value: 22 },{ date: "2018-10-16", value: 25 },{ date: "2018-10-22", value: 24 },{ date: "2018-11-13", value: 21 }]
dataset2.forEach(function(d) {
d.date = new Date(d.date)
})
// 6. Y scale will use the randomly generate number
const yScale = d3
.scaleLinear()
.domain([
0,
d3.max(dataset2, function(d) {
return d.value
})
]) // input
.range([height, 0]) // output
const xScale = d3
.scalePoint()
.range([0, width])
.domain(dataset2.map(d => d.date))
const line = d3
.line()
.x(function(d, i) {
return xScale(d.date)
})
.y(function(d, i) {
return yScale(d.value)
})
.curve(d3.curveMonotoneX)
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 + ")")
svg
.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(
d3
.axisBottom(xScale)
.tickFormat(d3.timeFormat("%m/%d"))
)
svg
.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale))
svg
.append("path")
.datum(dataset2)
.attr("class", "line")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "black")
svg
.selectAll(".dot")
.data(dataset2)
.enter()
.append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d) {
return xScale(d.date)
})
.attr("cy", function(d) {
return yScale(d.value)
})
.attr("r", 10)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Codepen
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>
In the following example I mapped histograms on letter vs frequency. Now, I want a line chart also for the same data without making much change. This means just a red line joining top of histograms. Can someone help me out?
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
data.forEach(function(){
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
});
</script>
</body>
</html>
HISTOGRAM:
First, get rid of that data.forEach: why do you want to paint everything 3 times?
After that, define your line generator:
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);;
Here, x.bandwidth()/2 will put the line in the middle of the top of each bar. I'm using d3.curveCardinal, but you have other options for the curve.
Then, append the line:
g.append("path")
.datum(data)
.attr("d", line);
Here is the demo:
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
//Our basic data
var data = [
{frequency:0.08, letter:"A"},
{frequency:0.11,letter:"B"},
{frequency:0.13,letter:"C"}
];
var svg = d3.select("svg");
var margin = {top: 40, bottom: 40, left: 40, right: 40};
var width = svg.attr("width") - margin.left - margin.right;
var height = svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.6);
var y = d3.scaleLinear().rangeRound([height, 0]);
//defining our main g in svg
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Looping for data bars
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
var line = d3.line()
.x(function(d){ return x(d.letter) + x.bandwidth()/2})
.y(function(d){ return y(d.frequency)})
.curve(d3.curveCardinal);
g
.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g
.append("g")
.call(d3.axisLeft(y).ticks(10, "%"));
g
.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) { return x(d.letter); })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.frequency); });
g.append("path")
.datum(data)
.attr("d", line)
.attr("stroke", "red")
.attr("fill", "none");
</script>