D3 Time Scales
FREE     Duration: 11:37
Part of Course: Introductory D3 Course
 

Takeaways:

  • D3 Time Scales are an extension of the D3 Quantitative Scales that use the JavaSCript Date Object
  • The Scale domain is the input data, the Scale range is the output data
  • JavaScript Date Objects enable basic storage and retrieval of dates and times
  • If you put a plus sign in front of a JavaScript date object, JavaScript returns the number of milliseconds since January 1, 1970, 00:00:00 UTC (Coordinate Universal Time)
  • The D3 Time Scale is d3.time.scale
  • The D3 Time Scale is both an object and a function - you can call the scale just like any other function (pass in an argument and a result returns) and the scale has additional methods that change its behavior
  • The D3 Time Scale domain expects JavaScript Dates
  • The D3 Time Scale range expects numerical values

Transcript:

D3 Time Scales

D3 Scales Revisited


Quantitative Scales - for continuous input domains, such as numbers.

Ordinal Scales - for discrete input domains, such as names or categories.

Time Scales - for time domains.


D3 provides three types of functions that map an input domain to an output range.

The Quantitative scales are for real numbers

The Ordinal Scales are Discreet domains, such as letters of the alphabet

The Time Scales are an extension of the Quantitative Scales that use the JavaScript Date Objects.

All of these scales take in data and convert it to a useable set of output.

Sometimes this means scaling up the data and sometimes it means scaling down the data.


Quantitative Scales - Linear Scales


y = mx + b

D3 Quantitative Scales are for continuous input domains, such as numbers.

The mapping is linear in that the output range value y can be expressed as a linear function of the input domain value x.

So we get y = mx + b


d3.scale.linear()
    .domain( [0,400] )
    .range( [0,200] );

Rather than having to do this math ourselves, we can get D3 to do the math for us.

Using the D3 Scale Linear

We can tell D3 that the initial data covers 0 to 400

and we want it to cover 0 to 200 after it has been scaled.

This then figures out the correct math for the y=mx+b equation.

The initial data is entered in the domain.

What we want the data to be scaled to is put into the range.


d3.scale.linear()
    .domain( [0,400] )
    .range( [0,200] );

The domain takes in 1 array that contains two numbers in it.

Note - these numbers can be anything you want.

These numbers should cover all the possible values of the data you are using.


d3.scale.linear()
    .domain( [0,400] ) 
    .range( [0,200] );

The range takes in 1 array that contains two numbers in it.

Note - these numbers can be anything you want.

These numbers should cover all of the possible values that you want the data to be mapped to.


ℝ - Real Numbers
LINE 1: Domain
----------A---------B---------C---------
LINE 2: Range
----------D---------E---------F---------

The Domain is Line 1

The Range is Line 2

The Range is thus the result of the y=mx+b transformation from Line 1 to Line 2

The left most element in Line 1 gets transformed to the left most element in Line 2

And the right most element in Line 1 gets transformed to the right most element in Line 2

And the middle elements in line 1 get transformed to the middle elements in line 2.



JavaScript Date Objects Revisited


var dateObject = new Date();

JavaScript Date Objects enable basic storage and retrieval of dates and times.

JavaScript Date Objects can only be instantiated by calling JavaScript Date as a constructor with the word new.

Calling it as a regular function, without the new operator, will return a string rather than a Date object;


Date measured as Number of milliseconds since January 1, 1970, 00:00:00 UTC UTC = Coordinated Universal Time

The Time Zone on this date and time is UTC - The Coordinated Universal Time.


new Date()

  • getDate()
  • getDay()
  • getFullYear()
  • getHours()
  • getMilliseconds()
  • getMinutes()
  • getMonth()
  • getSeconds()
  • getTime()
  • getTimezoneOffset()
  • getUTCDate()
  • getUTCDay()
  • getUTCFullYear()
  • getUTCHours()
  • getUTCMilliseconds()
  • getUTCMinutes()
  • getUTCMonth()
  • getUTCSeconds()
  • parse()
  • setDate()
  • setFullYear()
  • setHours()
  • setMilliseconds()
  • setMinutes()
  • setMonth()
  • setSeconds()
  • setTime()
  • setUTCDate()
  • setUTCFullYear()
  • setUTCHours()
  • setUTCMilliseconds()
  • setUTCMinutes()
  • setUTCMonth()
  • setUTCSeconds()
  • toDateString()
  • toISOString()
  • toJSON()
  • toLocaleDateString()
  • toLocaleTimeString()
  • toLocaleString()
  • toString()
  • toTimeString()
  • toUTCString()
  • UTC()
  • valueOf()

These are all of the methods available to the JavaScript Date Object

There are many ways to get certain parts of the date and times.

There are also many ways to set certain parts of the date and times.

When working with Time Series in JavaScript and D3, these come up frequently.


var dateObject = new Date();

+dateObject;

If you put a plus sign in front of the dateObject, JavaScript returns the number of MilliSeconds since the starting date.

This is very useful if you are doing date Object Comparisons

Or adding and subtracting Date Objects.

Or Doing D3 Time Scales.



D3 Time Scales


Quantitative Scales - for continuous input domains, such as numbers.

Ordinal Scales - for discrete input domains, such as names or categories.

Time Scales - for time domains.


D3 provides three types of functions that map an input domain to an output range.

The Time Scales are an extension of the Quantitative Scales that use the JavaScript Date Objects.

The Time Scales take in data and convert it to a useable set of output.

Sometimes this means scaling up the data and sometimes it means scaling down the data.


Quantitative Scales - Linear Scales >> Time Scales y = mx + b

D3 Quantitative Scales are for continuous input domains, such as numbers and Dates

The mapping is linear in that the output range value y can be expressed as a linear function of the input domain value x.

So we get y = mx + b


d3.time.scale();

D3's time scale is an extension of d3.scale.linear that uses JavaScript Date objects as the domain representation.

Unlike the normal linear scale, domain values are coerced to dates rather than numbers

A scale object, such as that returned by d3.time.scale, is both an object and a function.

That is: you can call the scale like any other function, and the scale has additional methods that change its behavior.


d3.time.scale()
    .domain([date_left, date_right])
    .range ([numb_left, numb_right]);

The Domain of the D3 Time scale takes in dates.

The range of the D3 Time scale takes in numbers.

In this way we are mapping a set of dates to a set of numbers.


ℝ - Real Numbers
LINE 1: Domain (Dates)
----------A---------B---------C---------
LINE 2: Range (Numbers)
----------D---------E---------F---------

The Domain is Line 1

The Range is Line 2

The Range is thus the result of the y=mx+b transformation from Line 1 to Line 2

The reason this works for dates to numbers is that when dates are used in D3 Quantitative Scales,

The dates are implicitly coerced to numbers representing the number of milliseconds

Which means Line 1 is the number of milliseconds

And line 2 is a set of numbers.


Let's check out some examples in the JavaScript Console.


First we take a look at how the d3.time.scale can transform data from 10 days to 10 numbers.

var timeScale1 = d3.time.scale()
    .domain([new Date(2020,0,1),new Date(2020,0,11)])
    .range([0,10]);

In this case, we want to transform the first 10 1 day intervals of January 2020

To a line of numbers from 0 to 10.

That is a set of 10 1 unit intervals.


Let's check to see what was assigned to the timeScale1 variable

timeScale1;

typeof(timeScale1);

The timeScale1 command returns the function while the typeOf(timeScale1) command returns the function string.

This means that d3.time.scale() is a function.


Next, let's test the left end points of the domain to range conversion.

timeScale1(new Date(2020,0,1));

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

We told the function to convert the left most point in line 1 to the left most point in line 2.

In this case, we were converting line 1 point January 1st 2020 to line 2 point 0.

So we should expect zero.

BROWSER - PRESS ENTER

As you can see, it returned zero.


Next, let's test the right end points of the domain to range conversion.

timeScale1(new Date(2020,0,11));

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

We told the function to convert the right most point in line 1 to the right most point in line 2.

In this case, we were converting line 1 point January 11th 2020 to line 2 point 10.

So we should expect 10.

BROWSER - PRESS ENTER

As you can see, it returned 10.


Next, let's test the middle points of the domain to range conversion.

[timeScale1(new Date(2020,0,3)), timeScale1(new Date(2020,0,5)), timeScale1(new Date(2020,0,7))];

I put the functions into an array to save space so that we could test a few of them at the same time.

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

So we should expect 2, 4 and 6.

Because we are passing in a date 2 days after January 1st 2020

a date 4 days after January 1st 2020

and a date 6 days after January 1st 2020.

BROWSER - PRESS ENTER

As you can see, it returned 2, 4 and 6.

Well, something incredibly close to 2, 4 and 6.

Why is it off?

It is slightly off because JavaScript treats decimal numbers as 64-bit floating point numbers.

For our purposes, this isn't really important now.


From this we can see that after we define the function with our initial data, we can use it to convert data for later use.

Let's reload the browser and look at how we can scale up the dates to a bigger second number line.


Next, we take a look at how the d3.time.scale can transform data from 10 days to 100 numbers.

var timeScale2 = d3.time.scale()
    .domain([new Date(2020,0,1),new Date(2020,0,11)])
    .range([0,100]);

In this case, we want to transform the first 10 1 day intervals of January 2020

To a line of numbers from 0 to 100.

That is a set of 100 1 unit intervals.

BROWSER - PRESS ENTER


Let's check to see what was assigned to the timeScale2 variable

timeScale2;

typeof(timeScale2);

The timeScale2 command returns the function while the typeOf(timeScale2) command returns the function string.

This means that d3.time.scale() is a function.


Next, let's test the left end points of the domain to range conversion.

timeScale2(new Date(2020,0,1));

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

We told the function to convert the left most point in line 1 to the left most point in line 2.

In this case, we were converting line 1 point January 1st 2020 to line 2 point 0.

So we should expect zero.

BROWSER - PRESS ENTER

As you can see, it returned zero.


Next, let's test the right end points of the domain to range conversion.

timeScale2(new Date(2020,0,11));

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

We told the function to convert the right most point in line 1 to the right most point in line 2.

In this case, we were converting line 1 point January 11th 2020 to line 2 point 100.

So we should expect 100.

BROWSER - PRESS ENTER

As you can see, it returned 100.


Next, let's test the middle points of the domain to range conversion.

[timeScale2(new Date(2020,0,3)), timeScale2(new Date(2020,0,5)), timeScale2(new Date(2020,0,7))];

I put the functions into an array to save space so that we could test a few of them at the same time.

Before we press enter, what do we expect?

The function assumes that you are giving it a Date from the domain.

The function then returns a number in the range.

So we should expect 20, 40 and 60.

Because we are passing in a date 2 days after January 1st 2020

a date 4 days after January 1st 2020

and a date 6 days after January 1st 2020.

BROWSER - PRESS ENTER

As you can see, it returned 20, 40 and 60.


From this we can see that after we define the function with our initial data, we can use it to convert data for later use.

As you can imagine, we can also use the time scale function to shift data to the right or the left

As well as combinations of scaling up or down and shifting the data to the right or the left.


d3.time.scale()
    .domain([date_left, date_right])
    .range ([numb_left, numb_right]);

Again,

The Domain of the D3 Time scale takes in dates.

The range of the D3 Time scale takes in numbers.

In this way we are mapping a set of dates to a set of numbers.


And with that, you can see just how powerful the D3 Time Scale function can be because we can scale one data set to another.

This is incredibly helpful when building Data Visualizations that contain dates and times.

Both for constructing the SVG Viewport as well as for doing the x-axis and/or the y-axis.

<< Back To D3 Screencast and Written Tutorials Index