d3.js Date vs Half hour heat map not working - d3.js

I am trying to create a Date vs time heat map based on this http://bl.ocks.org/mbostock/3202354
The changes i am trying to do:
->x axis still has dates
->y axis will have 24 hours of the day in half hour intervals (00:00:00, 00:30:00, 01:00:00, 01:30:00 so on). Therefore 48 ticks
I have managed to modify the code so far which is below. The two problems are:
1. y axis does not have labels
2. Data goes one tick below x axis
I am a D3 noob (this is my first hands on experiment ) and struggling with it.
d3+html+css code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.label {
font-weight: bold;
}
.tile {
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="http://d3js.org/d3.v2.js?2.9.6"></script>
<script>
var margin = {top: 20, right: 90, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%m/%d/%Y").parse,
formatDate = d3.time.format("%b %d");
var parseTimeBucket = d3.time.format("%H:%M:%S").parse,
formatTimeBucket =d3.time.format("%H:%M");
var StartTime = parseTimeBucket("00:00:00");
var EndTime = parseTimeBucket("23:59:59");
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
z = d3.scale.linear().range(["white", "black"]);
// The size of the buckets in the CSV data file.
// This could be inferred from the data if it weren't sparse.
var xStep = 864e5,
yStep = 18e5;
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("TimeDateHeatMap.csv", function(buckets) {
// Coerce the CSV data to the appropriate types.
buckets.forEach(function(d) {
d.date = parseDate(d.date);
d.bucket = parseTimeBucket(d.bucket);
d.count = +d.count;
});
// Compute the scale domains.
x.domain(d3.extent(buckets, function(d) { return d.date; }));
y.domain([StartTime, EndTime]);
// y.domain(d3.extent(buckets, function(d) { return d.bucket; }));
//console.log(d3.extent(buckets, function(d) { return d.bucket; }));
z.domain([0, d3.max(buckets, function(d) { return d.count; })]);
// Extend the x- and y-domain to fit the last bucket.
// For example, the y-bucket 3200 corresponds to values [3200, 3300].
x.domain([x.domain()[0], +x.domain()[1] + xStep]);
y.domain([y.domain()[0], +y.domain()[1] + yStep]);
// Display the tiles for each non-zero bucket.
// See http://bl.ocks.org/3074470 for an alternative implementation.
svg.selectAll(".tile")
.data(buckets)
.enter().append("rect")
.attr("class", "tile")
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.bucket); })
.attr("width", x(xStep) - x(0))
.attr("height", y(0) - y(yStep))
.style("fill", function(d) { return z(d.count); });
// Add a legend for the color values.
var legend = svg.selectAll(".legend")
.data(z.ticks(6).slice(1).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + (width + 20) + "," + (20 + i * 20) + ")"; });
legend.append("rect")
.attr("width", 20)
.attr("height", 20)
.style("fill", z);
legend.append("text")
.attr("x", 26)
.attr("y", 10)
.attr("dy", ".35em")
.text(String);
svg.append("text")
.attr("class", "label")
.attr("x", width + 20)
.attr("y", 10)
.attr("dy", ".35em")
.text("Count");
// Add an x-axis with label.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis().scale(x).ticks(d3.time.days).tickFormat(formatDate).orient("bottom"))
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.attr("text-anchor", "end")
.text("Date");
// Add a y-axis with label.
svg.append("g")
.attr("class", "y axis")
//.call(d3.svg.axis().scale(y).orient("left"))
.call(d3.svg.axis().scale(y).ticks(d3.time.minutes).tickFormat(formatTimeBucket).orient("left"))
.append("text")
.attr("class", "label")
.attr("y", 6)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.text("Value");
});
</script>
TimeDateHeatMap.csv:
date,bucket,count
7/20/2012,00:00:00,119
7/20/2012,00:30:00,123
7/20/2012,01:00:00,173
7/20/2012,01:30:00,226
7/20/2012,02:00:00,284
7/20/2012,02:30:00,257
7/20/2012,03:00:00,268
7/20/2012,03:30:00,244
7/20/2012,04:00:00,191
7/20/2012,04:30:00,204
7/20/2012,05:00:00,187
7/20/2012,05:30:00,177
7/20/2012,06:00:00,164
7/20/2012,06:30:00,125
7/20/2012,07:00:00,140
7/20/2012,07:30:00,109
7/20/2012,08:00:00,103
7/21/2012,08:30:00,123
7/21/2012,09:00:00,165
7/21/2012,09:30:00,237
7/21/2012,10:00:00,278
7/21/2012,10:30:00,338
7/21/2012,11:00:00,306
7/21/2012,11:30:00,316
7/21/2012,12:00:00,269
7/21/2012,12:30:00,271
7/21/2012,13:00:00,241
7/21/2012,13:30:00,188
7/21/2012,14:00:00,174
7/21/2012,14:30:00,158
7/21/2012,15:00:00,153
7/21/2012,15:30:00,132
7/22/2012,16:00:00,154
7/22/2012,16:30:00,241
7/22/2012,17:00:00,246
7/22/2012,17:30:00,300
7/22/2012,18:00:00,305
7/22/2012,18:30:00,301
7/22/2012,19:00:00,292
7/22/2012,19:30:00,253
7/22/2012,20:00:00,251
7/22/2012,20:30:00,214
7/22/2012,21:00:00,189
7/22/2012,21:30:00,179
7/22/2012,22:00:00,159
7/22/2012,22:30:00,161
7/22/2012,23:00:00,144
7/22/2012,23:30:00,139
7/22/2012,00:00:00,132
7/22/2012,00:30:00,136
7/22/2012,01:00:00,105
7/23/2012,01:30:00,120
7/23/2012,02:00:00,156
7/23/2012,02:30:00,209
7/23/2012,03:00:00,267
7/23/2012,03:30:00,299
7/23/2012,04:00:00,316
7/23/2012,04:30:00,318
7/23/2012,05:00:00,307
7/23/2012,05:30:00,295
7/23/2012,06:00:00,273
7/23/2012,06:30:00,283
7/23/2012,07:00:00,229
7/23/2012,07:30:00,192
7/23/2012,08:00:00,193
7/23/2012,08:30:00,170
7/23/2012,09:00:00,164
7/23/2012,09:30:00,154
7/23/2012,10:00:00,138
7/23/2012,10:30:00,101
7/23/2012,11:00:00,115
7/23/2012,11:30:00,103
7/24/2012,12:00:00,105
7/24/2012,12:30:00,156
7/24/2012,13:00:00,220
7/24/2012,13:30:00,255
7/24/2012,14:00:00,308
7/24/2012,14:30:00,338
7/24/2012,15:00:00,318
7/24/2012,15:30:00,255
7/24/2012,16:00:00,278
7/24/2012,16:30:00,260
7/24/2012,17:00:00,235
7/24/2012,17:30:00,230
7/24/2012,18:00:00,185
7/24/2012,18:30:00,145
7/24/2012,19:00:00,147
7/24/2012,19:30:00,157
7/24/2012,20:00:00,109
7/25/2012,20:30:00,104
7/25/2012,21:00:00,191
7/25/2012,21:30:00,201
7/25/2012,22:00:00,238
7/25/2012,22:30:00,223
7/25/2012,23:00:00,229
7/25/2012,23:30:00,286
7/25/2012,00:00:00,256
7/25/2012,00:30:00,240
7/25/2012,01:00:00,233
7/25/2012,01:30:00,202
7/25/2012,02:00:00,180
7/25/2012,02:30:00,184
7/25/2012,03:00:00,161
7/25/2012,03:30:00,125
7/25/2012,04:00:00,110
7/25/2012,04:30:00,101
7/26/2012,05:00:00,132
7/26/2012,05:30:00,117
7/26/2012,06:00:00,124
7/26/2012,06:30:00,154
7/26/2012,07:00:00,167
7/26/2012,07:30:00,137
7/26/2012,08:00:00,169
7/26/2012,08:30:00,175
7/26/2012,09:00:00,168
7/26/2012,09:30:00,188
7/26/2012,10:00:00,137
7/26/2012,10:30:00,173
7/26/2012,11:00:00,164
7/26/2012,11:30:00,167
7/26/2012,12:00:00,115
7/26/2012,12:30:00,116
7/26/2012,13:00:00,118
7/26/2012,13:30:00,125
7/26/2012,14:00:00,104
Changes to original mbostock code:They mostly concentrate on y axis, while x xis remains same as original. Because the original mbostock code has numbers on y axis while i need 48 half hour interval buckets on y axis
I have added the following so that later on i can create a y axis of times:
var parseTimeBucket = d3.time.format("%H:%M:%S").parse,
formatTimeBucket =d3.time.format("%H:%M");
Then changed domain this way:
var StartTime = parseTimeBucket("00:00:00");
var EndTime = parseTimeBucket("23:59:59");
y.domain([StartTime, EndTime]);
also added ystep in addition to xstep. ystep corresponds to 30 minute intervals
var xStep = 864e5,
yStep = 18e5;
and then finally to build the axis i changed the third line (.call...)
svg.append("g")
.attr("class", "y axis")
.call(d3.svg.axis().scale(y).ticks(d3.time.minutes, 30).tickFormat(formatTimeBucket).orient("left"))
.append("text")
.attr("class", "label")
.attr("y", 6)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.text("Value");

The scale for the y axis you're using is a linear scale. You're using .ticks(d3.time.minutes) to set the ticks for the axis for that scale. You can only do this for time scales, not linear scales. If you change either the way the ticks are computed or the type of scale it should work.

Related

D3 v5: multiline chart, not able to get multiple lines to draw

Thank you for any help you can offer in advance, I am new to D3 and having a hard time following the multiline chart examples I've seen online. I have data that looks like:
country,year,average
United States,1970,51
United States,1971,50
United States,1972,54
United States,1973,56
United States,1974,53
United States,1975,57
United States,1976,60
Brazil,1970,23
Brazil,1971,25
Brazil,1972,24
Brazil,1973,21
Brazil,1974,25
Brazil,1975,26
Brazil,1976,24
for multiple countries and I would like to make a line for each of them.
var margin = {top: 10, right: 40, bottom: 150, left: 70},
width = 760 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var w = width + margin.left + margin.right;
var h = height + margin.top + margin.bottom;
var svg = d3.select("body").append("svg") // this appends a new SVG element to body
.attr("width", w) // set the width
.attr("height", h) // set the height
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x scale will handle time
var xScale = d3.scaleBand().range([0, width]).padding(0.1);
// y scale will handle energy consumption values
var yScale = d3.scaleLinear().range([height,0]);
// Define X and Y AXIS
var yAxis = d3.axisLeft(yScale).ticks(5);
var xAxis = d3.axisBottom(xScale);
function rowConverter(data) {
return {
country : data.country,
year : +data.year,
average : +data.average // the + operator parses strings into numbers
};
}
// line generator function
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return xScale(d.year); })
.y(function(d) { return yScale(d.average); })
d3.csv("EvenMore.csv", rowConverter).then(function(data) {
var countries = d3.nest()
.key(function (d) { return d.country; })
.entries(data);
console.log(countries);
yScale.domain([0,d3.max(data, function(d) {return d.average; } )]);
xScale.domain(d3.extent(data, function(d) { return d.year; } ));
// Draw xAxis
svg.append("g") // add a new svg group element
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("text-anchor", "end");
// Draw yAxis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("text-anchor", "end");
svg.selectAll("path")
.data(countries)
.enter()
.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
});
});
I do not know what these errors mean, Error: attribute d: Expected number, "….33333333333334LNaN,114.27777777…":
The Problem
You're not using the band scale correctly. A band scale is not a quantitative scale and therefore does not have an upper and lower bounds. Instead, every value of the domain needs to be specified:
The first element in domain will be mapped to the first band, the
second domain value to the second band, and so on. Domain values are
stored internally in a map from stringified value to index; the
resulting index is then used to determine the band (docs)
This explains your error, you've specified two values to the domain, the first year and the last year. We can see that the domain is only these two values a few ways, when looking at the scale (a band scale's axis by default includes all ticks, but even here we see the spacing is really odd if 1970 and 1976 are the start and end values):
The error message also helps in finding the error: if the first coordinate's x value was NaN the message would read "Expected Number, "MNan,1234..." when examining the path d attribute (especially without any curve applied), we can see the x value of every coordinate except the first and last are NaN.
The solution
You need to provide all values in the domain to the scale. We can get all values with:
xScale.domain(data.map(function(d) { return d.year; }))
The scale will weed out duplicates when setting the domain.
var margin = {top: 10, right: 40, bottom: 150, left: 70},
width = 760 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var w = width + margin.left + margin.right;
var h = height + margin.top + margin.bottom;
var svg = d3.select("body").append("svg") // this appends a new SVG element to body
.attr("width", w) // set the width
.attr("height", h) // set the height
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x scale will handle time
var xScale = d3.scaleBand().range([0, width]).padding(0.1);
// y scale will handle energy consumption values
var yScale = d3.scaleLinear().range([height,0]);
// Define X and Y AXIS
var yAxis = d3.axisLeft(yScale).ticks(5);
var xAxis = d3.axisBottom(xScale);
function rowConverter(data) {
return {
country : data.country,
year : +data.year,
average : +data.average // the + operator parses strings into numbers
};
}
// line generator function
var line = d3.line()
.curve(d3.curveBasis)
.x(function(d) { return xScale(d.year); })
.y(function(d) { return yScale(d.average); })
var data = d3.csvParse(d3.select("pre").remove().text())
data = data.map(rowConverter);
var countries = d3.nest()
.key(function (d) { return d.country; })
.entries(data);
yScale.domain([0,d3.max(data, function(d) {return d.average; } )]);
xScale.domain(countries[0].values.map(function(d) { return d.year; }));
// Draw xAxis
svg.append("g") // add a new svg group element
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("text-anchor", "end");
// Draw yAxis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.attr("dx", "-.8em")
.attr("dy", ".25em")
.attr("text-anchor", "end");
svg.selectAll(null)
.data(countries)
.enter()
.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
});
.line {
stroke-width: 2px;
fill: none;
stroke:black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<pre>country,year,average
United States,1970,51
United States,1971,50
United States,1972,54
United States,1973,56
United States,1974,53
United States,1975,57
United States,1976,60
Brazil,1970,23
Brazil,1971,25
Brazil,1972,24
Brazil,1973,21
Brazil,1974,25
Brazil,1975,26
Brazil,1976,24</pre>

String x ticks not scaling data in D3

`
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
// X scale
var xScale = d3.scaleBand()
.domain(['A','B','C','D','F','E','Z']) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(i); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
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("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
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.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
I need for each data point to correspond to an (string) x-coordinate.
I am knew to d3 and I have yet to get accustomed to formatting axis.
I would also be great if anyone can point me out to how to add a tooltip. (Just an explanation)
Thank you everyone.
Not sure why it keeps saying your: "
It looks like your post is mostly code; please add some more details."
`
The scaleOrdinal is mapped to an array of alphabets but when you are calculating the cx you are mapping to an integer i. To resolve this:
Separate the labels as as array first:
var labels = ['A','B','C','D','F','E','Z'];
Then pass the labels to the domain:
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
Finally, when you call calculate the cx, you need to send a value which was used in the domain. In your case since your domain is an array of alphabets you need to reparse the i to that particular alphabet. Hence you need to return xScale(labels[i]) as below:
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(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
Full working snippet below. Hope this helps.
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
//labels
var labels = ['A','B','C','D','F','E','Z'];
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(i); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
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("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
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(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>
Updated Snippet with Lines:
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right
, height = window.innerHeight - margin.top - margin.bottom;
// n data points
var n = 7;
//labels
var labels = ['A','B','C','D','F','E','Z'];
// X scale
var xScale = d3.scaleBand()
.domain(labels) // input
.range([0, width]); // output
// Y scale
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height, 0]);
var line = d3.line()
.x(function(d, i) { return xScale(labels[i]); })
.y(function(d) { return yScale(d.y); })
.curve(d3.curveMonotoneX)
var dataset = d3.range(n).map(function(d) { return {"y": d3.randomUniform(1)()} })
// SVGs
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("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// x axis call
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// y axis call
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale));
svg.append("path")
.datum(dataset)
.attr("class", "line")
.attr("d", line);
// 12. Appends a circle for each datapoint
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(labels[i]) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 6);
svg.append("text")
.attr("class", "title")
.attr("x", width/2)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Testing");
/* 13. Basic Styling with CSS */
/* Style the lines by removing the fill and applying a stroke */
.line {
fill: none;
stroke: green;
stroke-width: 3;
}
/* Style the dots by assigning a fill and stroke */
.dot {
fill: red;
stroke: #fff;
}
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
</style>
<!-- Body tag is where we will append our SVG and SVG objects-->
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
</script>

Formatting year in D3 V4 to remove commas

I am creating a line chart in D3 v4.
The x-axis is showing the year with commas like 1,998 and 1,999 instead of 1998 and 1999 etc. It is addig the thousand comma which is what I am trying to remove.
I am trying to remove the commas, but I have not been able to. tickformat is not working in v4.
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 50, right: 50, bottom: 100, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(d.Amount); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 + ")");
// Get the data
d3.csv("australia.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.Year = d.Year;
d.Amount = +d.Amount;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Year}));
y.domain([0, d3.max(data, function(d) { return d.Amount; })]);
// Add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top) + ")")
.style("text-anchor", "middle")
.text("Year");
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
// text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Amount");
});
</script>
</body>
And here is my csv file:
Year,Amount
1998,103323
1999,57914.9
2003,297.969
2004,921253.8
2007,169869.2
2008,44685.5
2010,86084.5
Thanks,
You should use scaleTime for x axis, not scaleLinear:
var x = d3.scaleTime().range([0, width]);
You also should process your dataset this way:
var parseTime = d3.timeParse("%Y");
data.forEach(function(d) {
d.Year = parseTime(d.Year);
d.Amount = +d.Amount;
});
Check working example in the hidden snippet below:
var dataAsCsv = `Year,Amount
1998,103323
1999,57914.9
2003,297.969
2004,921253.8
2007,169869.2
2008,44685.5
2010,86084.5`;
// set the dimensions and margins of the graph
var margin = {top: 50, right: 50, bottom: 100, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(d.Amount); });
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 = d3.csvParse(dataAsCsv);
var parseTime = d3.timeParse("%Y");
// format the data
data.forEach(function(d) {
d.Year = parseTime(d.Year);
d.Amount = +d.Amount;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Year}));
y.domain([0, d3.max(data, function(d) { return d.Amount; })]);
// Add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top) + ")")
.style("text-anchor", "middle")
.text("Year");
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
// text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Amount");
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.js"></script>

How to hide all graphs with one click of a button in a scatterplot graph legend

I have this code below.
My original data has a lot more different kinds of data which make a big graph with lots of scatterplot points and the legend get big and a lot of times I need to deselect all the legens one by one to see one data that I need. I want a button that will deselect all the squares at once and then select the once I need. Is that possible. I cannot figure it out.
<!DOCTYPE html>
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
<style>
body { font: 10px sans-serif;
}
.axis path,
.axis line { fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot { stroke: #000;
stroke-width: 0px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 160px;
height: 28px;
padding: 2px;
font: 8px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
}
</style>
<body>
<script src="d3.js"></script>
<script src="d3.min.js"></script>
<div id="graphContainer1" class="graphContainer1">
</div>
<div id="graphContainer2" class="graphContainer2">
</div>
<script>
if(!(window.console && console.log)) {
console = {
log: function(){},
debug: function(){},
info: function(){},
warn: function(){},
error: function(){}
};
}
// set start of report (where to filter data on)
var dtReportStart = new Date();
//dtReportStart.setMonth(dtReportStart.getMonth() - 1); //************************ hour
dtReportStart.setDate(dtReportStart.getDate() - 14); // adjusted report to show only 2 weeks for TAR ************************ hour
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 100, left: 150},
width = 1800 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
legend_delta = 15;
var yScaleSize = 20;
// setup fill color
var cValue = function(d) { return d.jobtype;};
var color = d3.scale.category20b();
// add the tooltip area to the webpage
var tooltip = d3.select("body").select("#graphContainer1" ).append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Parse the date / time ************************ hour
var parseDate = d3.time.format("%Y-%m-%d %H:%M").parse;
// setup x
var xValue = function(d) { return d.date_time;}; // data -> value
var xScale = d3.time.scale()
.range([0, width]);
// xScale = d3.time.scale.linear().range([0, width]), // value -> display
xMap = function(d) { return xScale(xValue(d));}, // data -> display
xAxis = d3.svg.axis().scale(xScale).orient("bottom");
// setup y
var yValue = function(d) { return d.average_jobtime;}, // data -> value
yScale = d3.scale.linear().range([height, 0]), // value -> display
yMap = function(d) { return yScale(yValue(d));}, // data -> display
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Define the axes
var xAxis = d3.svg.axis().scale(xScale)
.orient("bottom")
.ticks(d3.time.hour, 12) //************************ hour
.tickFormat(d3.time.format("%Y-%m-%d %H:%M")); //************************ hour
var yAxis = d3.svg.axis().scale(yScale)
.orient("left")
.ticks(10);
function do_graph(graph_title,filetoUse,gcid) {
console.log ('doing graph')
// Adds the svg canvas
var svg = d3.select("body").select("#graphContainer" + gcid )
.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("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("font-weight", "bold")
.text(graph_title + " - average job duration per hour" ); //************************ hour
// Get the data
console.log (filetoUse)
d3.csv(filetoUse, function(error, data) {
data=data.filter(function(row) {
return parseDate(row['date_time']) >= dtReportStart;
})
data.forEach(function(d) {
d.average_jobtime = +d.average_jobtime;
d.jobtype = d.jobtype;
d.date_time = parseDate(d.date_time);
});
// Scale the range of the data
xScale.domain(d3.extent(data, function(d) { return d.date_time; }));
xScale.nice(d3.time.hour, 12); //************************ hour
//yScale.domain([0,1+d3.max(data, function(d) { return d.average_jobtime; })]);
yScale.domain([0,yScaleSize]);
//console.log("test :" + d3.max(data, function(d) { return d.average_jobtime; }) )
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", height-6)
.style("text-anchor", "end")
.text("Time");
svg.select(".x.axis")
.selectAll("text")
.attr("transform"," translate(-13,50) rotate(-90)"); // To rotate the texts on x axis. Translate y position a little bit to prevent overlapping on axis line.
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("average_jobtime (seconds)");
svg.selectAll(".dot" )
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 2)
.attr("id" , function(d) { return d.jobtype; } )
.attr("cx", xMap )
.attr("cy", yMap )
.style("fill", function(d) { return color(cValue(d));})
.on("mouseover", function(d) {
tooltip.transition()
.duration(50)
.style("opacity", 0);
tooltip.transition()
.duration(20)
.style("opacity", .9);
tooltip.html(d.jobtype + ": " + yValue(d) +"<br/> (" + xValue(d) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
});
// draw legend
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter()
.append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(-1680," + i * legend_delta + ")"; })
// .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
;
// draw legend empty rectangles
legend.append("rect")
.attr("x", width - legend_delta-2)
.attr("width", legend_delta-2 )
.attr("height", legend_delta-2)
.attr("border", 1)
.style("stroke", 'black')
.style("stroke-width", 1)
.style("fill", 'white')
;
// draw legend colored rectangles
legend.append("rect")
.attr("class", "fade_rectangle" )
.attr("x", width - legend_delta-2)
.attr("id" , function(d) { return d; } )
.attr("width", legend_delta-2)
.attr("height", legend_delta-2)
.style("fill", color)
.style("stroke", 'black')
.style("stroke-width", 1)
.style("opacity", 1)
.on("click", function (d, i) {
// register on click event
console.log ('opacity:' + this.style.opacity );
var lVisibility = this.style.opacity
console.log ('lVisibility:' + lVisibility );
filterGraph(d, lVisibility);
});
// draw legend text
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d;})
;
});
// Method to filter graph
function filterGraph( aType, aVisibility) {
console.log( "jobtype :" + aType + ",vis:" + aVisibility );
// change lthe visibility of the node
// if all the links with that node are invisibile, the node should also be invisible
// otherwise if any link related to that node is visibile, the node should be visible
newOpacity = 1 - aVisibility ;
console.log( "newOpacity :" + newOpacity);
// Hide or show the elements
d3.selectAll("#" + aType).style("opacity", newOpacity);
}
}
console.log ('start')
do_graph('PILOT 0' ,'test.csv','1');
console.log ('end')
</script>
</body>
test.csv:
20150723_080028,xxxMio,0,12246,Job finished JobReport,369,60736,61106
20150723_080136,pppMio,1,12331,Job finished JobReport,773,44959,45733
20150723_080141,tttMio,0,12421,Job finished JobReport,570,46836,47407
20150723_080238,fffMio,1,12531,Job finished JobReport,427,53571,53999
20150723_080304,xxxMio,0,12596,Job finished JobReport,257,52017,52275
20150723_080355,pppMio,1,12681,Job finished JobReport,777,43932,44710
20150723_080358,tttMio,0,12771,Job finished JobReport,569,43565,44135
Say I had a button with id of toggle:
<button id="toggle">Toggle All</button>
Then the code would be as simple as:
d3.select('#toggle')
.on("click", function(d){
var o = d3.select('.fade_rectangle').style('opacity') === "1" ? "0" : "1";
d3.selectAll('.dot')
.style('opacity', o);
d3.selectAll('.fade_rectangle')
.style('opacity', o);
});
This would use the first legend item to determine the state for all elements.
Working example.

In d3.js ticks are not showing in the x axis

I am trying to show a barchart using d3.js. Y axis contains the speed and x axis contains time. I am using the following code:
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 400 - margin.left - margin.right,
height = 250 - margin.top;
var x = d3.scale.ordinal().rangeRoundBands([ 0, width ], .1);
var y = d3.scale.linear().range([ height, 0 ]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(20);
x.domain(data.map(function(d) { return d.datetime; }));
y.domain([0, d3.max(data, function(d) { return d.speed; })]);
var svg=d3.select("#bar").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 transition = svg.transition().duration(750), delay = function(d, i) {
return i * 50;
};
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" ).text("Time");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Speed");
svg.selectAll("rect")
.data(data)
.enter().append("rect").transition().delay(0)
.style("fill", "red")
.attr("x", function(d,i) { return 30*i+20; })
.attr("width", 25)
.attr("y", function(d) { return y(d.speed); })
.attr("height", function(d) { return height - y(d.speed); }); //function(d){return " "+d.datetime;}
transition.select(".y.axis").call(yAxis);
But unfortunately no ticks are showing in the x axis. Y axis ticks are working fine. Can anyone help me regarding this?
In your x-axis creation, this:
.attr("transform", "rotate(-90)" ).text("Time");
is just going to but the word "Time" on each tick. What you need is:
// define the time format you want
var format = d3.time.format("%Y-%m-%d");
// create x axis
...
.text(function(d){
return format(d);
});
Also in your rect placement:
svg.selectAll("rect")
...
.attr("x", function(d,i) { return 30*i+20; })
You are spacing them based on index. You need to match this to the axis position:
svg.selectAll("rect")
...
.attr("x", function(d, i) {
return x(d.datetime);
})
Here's an example.
Your problem may be where you set the domain for x:
x.domain(data.map(function(d) { return d.datetime; }));
You probably want something like:
x.domain([d3.min(data, function(d) { return d.datetime; }), d3.max(data, function(d) { return d.datetime; })]);

Resources