How to render/ format axes each 6 month with d3.scaleTime? - d3.js

I am trying to make chart like this, using d3.js v.4. But data that i have is twice each year, differ with the given example. How to render axes for every 6 month? My data is every February, and August for each year (2008-2015).

set your x line with scaleTime and startDate and endDate
when you create an axis you can pass an interval with filter to tick func.
If needed you can also format the axis tick with tickFormat.
Here the example using coffeescript
timeParse= d3.timeParse('%m-%Y');
startDate= timeParse('02-2008');
endDate= timeParse('08-2015');
x= d3.scaleTime()
.domain d3.extent d3.timeMonth.range startDate, endDate, 6
.range [0, 700];
xAxis= d3.axisBottom(x)
.ticks d3.timeMonth.filter (xDate)->
# january is 00
# console.log 'getMonth: ', xDate.getMonth();
if xDate.getMonth() is 1 or xDate.getMonth() is 7
true;
else
false;
.tickFormat d3.timeFormat '%b %Y';
svg.append 'g'
.attr 'class', 'axis-group'
.attr 'transform', "translate(0, 300)"
.call xAxis;
output:
Note the that the stop range are excluded as describe here
Reference:
Month Axis

Related

How to get abbreviated month name as X-axis in Linechart in DC.js

I am trying to get abbreviated month name in X-axis for DC.js line chart. I have formatted the month column and parsed it before sending it to dimension.Even after formatting the ticks in the line chart, I am getting "Dec" for all of them. Below is the code . Please help me to resolve it
var parseTime = d3.timeParse("%B %d, %Y");
var parseMonth = d3.timeFormat("%B");
canadaclimatProjects.forEach(function (d) {
var date = parseTime(d["year_mon"]);
d["year_mon"] = parseMonth(date);
});
var newMonth = ndx.dimension(function (d) { return d["year_mon"]; });
monthlyPrecipitationChart
.width(380)
.height(250)
.round(d3.timeMonth.round)
.xUnits(d3.timeMonths)
.x(d3.scaleTime().domain(newMonth))
.dimension(newMonth)
.group(precipitationByMonth)
.brushOn(false)
.valueAccessor(function (p) { return p.value.count > 0 ? p.value.total / p.value.count : 0; })
.elasticX(true)
.elasticY(true);
.xAxis().ticks(12).tickFormat(d3.timeFormat("%b"));
d3.scaleTime() won't work if you are passing in strings as the keys of your data. You need to pass in Date objects instead.
You've already handled printing the ticks with abbreviated months with
.xAxis().ticks(12).tickFormat(d3.timeFormat("%b"));
so it's redundant (and won't work) to format the dates on input.
Rarely is it a good idea to pass formatted data into a chart. Formatting usually happens after the chart is drawn.
Hard to say why you are getting the results you describe, without a complete runnable example.

How to remove weekends dates from x axis in dc js chart

I have data for every date from Jan 2018 and I am creating a stacked line chart out of that data. Every weekend the count of my data is zero, so every weekend it shows a dip in my graph (as data reaches to zero). I want to avoid that dip. I have a Date column as well as a Day column. The Day column has values from 1 to 7 representing each day of week (1 is Monday and 7 is Sunday). Can I modify my x axis or graph to show only weekdays data?
Fiddle
var data = [
{ Type: 'T', Date: "2018-01-01", DocCount: 10, Day: 1},
{ Type: 'E', Date: "2018-01-01", DocCount: 10, Day: 1},
...
]
chart
.height(350)
.width(450)
.margins({top: 10, right: 10, bottom: 5, left: 35})
.dimension(dateDim)
.group(tGroup)
.stack(eGroup, "E")
.valueAccessor( function(d) {
return d.value.count;
})
.transitionDuration(500)
.brushOn(false)
.elasticY(true)
.x(d3.time.scale().domain([minDateTime, maxDateTime]))
.xAxis().ticks(10).tickFormat(d3.format("s"));
A time scale is always going to be very literal about how it maps dates to x coordinates. It has no notion of "skipping dates".
Instead, I would suggest using an ordinal scale for this purpose. With an ordinal scale, you decide exactly what the input and output values will be. dc.js will also help you out by automatically determining the input (domain) values.
Tell the chart to use an ordinal scale like this:
chart
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
Remove any empty dates like this. remove_empty_bins is from the FAQ but I modified it to look at the count element.
function remove_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
//return Math.abs(d.value) > 0.00001; // if using floating-point numbers
return d.value.count !== 0; // if integers only
});
}
};
}
var nz_tGroup = remove_empty_bins(tGroup),
nz_eGroup = remove_empty_bins(eGroup);
chart
.group(nz_tGroup)
.stack(nz_eGroup, "E")
Only question is, what if there is a weekday that happens not to have any data? Do you still want that to drop to zero? In that case I think you'd probably have to modify the filter in remove_empty_bins above.
Fork of your fiddle.

D3 x-axis date not showing for Sundays

I'm using the code below to display the day of the week and the month value for my data. D3 displays the date as expected e.g. "Sat 18" but for Sunday it shows the month instead e.g. "Oct 19"!
Can't figure out why this is happening. The code is:
var xScale = d3.time.scale()
.domain([new Date($scope.dataset[0][0].time),d3.time.day.offset(new Date($scope.dataset[0][$scope.dataset[0].length-2].time),2)])
.rangeRound([0, w-0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(d3.time.days,1);
Any ideas why this is happening? the sample dataset looks like this:
{time: "2014-10-19",y: 0,y0: 0}
By default, D3 will apply some adaptive formatting to the date tick values (see "This set of time intervals is somewhat arbitrary and additional values may be added in the future" section), but you can override this with a specific date format, for example:
.ticks(d3.time.days,1)
.tickFormat(d3.time.format('%a %e'));
You can find the remainder of the formatting options here.

Showing Date range in d3.js graph axis

Right now for showing time on a line chart, I'm doing:
d3.svg.axis().scale(x).orient('bottom').ticks(numTicks, 1)
.tickFormat(d3.time.format('%a %d'))
which shows me dates like: Tue 12 on x-axis.
However, now I want to show the date range (something like: Tue 12-Tue 19) on the axis, but not getting how to do this.
Finally I had to do something like this:
var xAxis = d3.svg.axis().scale(x).orient('bottom').ticks(numTicks, 1)
.tickFormat(function(d,i) {return dateRanges[i]}).tickSize(6).tickPadding(8);
where dateRanges = an array containing all the dateRange string

D3.js time scale: nicely-spaced ticks at minute intervals when data is in seconds?

I'm working with a D3 time scale. My input data is in seconds, and it's duration data rather than dates - so 10 seconds, 30 seconds etc.
I want to create an axis that lets me do the following:
Display ticks formatted in minutes and seconds: like "0m 30s", "1m 00s", etc. This formatting on its own is fairly straightforward, but not when I also need to...
Display ticks at intervals that look neat when formatted in minutes. If I just use D3's default tick formatting then I get ticks at intervals that make sense in minutes, but not seconds.
Here is my code:
var values = [100,200,300....]; // values in seconds
var formatCount = d3.format(",.0f"),
formatTime = d3.time.format("%Mm %Ss"),
formatMinutes = function(d) {
var t = new Date(2012, 0, 1, 0, 0, d);
t.setSeconds(t.getSeconds() + d);
return formatTime(t);
};
var x = d3.scale.linear()
.domain([0, d3.max(values)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatMinutes);
This gives me nicely-formatted ticks at irregular intervals: "16m 40s", "33m 20s" etc. How can I generate ticks at "10m 00s", "20m 00s", etc?
The obvious answer would be to transform the values array into minutes, use a linear scale and write a formatter to handle it, but I'd prefer to use a time scale if possible
Here is a JSFiddle to demonstrate the problem: http://jsfiddle.net/83Xmf/
Normally when making a time scale, you would use d3.time.scale(), rather than a linear scale.
Your case is a little odd in that you are using abstract durations of time, and not specific points in time for your data. Unfortunately it seems that d3's built in time functionality is not well-suited to this case. There are a couple of options I can think of for workarounds:
Option 1: Use a linear scale with manual .tickValues()
Rather than formatting your ticks using a Date object. You could simply break down your data value (which is in seconds) into hours, minutes, and seconds. Something like this:
formatMinutes = function(d) {
var hours = Math.floor(d / 3600),
minutes = Math.floor((d - (hours * 3600)) / 60),
seconds = d - (minutes * 60);
var output = seconds + 's';
if (minutes) {
output = minutes + 'm ' + output;
}
if (hours) {
output = hours + 'h ' + output;
}
return output;
};
Basically, this takes the total number of seconds, creates an hour for every 3600 seconds, creates a minute for each remaining 60 seconds, and finally gives back the remaining seconds. Then it outputs a string representation, for example: 17s or 12m 42s or 4h 8m 22s.
Then when you make your axis, you can use the .tickValues() method to assign a range from zero to your data's max value, going by steps of 600, since there are 600 seconds in 10 minutes. That would look like this:
var x = d3.scale.linear()
.domain([0, d3.max(values)])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatMinutes)
.tickValues(d3.range(0, d3.max(values), 600));
Here's a JSFiddle of the output.
Option 2: Use a time scale with a fixed duration for .ticks()
Time scales let you specify directly that you'd like ticks every 10 minutes. You do that simply by passing a d3 duration and a multiplier to the .ticks() method of your axis. Like this:
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.minute, 10)
In order to do this, you must first set up your time scale. For the domain of your scale, you can use a range of millisecond values, since d3 will turn these into Date objects. In this case, since your data is in seconds, we can simply multiply by 1000 to get milliseconds. In this case we'll round up the max value to the nearest millisecond, since it must be an integer to make a valid date:
var x = d3.time.scale()
.domain([0, Math.ceil(d3.max(values) * 1000)])
.range([0, width]);
Finally, you can pass your format in directly to the axis, using .tickFormat():
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.minute, 10)
.tickFormat(d3.time.format('%Mm %Ss'));
However, at this point I need to point something out because, as I mentioned, the built-in time functions are not well-suited to dealing with abstract durations. I'm going to change the .tickFormat to show the hours as well:
.tickFormat(d3.time.format('%Hh %Mm %Ss'));
Have a look at the JSFiddle of what the result would be...
Depending on where you are in the world, you'll get a different value for the hours place. I'm on the East coast of the US, so my hours place says 19. Where is that coming from? Shouldn't it be zero?
Well, unfortunately, when we made the domain of the scale go from 0 to the number of milliseconds of the largest data value, it created regular Date objects, using those values for the millisecond input. This means that they represent the number of milliseconds since midnight UTC time on January 1, 1970. Here in the Eastern time zone of the US, that means it was 19:00:00 on December 31, 1969. That's where the 19 comes from, or whatever other value you get.
If you know that all of your data will be less than 1 hour, then perhaps you can just ignore this. If you need to use an hours place, you can work around this by forcing d3 to use UTC time to format the axis using d3.time.format.utc():
.tickFormat(d3.time.format.utc('%Hh %Mm %Ss'))
Here's the JSFiddle updated to use UTC.
Now you can see that the hour is 0 as expected.
Of course, if any of your data is ever longer than 24 hours, this method won't work at all, and you'll have to resort to doing the axis manually as in Option 1.
Hopefully this helps to at least get you started, it's a tricky problem though, and there doesn't seem to be an elegant solution built into the library for handling this. Perhaps it would make for a good feature request on d3's git repo. I'd love to hear if #mbostock has any suggestions on how to handle abstract durations of time in d3 without having to be tied to Date objects, which require references to absolute points in time.

Resources