d3.js x axis date range - d3.js

i have some values in my csv file and i show a graph with values on y axis and dates on x axis.
For first graph i have following values
date,close
13-Jul-16,0.8736701869033555
15-Jul-16,0.3631761567983922
17-Jul-16,0.4795564555162078
19-Jul-16,0.3754827857186281
21-Jul-16,0.4355941951068847
23-Jul-16,0.34393804366457353
25-Jul-16,0.40967947088135176
27-Jul-16,0.2707818657230363
29-Jul-16,0.34430251610420176
31-Jul-16,0.28089496856221585
For second graph i have following values
date,close
11-Jul-16,0.766705419439816
15-Jul-16,0.7353651170975812
17-Jul-16,0.41531502169603063
19-Jul-16,0.5927871032351933
21-Jul-16,0.7986419920511857
23-Jul-16,0.7904979990272231
25-Jul-16,0.817690401573838
27-Jul-16,0.8433545168648027
29-Jul-16,0.8612307965742473
31-Jul-16,0.806498303188971
But in second graph x axis does not contain all dates.. As an example i put a printscreen of my output graphs myoutput to here.
This is my code which takes datas from csv file and visualize it.
var selectedMonth=document.getElementById('selectedMonth').value;
var selectedTopic=document.getElementById('selectedTopic').value;
var userFileDirectory="../documents/";
userFileDirectory=userFileDirectory+selectedMonth+"/"+selectedTopic+"/"+"dataCs.csv";
// 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;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// 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(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
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(userFileDirectory, function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// 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);
svg.append("text")
.text("("+selectedMonth+" "+selectedTopic+")");
});

I would try setting the tick values explicitly, using tickValues:
https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickValues
ticks(5) will suggest 5 ticks, but will be adapted based on the scale's domain. Alternative to tickValues(), you could try ticks(d3.time.day, 2) to have a tick every 2 days.

Related

Parsing a dataset of time in the format "Y-M-D H:M:S.MS" gives me 0NaN-NaN-NaNTNaN:NaN:NaN.NaNZ

I am able to see glucose readings but time shows up as: 0NaN-NaN-NaNTNaN:NaN:NaN.NaNZ
I am trying to parse a dataset of time of the format "Y-M-D H:M:S.MS". I need it to be formatted properly so that I can show it on the x axis. I have attached sample dataset to this code.
My code looks like this:
<script>
function overview(){
// Set the dimensions of the canvas / graph
var margin = {top: 10, right: 20, bottom: 30, left: 30},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// // Parse the date / time
var parseDate = d3.utcFormat("%Y-%m-%dT%H:%M:%S.%LZ");
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.tickFormat(d3.timeFormat("%H"));
var yAxis = d3.axisLeft();
// Define the line
var valueline = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.glucoseReading); });
// Adds the svg canvas
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("glucose.csv", function(error, data) {
data.forEach(function(d) {
d.time = parseDate(d.time);
d.glucoseReading = +d.glucoseReading;
console.log(d.time);
console.log(d.glucoseReading);
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain([0, d3.max(data, function(d) { return d.glucoseReading; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.time); })
.attr("cy", function(d) { return y(d.glucoseReading); });
// 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);
});
}
overview();
</script>
My Dataset looks like:
You want to convert (parse) strings to dates, not the other way around. Therefore, instead of d3.utcFormat(), you have to use d3.utcParse(). On top of that, your specifier is incorrect: there is no timezone in your strings.
So, this should be your parseDate function and specifier:
var parseDate = d3.utcParse("%Y-%m-%d %H:%M:%S.%L")
Here is it working (check your browse console, not the snippet's one):
var parseDate = d3.utcParse("%Y-%m-%d %H:%M:%S.%L")
var string = "2017-08-23 00:03:52.591";
console.log(parseDate(string))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Constraining axis labels in d3

I am following a tutorial so I can learn a bit of d3js.
Here is my code:
'use strict';
//Dashboard
//setup size of line chart
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
//parse data from file
var parseDate = d3.time.format("%b").parse;
//set scales
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
//create axes
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//construct the line using points from data
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.users); });
var svg = d3.select(".linechart").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.tsv("data.tsv", function(error, data) {
if (error) throw error;
//traverse through the data
data.forEach(function(d) {
d.date = parseDate(d.date);
d.users = +d.users;
});
//establish the domain for x and y axes
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.users; }));
//add "groups"
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("Users (unique)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
The results look like this:
The data is:
date users
Jan 10
Feb 20
Mar 30
....
My question is about the axis, how can I force it to not insert labels on the x axis that are not in the data set?
Set ticks for x axis manually:
...
if (error) throw error;
var ticks = data.map(function(d) { return parseDate(d.date) };
...
x.domain(d3.extent(data, function(d) { return d.date; })).tickValues(ticks);
https://github.com/d3/d3-3.x-api-reference/blob/master/SVG-Axes.md#tickValues

D3 x axis value stacked on the left

I've tried to make a simple line chart.
The x axis will show 10,100,1000 etc' values
For some reason I get all the values stacked on the left side of the x axis instead of spreading them equally on the axis.
var data = [
{views:10, odds: 56},
{views:100, odds: 64},
{views:1000, odds: 81},
{views:10000, odds: 95},
{views:100000, odds: 99},
{views:1000000, odds: 99},
{views:10000000, odds: 100},
];
// 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;
// Set the ranges
var x = d3.scale.linear()
.domain([
d3.min(data, function(d) { return d.views; }),
d3.max(data, function(d) { return d.views; })
])
.range([0, width]);
var y = d3.scale.linear()
.domain([
d3.min(data, function(d) { return d.odds; }),
d3.max(data, function(d) { return d.odds; })
])
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(7)
.tickValues(data.map((d)=>{ return d.views; }));
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(7);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.views); })
.y(function(d) { return y(d.odds); });
// Adds the svg canvas
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 + ")");
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// 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);
https://jsfiddle.net/guy_l/77agq0hz/
This is the expected behaviour. They are not "stacked" on the left side, it's just a math problem: each value of x is just 10% of the next value! Keep in mind that your domain goes from 10 to 10 million, so the points would never be equally spread: 90% of your domain is just the space between the 6th and the 7th point.
You can change the scale for an ordinal one or, if you want to keep it quantitative, you need a logarithmic scale here:
d3.scale.log();
Check your updated fiddle: https://jsfiddle.net/gerardofurtado/v17cpqdk/

D3 single line chart with tsv data

I am trying to import single-column tsv data in order to create a line chart with d3.js, but I am getting an error message, and I don't know how to solve this.
Here is my code (based on already existing examples):
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50}, // as if margin would be an object
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis() // we create a new axis
.scale(x) // we set the scale based on the var x
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d,i) {
console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
return x(i);
})
.y(function(d) { return y(d.values); });
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.tsv("data.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.length; })); // get min and max date values
y.domain(d3.extent(data, function(d) { return d.values; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis); // we call also a var!!
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("Values");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
function type(d) {
d.values = +d.values; // converts string to number
return d;
}
</script>
And here is the tsv file:
values
1.5
2.6
3.4
7.8
3.8
1.0
6.7
The error I get is:
"Invalid value for <path> attribute d="MNaN,416.9117647058824LNaN,344.1176470588235LNaN,291.17647058823525LNaN,0LNaN,264.70588235294116LNaN,450LNaN,72.79411764705881"
Apparently my data index is not correctly read.
Could someone maybe help?
One change instead of
x.domain(d3.extent(data, function(d) { return d.length; })); // get min and max
Do this since the x axis is the length of the array.
x.domain([0, data.length-1]); // get min and max
Working code here
Hope this helps!

How to transition area along one axis in D3.js

I am trying do some path interpolation in D3. I'd like to produce an area plot like this, but I want to transition the area along the y-axis, starting from the bottom of the xaxis up to the final position shown in the example. Here's a quick sketch to explain what I'd like to do:
I'd like to start the transition with no area:
and transition it up along the y-axis:
Using the code, copied from the example, here's what I'd trying to do:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
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");
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
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.tsv("data.tsv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
var dataSel = svg.selectAll('.area').data(data)
dataSel.exit().remove()
dataSel.enter()
.append('path')
.attr("class", "area")
.attr("d", 'M0,0h' + width) // my idea here was to draw a path that
// has no area along the x-axis and then
// interpolate the path up to the final area
dataSel.transition() // transition the path to its final position
.duration(1000)
.attr("d", area)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
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("Price ($)");
});
Can anyone explain/show how I can transition a path along one axis as I'm trying to do?
D3 path transitions only really work if the starting and ending paths have the same number of control points. So, for example, D3 can't transition a line into an area. What you could do, however, is something like the following:
Use d3.area to generate the final path for the area.
Make a copy of the path and, in the copy, change all the control points that correspond to the "top" of the area to control points on the "bottom" of the area. (In other words, modify their y-values.)
Draw the area using this modified path.
Transition to the final path.

Resources