Stacked bar chart: how to make diverging and vertically centered? - javascript

I am using D3 to make a stacked bar chart (for more artistic purposes than scientific). I want to design my stacked bar chart to be centered around one group, with half above and half below an invisible line, and have the other two groups be on either side of the line.
Currently, my graph looks like this
But I want it to look more like this
My code is here:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = { top: 40, right: 30, bottom: 20, left: 20 };
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
d3.csv("test.csv").then((data) => {
let x = d3
.scaleBand(
data.map((d) => d.Time),
[margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear([0, 500], [height - margin.bottom, margin.top]);
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = data.columns.slice(1);
const color = d3.scaleOrdinal(subgroups, [
"#e41a1c",
"#377eb8",
"#4daf4a",
]);
const stackedData = d3.stack().keys(subgroups)(data);
console.log(stackedData);
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
});
</script>
</body>
</html>
and csv:
Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188
Does anyone know how I could vertically center each line around the middle group? Is this something to do in the data pre-processing or in the graph making itself?

You have to use the correct offset, in this case d3.offsetWiggle:
const stackedData = d3.stack().offset(d3.stackOffsetWiggle)
In this solution I'm flattening the stacked data and getting the extent, which I'll pass to the y scale:
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
Finally, I'm just moving the x axis to the middle of the y range. Also, I'm hardcoding the stack keys, but making the sequence programatically is trivial, as well as some other details you'll have to adjust.
Here's the result:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Stacked Bar</title>
</head>
<body>
<div class="container">
<div id="chart"></div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const width = 860,
height = 400,
margin = {
top: 40,
right: 30,
bottom: 20,
left: 20
};
const svg = d3
.select("#chart")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
const csv = `Time,team1,team2,middle
0,5,2,70
1,10,13,89
2,4,15,110
3,6,16,145
4,12,2,167
5,42,3,111
6,6,4,108
7,7,5,92
8,8,34,140
9,12,89,190
10,22,90,398
11,42,91,459
12,60,23,256
13,69,13,253
14,43,11,188
15,42,7,167
16,21,9,124
17,16,12,156
18,7,14,167
19,12,13,188`;
const data = d3.csvParse(csv);
let x = d3
.scaleBand(
data.map((d) => d.Time), [margin.left, width - margin.right]
)
.padding([0.2]);
let y = d3.scaleLinear().range([height - margin.bottom, margin.top]);
svg.append("g").attr("transform", `translate(${margin.left},0)`);
// .call(d3.axisLeft(y).tickSize(-width + margin.left + margin.right));
//protein,carbs,fiber
const subgroups = ["team1", "middle", "team2"];
const color = d3.scaleOrdinal(subgroups, [
"#377eb8",
"#4daf4a",
"#e41a1c"
]);
const stackedData = d3.stack().offset(d3.stackOffsetWiggle).order(d3.stackOrderNone).keys(subgroups)(data);
const flatData = stackedData.flat(2);
y.domain(d3.extent(flatData));
svg
.append("g")
.selectAll("g")
.data(stackedData)
.join("g")
.attr("fill", (d) => color(d.key))
.selectAll("rect")
.data((d) => d)
.join("rect")
.attr("x", (d) => x(d.data.Time))
.attr("y", (d) => y(d[1]))
.attr("height", (d) => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
svg
.append("g")
.attr("transform", `translate(0,${margin.top + (height - margin.bottom)/2})`)
.call(d3.axisBottom(x));
let legendGroup = svg
.selectAll(".legend-group")
.data(subgroups)
.join("g")
.attr("class", "legend-group");
legendGroup
.append("circle")
.attr("cx", (d, i) => 10 + i * 75)
.attr("cy", 10)
.attr("r", 3)
.attr("fill", (d, i) => color(i));
legendGroup
.append("text")
.attr("x", (d, i) => 20 + i * 75)
.attr("y", 15)
.text((d, i) => subgroups[i]);
</script>
</body>
</html>

Related

How can i set each text label vertically in d3?

Please, have a look. I feel like i'm somewhere near,after searching stackoverflow.
But still can set each label(it's date) vertically so it's readable.
https://codepen.io/DeanWinchester88/pen/XWgjjeW
const svg = d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h)
.attr("style", "outline: thin solid red;");
svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ) )
.attr("width", 8)
.attr("height", (d) => d[1] /10 )
.attr("class", 'bar')
.attr("data-date",(d) => d[0])
.attr("data-gdp",(d) => d[1])
.append("title")
.text((d) => d)
svg.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text((d) => d)
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ))
// .attr("transform", "rotate(-5)")
.attr('transform', (d,i)=>{
return 'translate( i * 10, (d) => ( h - (d[1]/10 ) )) rotate(125)';})
});
You can achieve this with just a little tweak. Instead of setting the x and y attribute, we can add that to the transform instead. So we move the object to the position that we want, then rotate it.
That is:
.attr("transform", (d,i) => "translate(" + i*10 + "," + (h-(d[1]/10)) + "),rotate(90)");
Remove the x and y lines before adding that.
That will show the text overlapping with the bars. To have the text above the bar we can just add:
.attr("text-anchor", "end")
You can also change the font-size, to ensure the text doesn't overlap with the other text:
text {
font-size: 12px;
}
The final result is:
let dataSet;
let data;
// let pizda =[1,5,8,15,35,76,36,
function readTextFile(file, callback) {
var rawFile = new XMLHttpRequest();
rawFile.overrideMimeType("application/json");
rawFile.open("GET", file, true);
rawFile.onreadystatechange = function() {
if (rawFile.readyState === 4 && rawFile.status == "200") {
callback(rawFile.responseText);
}
}
rawFile.send(null);
}
//usage:
readTextFile("https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json", function(text){
data = JSON.parse(text);
dataSet = data["data"]
console.log(dataSet)
const w = 3400;
const h = 2000;
const svg = d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h)
.attr("style", "outline: thin solid red;");
svg.selectAll("rect")
.data(dataSet)
.enter()
.append("rect")
.attr("x", (d,i) => i * 10)
.attr("y", (d) => ( h - (d[1]/10 ) ) )
.attr("width", 8)
.attr("height", (d) => d[1] /10 )
.attr("class", 'bar')
.attr("data-date",(d) => d[0])
.attr("data-gdp",(d) => d[1])
.append("title")
.text((d) => d)
svg.selectAll("text")
.data(dataSet)
.enter()
.append("text")
.text((d) => d)
.attr("text-anchor", "end")
.attr("transform", (d,i) => "translate(" + i*10 + "," + (h-(d[1]/10)) + "),rotate(90)");
});
.bar:hover {
fill: black;
}
.bar {
margin: 6px;
fill: #a87a44;
}
text {
font-size: 12px;
}
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title id = "title">Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>
<body>
</body>
</html>

Interaction with the legend D3.js grouped bar chart

I plotted the Grouped Bar chart using D3.js.
Here:
https://jsfiddle.net/astropsych/uc3xy0bj/5/
I need to add dynamic interaction. So that when you hover over the legend, the corresponding values on the chart are highlighted, and when you click on the legend, the corresponding values disappear, and the chart is rebuilt. Help me please
This is a very simple example of how you could achive this:
var svg = d3.select("svg"),
margin = { top: 20, right: 20, bottom: 30, left: 220 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
const data = [
{ key: 'A', value: 20 },
{ key: 'B', value: 10 },
{ key: 'A', value: 8 },
{ key: 'B', value: 25 }
]
const keys = ['A', 'B']
const fade = (opacity) => {
return e => {
circles.style('opacity', d => d.key === e.target.id ? 1 : opacity)
textLegend.style('opacity', d => d === e.target.id ? 1 : opacity)
rectLegend.style('opacity', d => d === e.target.id ? 1 : opacity)
}
}
const circles = svg
.append('g')
.selectAll('circle')
.data(data)
.join('circle')
.attr('id', d => d.key)
.attr('cx', (d, i) => 50 * i + 200)
.attr('cy', 100)
.attr('r', d => d.value)
.attr('fill', d => d.key === 'A' ? 'red' : 'blue')
.on('mouseover', fade(0.4))
.on('mouseout', fade(1))
const textLegend = svg
.append('g')
.selectAll('text')
.data(keys)
.join('text')
.attr('id', d => d)
.attr('x', 100)
.attr('y', (d, i) => i * 20 + 50)
.text(d => d)
.on('mouseover', fade(0.4))
.on('mouseout', fade(1))
const rectLegend = svg
.append('g')
.selectAll('rect')
.data(keys)
.join('rect')
.attr('id', d => d)
.attr('x', 70)
.attr('y', (d, i) => i * 20 + 40)
.attr('width', 25)
.attr('height', 10)
.attr('fill', d => d === 'A' ? 'red' : 'blue')
.on('mouseover', fade(0.4))
.on('mouseout', fade(1))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/style.css">
<title>New Graph D3 - horizontal</title>
<div class="chart">
<svg width="800" height="600"></svg>
</div>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script src="/script.js"></script>
</body>
</html>

Draw line chart on top of area chart

I am building a chart using D3js v5.12.0.
I have already done the area chart that has the variable year in X axis and variable earth_footprint on Y axis.
The data is in this link: https://raw.githubusercontent.com/cvrnogueira/CODWorkData/master/database/final_data_set.json
I wish to draw a line chart on the top of the area chart. This line chart should have the variable year in X axis and pop_total on the Y axis.
pop_total is another variable that is on the data.
But I can't manage how to, I saw some tutorials of how to draw a line in bar chart, but when I adapt to my code that is a area chart it does not work.
Thanks in advance
CSS
#area-chart {
text-align: center;
margin-top: 40px;
}
.selection {
fill: none;
}
HTLM
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="area-chart"></div>
</body>
</html>
JS
var url = "http://raw.githubusercontent.com/cvrnogueira/CODWorkData/master/database/final_data_set.json";
d3.json(url)
.then(function(data) {
data = data.filter(dataPoint => dataPoint.country_code == 'BRA');
data = data.filter(element => element.hasOwnProperty("earth_footprint"));
const heightValue = 300;
const widthValue = 600;
// Create SVG and padding for the chart
const svg = d3
.select("#area-chart")
.append("svg")
.attr("viewBox", `0 0 ${widthValue} ${heightValue}`)
;
const strokeWidth = 1.5;
const margin = { top: 0, bottom: 20, left: 30, right: 20 };
const chart = svg.append("g").attr("transform", `translate(${margin.left},0)`);
const width = 600 - margin.left - margin.right - (strokeWidth * 2);
const height = 300 - margin.top - margin.bottom;
const grp = chart
.append("g")
.attr("transform", `translate(-${margin.left - strokeWidth},-${margin.top})`);
// Create scales
const yScale = d3
.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, dataPoint => dataPoint.earth_footprint)]);
const xScale = d3
.scaleLinear()
.range([0, width])
.domain(d3.extent(data, dataPoint => dataPoint.year));
const area = d3
.area()
.x(dataPoint => xScale(dataPoint.year))
.y0(height)
.y1(dataPoint => yScale(dataPoint.earth_footprint));
// Add area
grp
.append("path")
.attr("transform", `translate(${margin.left},0)`)
.datum(data)
.style("fill", "lightblue")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", strokeWidth)
.attr("d", area);
// Add the X Axis
chart
.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale).ticks(data.length));
// Add the Y Axis
chart
.append("g")
.attr("transform", `translate(0, 0)`)
.call(d3.axisLeft(yScale));
chart.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of Earths");
chart.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Year");
});
For showing that line you need a line generator:
const line = d3.area()
.x(dataPoint => xScale(dataPoint.year))
.y(dataPoint => yScale(dataPoint.pop_total));
However, your yScale gets the maximum of earth_footprint, and the pop_total values would be way out of scale. So, you'll need another scale for that line generator:
const yScale2 = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, dataPoint => dataPoint.pop_total)]);
After that, just append the path:
grp.append("path")
.attr("d", line);
The biggest problem now is that you have two visual encodings (the area and the line) which have different scales. Therefore, you'll need an additional axis for the line. I'll leave that work to you.
Here is the resulting code:
var url = "https://raw.githubusercontent.com/cvrnogueira/CODWorkData/master/database/final_data_set.json";
d3.json(url)
.then(function(data) {
data = data.filter(dataPoint => dataPoint.country_code == 'BRA');
data = data.filter(element => element.hasOwnProperty("earth_footprint"));
const heightValue = 300;
const widthValue = 600;
// Create SVG and padding for the chart
const svg = d3
.select("#area-chart")
.append("svg")
.attr("viewBox", `0 0 ${widthValue} ${heightValue}`);
const strokeWidth = 1.5;
const margin = {
top: 0,
bottom: 20,
left: 30,
right: 20
};
const chart = svg.append("g").attr("transform", `translate(${margin.left},0)`);
const width = 600 - margin.left - margin.right - (strokeWidth * 2);
const height = 300 - margin.top - margin.bottom;
const grp = chart
.append("g")
.attr("transform", `translate(-${margin.left - strokeWidth},-${margin.top})`);
// Create scales
const yScale = d3
.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, dataPoint => dataPoint.earth_footprint)]);
const yScale2 = d3
.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, dataPoint => dataPoint.pop_total)]);
const xScale = d3
.scaleLinear()
.range([0, width])
.domain(d3.extent(data, dataPoint => dataPoint.year));
const area = d3
.area()
.x(dataPoint => xScale(dataPoint.year))
.y0(height)
.y1(dataPoint => yScale(dataPoint.earth_footprint));
const line = d3.area()
.x(dataPoint => xScale(dataPoint.year))
.y(dataPoint => yScale2(dataPoint.pop_total));
// Add area
grp
.append("path")
.attr("transform", `translate(${margin.left},0)`)
.datum(data)
.style("fill", "lightblue")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", strokeWidth)
.attr("d", area);
grp
.append("path")
.attr("transform", `translate(${margin.left},0)`)
.datum(data)
.style("fill", "none")
.attr("stroke", "red")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", strokeWidth)
.attr("d", line);
// Add the X Axis
chart
.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale).ticks(data.length));
// Add the Y Axis
chart
.append("g")
.attr("transform", `translate(0, 0)`)
.call(d3.axisLeft(yScale));
chart.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of Earths");
chart.append("text")
.attr("transform",
"translate(" + (width / 2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Year");
});
#area-chart {
text-align: center;
margin-top: 40px;
}
.selection {
fill: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v5.min.js"></script>
<title>JS Bin</title>
</head>
<body>
<div id="area-chart"></div>
</body>
</html>

d3.js bar graph is not showing bars

I read previously answered questions but had no luck.
I am plotting a bar graph of elements on array using d3.js but the bars are not shown as they should be.
This is the script I am trying:
var data = [1, 2, 3, 4, 5];
var svg = d3.select("svg");
var margin = 100,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var Xscale = d3.scaleBand()
.domain([0, data.length])
.range([0, width])
.padding(0.2);
var Yscale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(Xscale).tickFormat(function(d) {
return d;
}).ticks(10));
// .append("text")
// .attr("x", 6)
// .attr("text-anchor", "end")
// .text("index");
g.append("g")
.call(d3.axisLeft(Yscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("value");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) {
return 0;
})
.attr("y", function(d, i) {
return 0;
})
.attr("width", Xscale.bandwidth())
.attr("height", function(d, i) {
return 0;
});
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/da.css">
<script src="https://d3js.org/d3.v5.min.js"></script>
<!-- <script src="js/da.js"></script> -->
</head>
<body>
<div id='layout'>
<h2>Bar chart example</h2>
<div class='container'>
<svg class="chart" height="500" width="1000" />
</div>
</div>
<p>Why this is not working?? </p>
</body>
</html>
I do feel that the problem is in last few lines, fetching x, y, width and height. I can't understand what values to return, I tried various, but didn't get the graph. So, I have just put return 0 there.
What should be the values? And how to decide it?
Thank you for help. :)
Your scaleBand().domain() has to be an array for the Xscale. In my solution I choose to have the indexes of the values as the array. You could map your data (usually an array of objects) to other values of the objects in an array.
Additionally there were several other issues with the scaling in terms of height and width of the actual bars and their positioning. Keep in mind that the SVG origin is the top left corner and everything is with respect to that.
I have updated the code below which makes necessary changes to produce a bar graph. Please go through it and let me know if there is anything that you do not understand.
var data = [1, 2, 3, 4, 5];
var svg = d3.select("svg");
var margin = 100,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var Xscale = d3.scaleBand()
.domain(data.map((e,i) => i)) //returns array [0,1,2,3,4] for the index of the values
.range([0, width])
.padding(0.2);
var dmax = d3.max(data) //calculates the max value of the data
var Yscale = d3.scaleLinear()
.domain([0, dmax])
.range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 50 + "," + 50 + ")");
var x = g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(Xscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("x", 6)
.attr("text-anchor", "end")
.text("index");
var y = g.append("g")
.call(d3.axisLeft(Yscale).tickFormat(function(d) {
return d;
}).ticks(10))
.append("text")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("value");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i){ return Xscale(i)}) //move the bar to the x position where it should appear
.attr("y", function(d, i) { return Yscale(d); }) //move the bar from the top down to the level of the value.
.attr("width", Xscale.bandwidth() ) //the width of the bar is the width between the points on the x-axis
.attr("height", function(d, i) {
return Yscale(dmax-d);
}); // the height of the points is calculated based on the scale and the difference between this point and the max value of the data.
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Bar chart with D3.js</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id='layout'>
<h2>Bar chart example</h2>
<div class='container'>
<svg class="chart" height="500" width="1000" />
</div>
</div>
<p>Why this is not working?? </p>
</body>
</html>
I recommand you to read this article. It explain very well how to use bar charts.
https://blog.risingstack.com/d3-js-tutorial-bar-charts-with-javascript/
I used your code and did a simple example. https://codepen.io/mp-9007/pen/jJpEWY
The main issues are because of the return values of x, y and height;
You have to provide the x and y position in the graph area. Drawing the bar chart is like drawing on a Cartesian plane, you must provide the coordinate of where to start the bar, the width of the bar and the height of it. The origin of the plan is at the top left of the image.
.attr("x", function(d, i) { //d = input data, i = index of it
return Xscale(d); //The scaling function returns the coordinate for a given domain value.
})
.attr("y", function(d, i) { //d = input data, i = index of it
return Yscale(d); //The scaling function returns the coordinate for a given domain value.
})
.attr("width", Xscale.bandwidth())
.attr("height", function(d, i) { //d = input data, i = index of it
return height - Yscale(d); //The computed y coordinate has to be subtracted from the height of the chart to get the correct representation of the value as a column.
});
Also, the domain for the x-axis can be think as categories. In your code, you were providing only two category: 0 and data.length . Provinding the array solved this issue.
var Xscale = d3.scaleBand().domain(data)

D3 multiple pie chart labels

I am relatively new to D3 working with a very large data set and trying to create a very large array of pie charts. However I cannot figure out how to place tittles at the very top of each pie chart.
My data that I am using is currently in a csv format like this and the fruits would be the labels I want for the pie charts
[apple,90,36,2]
[pear,36,36,3]
[grape,19,13,0]
I have pasted my code bellow with the data that works for it included bellow. Also I would ultimately like to be able to zoom into the data and look at it from a zoomed out feature like this:
http://mbostock.github.io/d3/talk/20111116/pack-hierarchy.html
If anybody has an idea to effectively convey this it would be greatly appreciated.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Multiple Pie Charts</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.4.5"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.4.5"></script>
<style type="text/css">
body {
text-align: center;
}
</style>
</head>
<body>
<script type="text/javascript">
var data = [
[90,36,2],
[36,36,3],
[19,13,0],
]
var m = 10,
r = 100,
z = d3.scale.category20c();
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg:svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("svg:g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
svg.selectAll("path")
.data(d3.layout.pie())
.enter().append("svg:path")
.attr("d", d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r))
.style("fill", function(d, i) { return z(i); });
</script>
</body>
</html>
You could use dc.js, it simplifies making d3 charts and retains the flexibility. On the homepage of that project they have a link to annotated source so you can see how to use it.
If you have a large data set I would use something like that because it uses crossfilter to reduce your data elements to only those that need to be displayed, resulting in much better performance.
Sorry I didn't directly answer your title question but suggested a different way of doing this, but I have never had to do that because I use dc.js which makes all this much simpler.
Figured it out
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 74,
padding = 10;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#2B8429"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 30);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data1.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "fruit"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.fruit; });
});
</script>

Categories

Resources