Related
Update:
The zooming stream graph question is resolved. Thank you, Andrew! But the zooming of the streamgraph doesn't align with the zooming of the X and Y axis.
Thisis the picture showing how it looks like now2
the original post is here:
I am new to StackOverflow and the javascript community. I am trying to zoom a streamgraph I did use javascript and D3 and I followed this tutorial: https://www.d3-graph-gallery.com/graph/interactivity_zoom.html#axisZoom.
My code can be viewed here: https://github.com/Feisnowflakes/zoomtest222/tree/main/streamgraph-test
However, currently, I can zoom X aXis and Y aXis, but not my stream graph is zoomable. I didn't see any errors in my console, so I am stuck now. Can anyone like to look at my codes and help me figure out why my streamgraph cannot be zoomed?
Thank you so much!
(function () {
// first, load the dataset from a CSV file
d3.csv("https://raw.githubusercontent.com/Feisnowflakes/zoomtest222/main/streamgraph-test/Los_Angeles_International_Airport_-_Passenger_Traffic_By_Terminal.csv")
.then(data => {
// log csv in browser console
console.log(data);
var advanceVisData = {};
var airport = new Set();
data.forEach(d => {
airport.add(d['Terminal']);
var period = new Date(d['ReportPeriod']);
if (period in advanceVisData) {
if (d['Terminal'] in advanceVisData[period]) {
advanceVisData[period][d['Terminal']] += Number(d['Passenger_Count']);
}
else {
advanceVisData[period][d['Terminal']] = Number(d['Passenger_Count']);
}
}
else {
advanceVisData[period] = {};
advanceVisData[period][d['Terminal']] = Number(d['Passenger_Count']);
}
});
console.log(airport);
console.log(advanceVisData);
// reformat the advanceVisData for d3.stack()
var formattedData = [];
Object.keys(advanceVisData).forEach(d => {
var item = {};
item['year'] = d;
airport.forEach(terminal => {
if (terminal in advanceVisData[d]) {
item[terminal] = advanceVisData[d][terminal];
} else {
item[terminal] = 0;
}
});
formattedData.push(item);
});
console.log(formattedData);
/*********************************
* Visualization codes start here
* ********************************/
var width = 1200;
var height = 400;
var margin = { left: 60, right: 20, top: 20, bottom: 60 };
//set the dimensions and margins of the graph
// append the svg object to the body of the page
var svg = d3.select('#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 + ")");
// List of groups = header of the csv files
var keys = Array.from(airport);
//stack the data?
var stackedData = d3.stack()
//.offset(d3.stackOffsetSilhouette)
.keys(keys)
(formattedData);
console.log(stackedData);
var max_val = 0;
var min_val = 0;
stackedData.forEach(terminal => {
terminal.forEach(year => {
if (year[0] < min_val) min_val = year[0];
if (year[1] < min_val) min_val = year[1];
if (year[0] > max_val) max_val = year[0];
if (year[1] > max_val) max_val = year[1];
})
});
//console.log(max_val, min_val);
// Add X axis
var x = d3.scaleTime()
.domain(d3.extent(formattedData, function (d) {
return new Date(d.year);
}))
.range([0, width]);
var xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(20));
// Add Y axis
var y = d3.scaleLinear()
.domain([min_val, max_val])
.range([height, 0]);
var yAxis = svg.append("g")
.call(d3.axisLeft(y));
// color palette
var color = d3.scaleOrdinal()
.domain(keys)
.range(['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#f781bf', "#87sbf", "#ff981bf","#d6a3b6", '#b3afb0', '#ddd8c2']);
// create a tooltip
var Tooltip = svg
.append("text")
.attr("x", 0)
.attr("y", 0)
.style("opacity", 0)
.style("font-size", 17)
// Show the areas
var stream = svg.append("g")
stream
.selectAll(".myStreamArea")
.data(stackedData)
.enter()
.append("path")
.attr("class", "myStreamArea")
.style("fill", function (d) {
return color(d.key);
})
.style("opacity", 1)
.attr("d", d3.area()
.x(function (d) {
return x(new Date(d.data.year));
})
.y0(function (d) {
return y(d[0]);
})
.y1(function (d) {
return y(d[1]);
})
);
// Set the zoom and Pan features: how much you can zoom, on which part, and what to do when there is a zoom
var zoom = d3.zoom()
.scaleExtent([.5, 20]) // This control how much you can unzoom (x0.5) and zoom (x20)
.extent([[0, 0], [width, height]])
.on("zoom", updateChart);
// This add an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.call(zoom);
// now the user can zoom and it will trigger the function called updateChart
// A function that updates the chart when the user zoom and thus new boundaries are available
function updateChart() {
// recover the new scale
var transform = d3.zoomTransform(this);
var newX = transform.rescaleX(x);
var newY = transform.rescaleY(y);
// var newX = d3.event.transform.rescaleX(x);
// var newY = d3.event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX))
yAxis.call(d3.axisLeft(newY))
stream
.selectAll(".myStreamArea")
.attr("d", d3.area()
.x(function (d) {
return newX(new Date(d.data.year));
})
.y0(function (d) {
return newY(d[0]);
})
.y1(function (d) {
return newY(d[1]);
}));
}
})
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Zoomable streamgraph</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<style>
#tooltip {
min-width: 100px;
min-height: 50px;
background-color: white;
}
</style>
</head>
<body>
<div id="container"> <div id="tooltip"></div></div>
<script src="main.js"></script>
</body>
</html>
I'm having issues getting a nested line chart to appear. The data is in the console, but maybe I am missing something crucial. I was following this for reference: https://amber.rbind.io/2017/05/02/nesting/
Possibly I am calling the nested data incorrectly, or maybe i need to append it to the svg? Any help greatly appreciated!
The chart needs to have year on the x-axis, sum of events on the y axis, and each line should be a region.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nested Chart</title>
<script src="../lib/d3.v5.min.js"></script>
<style type="text/css">
.pagebreak { page-break-before: always; }
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.point {
fill:none;
size: 2px
}
</style>
</head>
<div style= "width:800px; margin:0 auto;" class ='body'></div>
<div class="pagebreak"> </div>
<body>
<script type="text/javascript">
var parseTime = d3.timeParse("%Y");
var margin = {top: 20, right: 20, bottom: 30, left: 50},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding =20;
/////////////////get the data//////////////////
d3.csv("state-year-earthquakes.csv").then(function(dataset) {
dataset.forEach(function(d) {
d.date = parseTime(d.year);
d.region = d['region'];
d.state = d['state'];
d.count = d['count'];
//console.log(d)
});
/////////////////scales the data//////////////////
var xScale = d3.scaleTime()
.domain([d3.min(dataset,function (d) { return d.date }),d3.max(dataset,function (d) { return d.date }) ]).range([padding, w - padding * 2])
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset,function (d) { return d.count }) ]).range([h- padding, padding])
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
/////////////////charts start here//////////////////
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 + ")");
//Define the line
var valueLine = d3.line()
.x(function(d) { return xScale(d.date); })
.y(function(d) { return yScale(+d.count); })
var nest = d3.nest()
.key(function(d){
return d.region;
})
.key(function(d){
return d.date;
})
.rollup(function(leaves){
return d3.sum(leaves, function(d) {return (d.count)});
})
.entries(dataset)
var color = d3.scaleOrdinal(d3.schemeCategory10); // set the colour scale
console.log(nest)
var regYear = svg.selectAll(".regYear")
.data(nest)
.enter()
.append("g")
// console.log(regYear)
var paths = regYear.selectAll(".line")
.data(function(d){
return d.values
})
.enter()
.append("path");
console.log(paths)
// Draw the line
paths
.attr("d", function(d){
return d.values
})
.attr("class", "line")
svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg.append("text").attr("x", (w/2)).attr("y", h+30).attr("text-anchor", "middle").text("Year");
svg.append("text").attr("x", padding).attr("y", padding-20).attr("text-anchor", "middle").text("# of Events");
//add title
svg.append("text").attr("x", (w/2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
////////////////////////////////////END///////////////////////////
} );
</script>
</body>
</html>
data.csv
state,region,year,count
Alabama,South,2010,1
Alabama,South,2011,1
Alabama,South,2012,0
Alabama,South,2013,0
Alabama,South,2014,2
Alabama,South,2015,6
Alaska,West,2010,2245
Alaska,West,2011,1409
Alaska,West,2012,1166
Alaska,West,2013,1329
Alaska,West,2014,1296
Alaska,West,2015,1575
Connecticut,Northeast,2010,0
Connecticut,Northeast,2011,0
Connecticut,Northeast,2012,0
Connecticut,Northeast,2013,0
Connecticut,Northeast,2014,0
Connecticut,Northeast,2015,1
Missouri,Midwest,2010,2
Missouri,Midwest,2011,3
Missouri,Midwest,2012,2
Missouri,Midwest,2013,0
Missouri,Midwest,2014,1
Missouri,Midwest,2015,5
You have a couple of problems. First, you're not using your line generator. It should be:
.attr("d", function(d) {
return valueLine(d)
})
Second, you are parsing the date strings, but then converting them to strings again. So, change your line generator (or do not use them as a key, which returns a string):
var valueLine = d3.line()
.x(function(d) {
return xScale(new Date(d.key));
})
.y(function(d) {
return yScale(d.value);
})
Finally, each datum for the line generator must be an array itself. So:
var paths = regYear.selectAll(".line")
.data(function(d) {
return [d.values]
})
Here is your code with those changes (and a little CSS for the paths):
path {
fill: none;
stroke: black;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nested Chart</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
.pagebreak {
page-break-before: always;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.point {
fill: none;
size: 2px
}
</style>
</head>
<div style="width:800px; margin:0 auto;" class='body'></div>
<div class="pagebreak"> </div>
<body>
<script type="text/javascript">
var parseTime = d3.timeParse("%Y");
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding = 20;
/////////////////get the data//////////////////
const csv = `state,region,year,count
Alabama,South,2010,1
Alabama,South,2011,1
Alabama,South,2012,0
Alabama,South,2013,0
Alabama,South,2014,2
Alabama,South,2015,6
Alaska,West,2010,2245
Alaska,West,2011,1409
Alaska,West,2012,1166
Alaska,West,2013,1329
Alaska,West,2014,1296
Alaska,West,2015,1575
Connecticut,Northeast,2010,0
Connecticut,Northeast,2011,0
Connecticut,Northeast,2012,0
Connecticut,Northeast,2013,0
Connecticut,Northeast,2014,0
Connecticut,Northeast,2015,1
Missouri,Midwest,2010,2
Missouri,Midwest,2011,3
Missouri,Midwest,2012,2
Missouri,Midwest,2013,0
Missouri,Midwest,2014,1
Missouri,Midwest,2015,5`;
const dataset = d3.csvParse(csv);
dataset.forEach(function(d) {
d.date = parseTime(d.year);
d.region = d['region'];
d.state = d['state'];
d.count = d['count'];
//console.log(d)
});
/////////////////scales the data//////////////////
var xScale = d3.scaleTime()
.domain([d3.min(dataset, function(d) {
return d.date
}), d3.max(dataset, function(d) {
return d.date
})]).range([padding, w - padding * 2])
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
return d.count
})]).range([h - padding, padding])
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
/////////////////charts start here//////////////////
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 + ")");
//Define the line
var valueLine = d3.line()
.x(function(d) {
return xScale(new Date(d.key));
})
.y(function(d) {
return yScale(d.value);
})
var nest = d3.nest()
.key(function(d) {
return d.region;
})
.key(function(d) {
return d.date;
})
.rollup(function(leaves) {
return d3.sum(leaves, function(d) {
return (d.count)
});
})
.entries(dataset)
var color = d3.scaleOrdinal(d3.schemeCategory10); // set the colour scale
var regYear = svg.selectAll(".regYear")
.data(nest)
.enter()
.append("g")
// console.log(regYear)
var paths = regYear.selectAll(".line")
.data(function(d) {
return [d.values]
})
.enter()
.append("path");
// Draw the line
paths
.attr("d", function(d) {
return valueLine(d)
})
.attr("class", "line")
svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
//add title
svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
////////////////////////////////////END///////////////////////////
</script>
</body>
</html>
I have the following code that nests my data based on region and date. The problem I am having is that I don't know how to define yScale to dynamically draw the axis so that the max sum from the nested data is returned (the max val of the nested data is higher than the max val in the dataset bc it is aggregated). Thus my yAxis is truncated and the chart doesn't show all the data.
In the code, if I hardcode the domain to .domain([0, 3500]) then the axis is correct, but otherwise it is not correct. I don't want to hardcode the domain. How do I reference the nested values?
EDITED to show code provided in comments, which helps but doesn't entirely fix when the script is run on the entire dataset. Please see pic at bottom.
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
return parseInt(d.count,10);
})])
.range([h - padding, padding])
// var yScale = d3.scaleLinear()
// .domain([0, 3500])
// .range([h - padding, padding]) //not supposed to hard code the scale but it is not working
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nested Chart</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
.pagebreak {
page-break-before: always;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.point {
fill: none;
size: 2px
}
.dot {
fill: #ffab00;
stroke: #fff;
}
</style>
</head>
<div style="width:800px; margin:0 auto;" class='body'></div>
<div class="pagebreak"> </div>
<body>
<script type="text/javascript">
var parseTime = d3.timeParse("%Y");
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding = 20;
/////////////////get the data//////////////////
const csv = `state,region,year,count
Alabama,South,2010,1
Alabama,South,2011,1
Alabama,South,2012,0
Alabama,South,2013,0
Alabama,South,2014,2
Alabama,South,2015,6
Alaska,West,2010,2245
Alaska,West,2011,1409
Alaska,West,2012,1166
Alaska,West,2013,1329
Alaska,West,2014,1296
Alaska,West,2015,1575
Connecticut,Northeast,2010,0
Connecticut,Northeast,2011,0
Connecticut,Northeast,2012,0
Connecticut,Northeast,2013,0
Connecticut,Northeast,2014,0
Connecticut,Northeast,2015,1
Missouri,Midwest,2010,2
Missouri,Midwest,2011,3
Missouri,Midwest,2012,2
Missouri,Midwest,2013,0
Missouri,Midwest,2014,1
Missouri,Midwest,2015,5
California,West,2010,546
California,West,2012,243
California,West,2013,240
Wyoming,West,2015,198
California,West,2011,195
California,West,2014,191`;
const dataset = d3.csvParse(csv);
dataset.forEach(function(d) {
d.date = parseTime(d.year);
d.region = d['region'];
d.state = d['state'];
d.count = d['count'];
//console.log(d)
});
/////////////////scales the data//////////////////
var xScale = d3.scaleTime()
.domain([d3.min(dataset, function(d) {
return d.date
}), d3.max(dataset, function(d) {
return d.date
})]).range([padding, w - padding * 2])
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
console.log(d.count)
return d.count ///ERROR HERE
})]).range([h - padding, padding])
// var yScale = d3.scaleLinear()
// .domain([0, 3500])
// .range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
/////////////////charts start here//////////////////
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 + ")");
var svg1 = 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 + ")");
//Define the line
var valueLine = d3.line()
.x(function(d) {
return xScale(new Date(d.key));
})
.y(function(d) {
return yScale(d.value);
})
var nest = d3.nest()
.key(function(d) {
return d.region;
})
.key(function(d) {
return d.date;
})
.rollup(function(leaves) {
return d3.sum(leaves, function(d) {
return (d.count)
});
})
.entries(dataset)
console.log(nest)
// Set the color scheme
var colors = d3.scaleOrdinal()
.domain(["South", "West", "Northeast","Midwest"])
.range(["#EF5285", "#88F284" , "#5965A3","#900C3F"]);
var regYear = svg.selectAll(".regYear")
.data(nest)
.enter()
.append("g")
.attr("stroke", function(d){ return colors(d.key)}); // Adding color!
// console.log(regYear)
var paths = regYear.selectAll(".line")
.data(function(d) {
return [d.values]
})
.enter()
.append("path");
// Draw the line
paths
.attr("d", function(d) {
return valueLine(d)
})
.attr("class", "line")
.style("fill", "none");
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(i) })
.attr("cy", function(d) { return yScale(d.count) })//this is not working
.attr("r", 5);
svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
//add title
svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
////////////////////////////////////END///////////////////////////
</script>
</body>
</html>
UPDATE :
The max value of yAxis is less than the actual max value of the nest data, so it's truncated.
You have to use your nest data inside yScale calculation rather than using the original dataset data.
Steps to achieve this:
define nest first
get the totalMax value by flatting nest twice
use totalMax to calculate the yScale value
var totalMax = Object
.entries(nest)
.reduce(function(totalMax, [key, regionValue]){
const regionMax = Object
.entries(regionValue.values)
.reduce(function(regionMax, [key,yearValue]){
return parseInt(yearValue.value,10) > regionMax ? parseInt(yearValue.value, 10) : regionMax;
}, 0)
return parseInt(regionMax, 10) > totalMax ? parseInt(regionMax, 10) : totalMax;
}, 0)
var yScale = d3.scaleLinear().domain([0, totalMax]).range([h - padding, padding])
I write a Demo base on your code :
var parseTime = d3.timeParse("%Y");
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding = 20;
/////////////////get the data//////////////////
const csv = `state,region,year,count
Alabama,South,2010,1
Alabama,South,2011,1
Alabama,South,2012,0
Alabama,South,2013,0
Alabama,South,2014,2
Alabama,South,2015,6
Alaska,West,2010,2245
Alaska,West,2011,1409
Alaska,West,2012,1166
Alaska,West,2013,1329
Alaska,West,2014,1296
Alaska,West,2015,1575
Connecticut,Northeast,2010,0
Connecticut,Northeast,2011,0
Connecticut,Northeast,2012,0
Connecticut,Northeast,2013,0
Connecticut,Northeast,2014,0
Connecticut,Northeast,2015,1
Missouri,Midwest,2010,2
Missouri,Midwest,2011,3
Missouri,Midwest,2012,2
Missouri,Midwest,2013,0
Missouri,Midwest,2014,1
Missouri,Midwest,2015,5
California,West,2010,546
California,West,2012,243
California,West,2013,240
Wyoming,West,2015,198
California,West,2011,195
California,West,2014,191`;
const dataset = d3.csvParse(csv);
dataset.forEach(function(d) {
d.date = parseTime(d.year);
d.region = d['region'];
d.state = d['state'];
d.count = d['count'];
});
var nest = d3.nest()
.key(function(d) {
return d.region;
})
.key(function(d) {
return d.date;
})
.rollup(function(leaves) {
return d3.sum(leaves, function(d) {
return parseInt(d.count, 10);
});
})
.entries(dataset)
/////////////////scales the data//////////////////
var xScale = d3.scaleTime()
.domain([d3.min(dataset, function(d) {
return d.date
}), d3.max(dataset, function(d) {
return d.date
})]).range([padding, w - padding * 2])
var totalMax = Object
.entries(nest)
.reduce(function(totalMax, [key, regionValue]){
const regionMax = Object
.entries(regionValue.values)
.reduce(function(regionMax, [key,yearValue]){
return parseInt(yearValue.value,10) > regionMax ? parseInt(yearValue.value, 10) : regionMax;
}, 0)
return parseInt(regionMax, 10) > totalMax ? parseInt(regionMax, 10) : totalMax;
}, 0)
var yScale = d3.scaleLinear()
.domain([0, totalMax]).range([h - padding, padding])
// var yScale = d3.scaleLinear()
// .domain([0, 3500])
// .range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
/////////////////charts start here//////////////////
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 + ")");
var svg1 = 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 + ")");
//Define the line
var valueLine = d3.line()
.x(function(d) {
return xScale(new Date(d.key));
})
.y(function(d) {
return yScale(d.value);
})
// Set the color scheme
var colors = d3.scaleOrdinal()
.domain(["South", "West", "Northeast","Midwest"])
.range(["#EF5285", "#88F284" , "#5965A3","#900C3F"]);
var regYear = svg.selectAll(".regYear")
.data(nest)
.enter()
.append("g")
.attr("stroke", function(d){ return colors(d.key)}); // Adding color!
var paths = regYear.selectAll(".line")
.data(function(d) {
return [d.values]
})
.enter()
.append("path");
// Draw the line
paths
.attr("d", function(d) {
return valueLine(d)
})
.attr("class", "line")
.style("fill", "none");
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(i) })
.attr("cy", function(d) { return yScale(d.count) })//this is not working
.attr("r", 5);
svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
//add title
svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
////////////////////////////////////END///////////////////////////
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nested Chart</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
.pagebreak {
page-break-before: always;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.point {
fill: none;
size: 2px
}
.dot {
fill: #ffab00;
stroke: #fff;
}
</style>
</head>
<div style="width:800px; margin:0 auto;" class='body'></div>
<div class="pagebreak"> </div>
<body>
</body>
</html>
I noticed the data type of your d.count is string so that the max won't be correct.
console.log(d3.max(['6','2245'])) // it's 6!
Try converting the value to the number before return it :
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
return parseInt(d.count,10);
})])
.range([h - padding, padding])
To get max value inside nested array you can nest d3.max function in the way like"
var maxCountSum = d3.max(nest, function(d) {
return d3.max(d.values, function (f) {
return f.value
});
});
Then apply to yScale domain:
var yScale = d3.scaleLinear()
.domain([0, maxCountSum]).range([height, 0]);
I'm trying to figure out how to get two d3 graphs onto the same page with one on the left and one on the right. However, what I'm getting is this.
This is my html file.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.polyline path {
fill: none;
stroke: #666;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
.xaxis text {
font: 10px sans-serif;
}
.yaxis text {
font: 10px sans-serif;
}
.xaxis path,
.xaxis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.yaxis path,
.yaxis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<svg class="chart"></svg>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src = "project3.js">//https://my.up.ist.psu.edu/lng5099/project3.html </script>
</body>
</html>
This is my js file.
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {};
var axis = d3.svg.axis().orient("left");
var line = d3.svg.line() //define a function to convert points into a polyline
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");//line style. you can try "cardinal".
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawPC();
});
function drawPC() {
// Extract the list of dimensions and create a scale for each.
for (var dim in cars[0]) {
if (dim != "name") {
y[dim] = d3.scale.linear()
.domain([d3.min(cars, function(d) { return +d[dim]; }), d3.max(cars, function(d) { return +d[dim]; })])
.range([height,0]);
}
}
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { return d != "name";}));
//draw polylines
for (var i=1; i< cars.length; i++) { //for each car
//prepare the coordinates for a polyline
var lineData = []; //initialize an array for coordinates of a polyline
for (var prop in cars[0]) { //get each dimension
if (prop != "name" ) { //skip the name dimension
var point = {}; //initialize a coordinate object
var val = cars[i][prop]; //obtain the value of a car in a dimension
point['x'] = x(prop); //x value: mapping the dimension
point['y'] = y[prop](val);//y value: mapping the value in that dimension
lineData.push(point); //add the object into the array
}
}
//draw a polyline based on the coordindates
chart.append("g")
.attr("class", "polyline")
.append("path") // a path shape
.attr("d", line(lineData)); //line() is a function to turn coordinates into SVG commands
}
//next: draw individual dimension lines
//position dimension lines appropriately
var g = chart.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; }); //translate each axis
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
};
//this function coerces numerical data to numbers
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear().range([50, width]),
y = d3.scale.linear().range([height-20,0]);
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawXY();
});
function drawXY(){
x.domain([d3.min(cars, function(d) { return d.year; }), d3.max(cars, function(d) { return d.year; })]);
y.domain([d3.min(cars, function(d) { return d.power; }), d3.max(cars, function(d) { return d.power; })]);
var yPos = height -20;
chart.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + yPos + ")")
.call(xAxis);
chart.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(50,0)")
.call(yAxis);
chart.selectAll(".dot")
.data(cars)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.power); })
.attr("r", 3);
}
d3.selectAll("circle").data(cars).enter()
.append("circle")
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
Both are displaying, but I'm not sure how set it so that they position properly. Please help.
Figured it out there was an issue with the positioning of my margins, width and height. There is the java code for anyone who needs the help.
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 700 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {};
var axis = d3.svg.axis().orient("left");
var line = d3.svg.line() //define a function to convert points into a polyline
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");//line style. you can try "cardinal".
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawPC();
});
function drawPC() {
// Extract the list of dimensions and create a scale for each.
for (var dim in cars[0]) {
if (dim != "name") {
y[dim] = d3.scale.linear()
.domain([d3.min(cars, function(d) { return +d[dim]; }), d3.max(cars, function(d) { return +d[dim]; })])
.range([height,0]);
}
}
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) { return d != "name";}));
//draw polylines
for (var i=1; i< cars.length; i++) { //for each car
//prepare the coordinates for a polyline
var lineData = []; //initialize an array for coordinates of a polyline
for (var prop in cars[0]) { //get each dimension
if (prop != "name" ) { //skip the name dimension
var point = {}; //initialize a coordinate object
var val = cars[i][prop]; //obtain the value of a car in a dimension
point['x'] = x(prop); //x value: mapping the dimension
point['y'] = y[prop](val);//y value: mapping the value in that dimension
lineData.push(point); //add the object into the array
}
}
//draw a polyline based on the coordindates
chart.append("g")
.attr("class", "polyline")
.append("path") // a path shape
.attr("d", line(lineData)); //line() is a function to turn coordinates into SVG commands
}
//next: draw individual dimension lines
//position dimension lines appropriately
var g = chart.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; }); //translate each axis
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
};
//this function coerces numerical data to numbers
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
(function() {
var margin = {top: 20, right: 30, bottom: 30, left: 690},
width = 1300 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var x = d3.scale.linear().range([50, width]),
y = d3.scale.linear().range([height-20,0]);
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var cars=[];
d3.csv("cars.csv", type, function(error, data) {
cars = data;
drawXY();
});
function drawXY(){
x.domain([d3.min(cars, function(d) { return d.year; }), d3.max(cars, function(d) { return d.year; })]);
y.domain([d3.min(cars, function(d) { return d.power; }), d3.max(cars, function(d) { return d.power; })]);
var yPos = height -20;
chart.append("g")
.attr("class", "xaxis")
.attr("transform", "translate(0," + yPos + ")")
.call(xAxis);
chart.append("g")
.attr("class", "yaxis")
.attr("transform", "translate(50,0)")
.call(yAxis);
chart.selectAll(".dot")
.data(cars)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.power); })
.attr("r", 3);
}
d3.selectAll("circle").data(cars).enter()
.append("circle")
.classed("circle", true)
.on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
.on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); });
function type(d) {
d.economy = +d.economy; // coerce to number
d.displacement = +d.displacement; // coerce to number
d.power = +d.power; // coerce to number
d.weight = +d.weight; // coerce to number
d.year = +d.year;
return d;
}
})();
I am trying to resolve this error("d3.min.js:1 Uncaught TypeError: n.getFullYear is not a function") by changing parseDate function parameters again and again but still no result.when i parse dates through d.date in forEach function dates becomes null which is why in svg path its taking 'NaN' as input,resulting into no plotting of Multi-series Line Chart.
//JSON array called "somearray" is like this.Actual JSON Array is much bigger but just to show the problem i have given this much.
somearray = [{date: "2014-12-19 02:22:31",s0: 2,s1: 4,s2: 2},{date: "2015-05-03 12:10:32",s0: 5,s1: 5,s2: 1},{date: "2015-03-23 19:45:14",s0: 2,s1: 2,s2: 1},{date: "2015-06-02 22:58:35",s0:0,s1: 5,s2: 0}]
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body {
font: 12px
Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: black;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- <script src="http://cdn.jsdelivr.net/alasql/0.2/alasql.min.js"></script> -->
<!-- <! <script type="text/javascript" src="d3.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// var margin = {top: 20, right: 80, bottom: 30, left: 50},
// width = 960 - margin.left - margin.right,
// height = 500 - margin.top - margin.bottom;
//I want on Year month and Date thats why i have used "%Y-%m-%d"
var parseDate = d3.time.format("%Y-%m-%d");
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(7);
// Define the s0 line
var reviewline1 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.s0); });
//define the s1 line
var reviewline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.s1); });
//define the s2 line
var reviewline3 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.s2); });
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 + ")");
//Making data structure which d3 understands[{}:{}]i.e.,array of objects
d3.json("rest_json.json", function(data){
var obj1={};
console.log(data.review_detail);
data.review_detail.forEach(function(d) {
//d.date = parseDate(d.date);
sentiment=d.vk;
dDate = d.vf
if (obj1[dDate]==undefined)
{
obj1[dDate]={"s0":0,"s1":0,"s2":0};
obj1[dDate][sentiment]=1;
}
else
{
obj1[dDate][sentiment]+=1;
};
});
console.log(obj1);
var somearray = [];
for (key in obj1){
var arrayObj = {};
console.log(key);
console.log(obj1[key].s1);
console.log(obj1[key].s0);
console.log(obj1[key].s2);
arrayObj.date = key;
arrayObj.s1 = obj1[key].s1;
arrayObj.s2 = obj1[key].s2;
arrayObj.s0 = obj1[key].s0;
somearray.push(arrayObj);
};
console.log(somearray);
// var res1 = alasql('SELECT id, SUM(s1) AS scor e FROM ? GROUP BY id',[dataArray ]);
somearray.forEach(function(d){
// console.log(d.s0);
d.date = parseDate(d.date);
// console.log(d.date);
d.s0 = +d.s0;
d.s1 = +d.s1;
d.s2 = +d.s2;
});
// Scale the range of the data
x.domain(d3.extent(somearray, function(d) { return d.date; }));
// y.domain([0, d3.max(somearray, function(d) { return d.s0; })]);
y.domain([0, d3.max(somearray, function(d) {
return Math.max(d.s0, d.s1, d.s2); })]);
console.log(somearray);
// Add the reviewline path.
svg.append("path")
.attr("class", "line")
.attr("d", reviewline1(somearray));
// Add the reviewline2 path.
svg.append("path")
.style("stroke", "red")
.attr("d", reviewline2(somearray));
// Add the reviewline3 path.
svg.append("path")
.style("stroke", "blue")
.attr("d", reviewline3(somearray));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
enter image description here
The issue was solved by using
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
parseDate('2014-10-10 00:50:09'); //returns Fri Oct 10 2014 00:50:09 GMT+0530 (IST)