Using dc.js to build some charts. The localHour attribute contains numbers between 0 and 23. However, when using this on my axis, all numbers are reported as thousandths instead of the standard hour. 04 PM also appears at the origin.
How can I fix this?
var hourDim = ndx.dimension(function(d){ return d.hour; });
var num = hourDim.group().reduceSum(dc.pluck('count'));
var mainChart = dc.lineChart("#main");
mainChart
.width(500).height(200)
.dimension(hourDim)
.group(num)
.x(d3.time.scale().domain([0,24]))
.yAxisLabel("Count per Hour")
What's actually going on here is that your "hour" measurements are being interpretted as milliseconds. Milliseconds are the default Javascript time unit if you don't specify otherwise. Specifically, you're getting milliseconds after the Javascript zero time, which is sometime on Dec 31 1969 or Jan 1 1970 depending on timezone adjustment, and apparently starts at 4pm in your timezone. The rest is just default formatting trying to make things look nice.
Unless you're doing other things that require the hours to be treated as timestamps, it is probably easiest to leave the hours as plain numbers, using a linear scale instead of a time scale.
If you're fine with plain old "1", "2", "3" on the axis labels, that's all you have to do.
If you want those numbers to look like hours, you need to set a tickFormat function on the chart axis.
You could just do something like
mainChart.x(d3.scale.linear().domain([0,24])
.tickFormat(function(h){return h + ":00";})
);
But that causes problems if the axis decides to put ticks at fractional values -- you'll get something that looks like 1.5:00 instead of 1:30. You could fix that with some math and number formatting functions, but at that point you're doing enough work to make it worth using proper date-time formatting.
To get proper hour:minute axis labels, you can use a d3 time formatting function to specify the format, but you're also going to have to translate the number of hours into a valid date-time object.
var msPerHour = 1000*60*60;
var timeFormat = d3.time.format.utc("%H:%M");
mainChart.x(d3.scale.linear().domain([0,24])
.tickFormat(function(h){
return timeFormat(new Date(msPerHour*h) );
})
);
Note that I've specified the time format function to use UTC time instead of local time, so that it treats zero as midnight. It still thinks it's midnight, Jan 1 1970, but you're also specifying the formatting to only include hours and minutes so that shouldn't be an issue.
Related
I need to display on the screen some date values but I'm receiving them in a format that I don't know. Does anybody know what format is this and how to convert them?
For example, I'm getting this:
/Date(1427982649000-0400)/
In the database is stored as
2015-04-02 09:50:49.000
I really don't know where to start looking at.
It's a unix timestamp in milliseconds, followed by a timezone (shift in hours differing from UTC).
So, it's UTC -4 hours, 1427982649 seconds after the 1st January of 1970.
Nice little tool for checking unix timestamps : http://www.unixtimestamp.com/index.php (don't forget to convert your milliseconds to seconds before posting them there)
/edit: To add some additional information - the "timezone shift" seems to be following RFC822 (and/or probably some other RFCs), that -0400 can be explained by the syntax "+/-HHMM" specified there, so to be exact it means -04 hours, 00 minutes.
The actual time and date gets converted into the milliseconds, and it follows the Unix time January 1st, 1970.
Because it is the date when the time for the Unix computer started.
But you can convert the milliseconds into the actual time by using some loops or conversions according to that time.
Does anybody know what format is this and how to convert them?
It seems that "/Date(1427982649000-0400)/" is a time value in milliseconds followed by an offset as ±HHmm. To convert that to a Date, use the time value adjusted by the offset.
Assuming the offset uses the typical sign convention, then a positive offset needs to be subtracted and negative offset added to get the correct UTC value, then something like the following should suit:
var s = '/Date(1427982649000-0400)/';
// Get the number parts
var b = s.match(/\d+/g);
// Get the sign of the offset
var sign = /-/.test(s)? -1 : +1;
// Adjust the time value by the offset converted to milliseconds
// and use to create a Date
var ms = +b[0] + sign * (b[1].slice(0,2)*3.6e6 + b[1].slice(-2)*6e4);
console.log(new Date(ms).toISOString()); // 2015-04-02T17:50:49.000Z
In your example, "2015-04-02 09:50:49.000" does not have a timezone, so it represents a different moment in time for each timezone with a different offset. If that is the actual value stored in the database, then I guess the missing timezone is UTC-0800. It is much better to store the values using UTC and to include the offset, then the host timezone is irrelevant.
Things are complicated here because ECMAScript timezone offsets are the opposite sign to the normal convention, i.e. positive for west of Greenwich and negative for east. If that convention is applied, then "/Date(1427982649000-0400)/" converts to 2015-04-02T09:50:49.000Z, which may be what you're after.
If that is the case, just change the sign in the line:
var sign = /-/.test(s)? -1 : +1;
to
var sign = /-/.test(s)? +1 : -1;
I am currently creating a d3 axis using d3.time.scale.utc(). My input for the axis is a series of time offsets in minutes (with decimal values). For example:
var minuteOffsets = [0.03, 1.65, 3.22, ..., 89.91, 90.01];
I want to display these time offsets in mm:ss format on the axis. The axis labels can be at standard intervals, like so:
+------+------+-- ... --+------+------+-- ... --+------+
00:00 00:30 01:00 59:30 60:00 60:30 90:00 90:30
Note specifically that the minute value should show values >60. The seconds value has the normal range 0-59.
When I tried using .tickFormat(d3.time.format('%M:%S')), it wraps the minute value back to 00:00 after the 45:00 label. I also had a look at duration from moment.js, but I can't figure out how exactly to incorporate that into my code.
Based on the comment by Lars Kotthoff, I changed my scale to be a linear scale, instead of a time scale. Then I added a custom tickFormat function to do the necessary formatting.
I used MomentJS to first create a duration from the minute values, and then used the library moment-duration-format to produce the tick label in mm:ss format.
var axis = d3.svg.axis().tickFormat(function (d) {
return moment.duration(d, 'minutes')
.format('mm:ss', { trim: false });
});
This helps to avoid explicitly writing code for extracting the minute and second components, and then formatting them into a string.
I'm trying to visualize some data I have. Here is the fiddle: http://jsfiddle.net/hohenheim/6R7mu/10/
For this visualization, I started a small subset of the JSON data I'm trying to visualize, data2 which looks like
data2 = [
{
"startDate":1396263600.0,
"adId":2483231759355,
"endDate":1401101940.0,
"impressions":754831
},
{
"startDate":1393851600.0,
"adId":2750329551133,
"endDate":1404212340.0,
"impressions":3947368
}
];
Notice there is a date range in the data. My goal is to split the impressions uniformly into all the days in that date range and aggregate all the impressions per day over all ads. IE the final result will only have 2 attributes: date and impressions. I have a solution that SHOULD work but days are not aggregating properly because the millisecond representations of the days are not equal even though they may approximate to the same day. For example, 1398164400000 and 1398171600000 represent the same day but have different millisecond values.
Basically, if you examine the middle part of the region on the chart, notice there are oscillating values but really each pair of differing values should actually represent just 1 day summed together.
Is there any way to properly merge 2 millisecond versions of dates that approximate to the same day?
Most clocks that are used to produce timestamps don't include leap-seconds, so you can isolate the day part by subtracting
(t % (24 /* hours per day */
* 3600 /* seconds per hour */
* 1000 /* milliseconds per second */ ))
from the time-stamp t.
Even if the timestamp does include leap-seconds, that function will still give you a pretty good proxy for day-of comparison. It just may get dodgy with timestamps that are close to midnight.
If you are in a time-zone that is far from the day boundary, you will probably want to adjust your timestamp to millis since the epoch in the local timezone, and just ignore DST which again will introduce some inaccuracy around midnight.
I am trying to have my barchart in d3.js update it's values when the user changes the time scale from days, to weeks or months. (i.e. when the time scale is changed to weeks, I want all the data values for each day in a given week summed together). For example, here is the default graph with the x-axis time scale in days:
x-Axis in days
When a user changes the time scale to weeks and the x-axis updates, the data values remain grouped by day, as shown here:
x-Axis scale in weeks, but data values remain grouped by day:
What I want is for there to be only one bar for each week number of the year on the x-axis, showing the sum of all the data values the user provided for all 7 days of that week. How do I achieve this?
Does this have to be done on the server-side, or can it be on the client-side with javascript, or is there some easy d3.js way I'm overlooking?
This is what my data looks like:
[{"date":"2013-04-20","load_volume":400},{"date":"2013-04-23","load_volume":400},{"date":"2013-04-24","load_volume":400},{"date":"2013-04-28","load_volume":1732},{"date":"2013-04-30","load_volume":400}]
I figured to achieve this I could convert the date values to weekNumberOfYear format (for e.g., 17 for this week), push them into an array and remove all duplicates, then sum the data values for each of the days in that array. I did this and the data looked like this:
[{"date":"15","load_volume":400},{"date":"16","load_volume":2532},{"date":"17","load_volume":400}]
However, I don't believe this is the correct approach because I always get an "Error: Invalid value for attribute x="NaN"" in the JS console. This I think is because I use the x scale to position the rects on my graph:
.attr("x", function(d) { return padding + x(new Date(d.date)); })
... which would result in x(Wed Dec 31 1969 19:00:00 GMT-0500 (EST)), which throws a NaN error.
I am now trying to format the date into %Y-%m-%d format and have it be the beginning Monday of each week, but I'm wondering if there is an easier solution since I've been at this all day.
Well, I think I've figured it out. I just had to convert the dates to millisecond time and remove the double quotes I had around the date value in the JSON string (the double quotes were giving me a NaN error). I did this with the following function:
function getWeekDate(d) {
d = new Date(d); // get current date
var day = d.getDay();
var diff = d.getDate() - day + (day == 0 ? -6 : 1); // Subtract day number of month from day number of week, and adjust when day is sunday
var date = new Date(d.setDate(diff));
return date.setHours(0);
}
which is adapted from this SO question.
Not sure if D3 has a better way of doing it. If I find out i'll post it here.
Solved my own problem. I wanted to convert seconds to HH:mm:ss using moment.js. I used the following code, which worked, but I didn't know why moment(0) was setting the time of day to 19 00
var yearZero = moment(0).subtract('hours',19); //beginning of time
var sTimecode = yearZero.add('seconds', secondsUntilEvent).format("HH:mm:ss");
From http://momentjs.com/docs/#/parsing/unix-offset/
moment(Number);
Similar to new Date(Number), you can create a moment by passing an integer value representing the number of milliseconds since the Unix Epoch (Jan 1 1970 12AM UTC).
Didn't see anything about starting at 7PM / 19 00 there
The problem was that I was outputting it without declaring UTC. The following change did the trick:
var sTimecode = moment(0).add('seconds', secondsUntilEvent).utc().format("HH:mm:ss");
Perhaps someone else encountered the same problem (or not). Good day!
Instead of doing such a complicated workaround, you can always use the moment constructor to read an integer, which is interpreted as the number of milliseconds from the unix epoch. This works fine for me:
var value = moment(18000 * 1000).format('HH:mm:ss'); // 5 Hours
document.getElementById('date').innerHTML = value;
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>
<pre id="date"></pre>
Obviously, the time is displayed accordingly to your browser's timezone