There is no denying the power of d3. You only have to look as far as the examples on their website to be overwhelmed by its flexibility and aesthetics.
d3-scale itself is an incredible library that can help us reduce code and normalise data to fit a particular domain and range for use anywhere in our code. Allow me to demonstrate this using three examples that make use of
Before we get started
This blog post takes a few liberties, so I am assuming you are familiar with ES6 syntax and that you have setup an environment that supports ES6+. If you are ready, take a few deep breaths, strap yourself in and run
npm i —save d3-scale.
Example 1: Embracing classification
Classification is used to categorise data. You’ve likely done it many times before. In the wild, I’ve recently used classification in code to designate status within a loyalty program, identify eligibility for bank loans and define a person’s tax bracket given a certain income.
Since relating to an example is important for learning uptake, I figure what could be more relatable than classifying school grades?
Putting on our thinking hats, we need to take the context of the situation to define what the domain and range will look like. For this example, we will assume that 50% means a pass, 65% a credit, 75% a distinction and 85% a high distinction - anything below means another semester at the school of hard knocks. This means that our domain should be an array that includes all the data values that correlate to its corresponding grade classification.
Knowing this, let’s begin by importing the
scaleThreshold function from d3-scale and setting up two basic arrays.
You may have noticed that our range has one more element than what is specified in the domain, why is that? As taken from the [online documentation](GitHub - d3/d3-scale: Encodings that map abstract data to visual representation.: "If the number of values in the scale’s domain is N, the number of values in the scale’s range must be N+1".
Now that we have defined our domain and range arrays with their corresponding breakpoints, let’s look at how we implement the function and create our scale.
Given the beauty of d3, setting up a domain and range will follow a similar pattern for each implementation of a scaling function.
But what actually happened? We just created a function variable that can be used to map an integer onto a range classification. Observe and see the magic unfold.
If you want to see the full scale in action, run the following.
Now is the time to classify all the things! What’s that? You know an alternative? You want to use a switch?
thScaleRamp(107) are still valid using a threshold scale. While other d3 scales have a
.clamp() method (you will see later),
scaleThreshold does not. If you cannot ensure that the data from the source won’t fall outside of the domain, clean the data before running it through the scale.
Example 2: Embracing polylinearity
I may have flexed my writer’s license and made up a word. My quick Google search didn’t confirm its existence. Worry not - the power of adding prefixes to existing words will be justified! Instead of defining polylinearity now, let’s explore some examples to demonstrate what a polylinear scale is.
Let’s begin small: scaling linearly. Assume we have started a company and to complete phase one of our funding, we are looking at a target of raising \$5000.
Assuming we wish to generate a simple progress report that will take some data within a domain, scale it and output it within a basic range, we could use the
scaleLinear() function, take what we know from our previous example and conjure this up.
What do you mean you’re not impressed? I can hear it now. "You can do just some basic math if you want to scale it as a percentage."
Alas, you are correct so far. Let’s create a random data array this time and see what happens.
Well fine, you’re not impressed... but what happens if we want to see our progress across four rounds of fundraisers?
Let’s define that we consider phase one completed after raising our capital to $5000, phase two completed after reaching $50000, three at $500000 and the final phase at $5000000. How can represent the total progress completion along the phases combined in the one report?
Well, why not just adjust our domain on the current scale?
As you can see, completing phase one only equates to 0.1% completion. Maybe this isn’t the best report we want to reflect our four phase business plan. Enter polylinear scaling.
Let’s create a scale now that denotes all of the important phase data points.
With the domain and range arrays that we have used, we are telling d3 to map anything between 0 - 5000 to a value between 0 - 25, anything between 5000 - 50000 mapped to 25 - 50 and so on.
Let’s generate some data and log out the results!
That is definitely a better representation of progress for where we sit along our four phase business plan! Included in the above data set are both 5000 and 50000 to demonstrate that hitting those figures corresponds to a completion point at 25% and 50% - meaning that both phase one and phase one of the four total phases have reached completion!
Example 3: Embracing the space-time continuum
Maybe not that far. That being said, showing dates along a timeline using d3-scale itself doesn’t need to sound so daunting.
Let’s keep running with the idea of a timeline. For this example, we want to show a timeline of our raging Fortune 500 company, "Llama with Hats". Correct. Singular Llama. Many hats.
Let’s presume our company started on September 14th, 2013 and the latest major milestone we want to include is the release of our incredible tech cross-species innovation the Llama Phone on December 7th ,2018 (don’t take my idea).
In between, we also celebrated a few other key achievements as assigned to our
Given the current format of the data given to use, we can make use of Array’s prototype function
.map() to create the
dates array that we actually want.
This time, the scaling function we are looking for our newest friend
scaleTime. By this stage of the tutorial, you could probably guess how we assign our scaling variable.
Now that we have our scaling variable, all that is left to do is run the elements of our
dates array through it!
And there you have it. Mapping points along a progress bar was once daunting, yet is now be a feeble percentage we can use to displace our marker in along the range.
Note: Setting your domain using the above is a really bad idea and is used simply for demonstration purposes. Another d3 library (d3-array)[GitHub - d3/d3-array: Array manipulation, ordering, searching, summarizing, etc. has an excellent array function
extent that returns the min and max of an array as an array and can passed as the argument for
domain() . I’ve passed on it given I wish to focus only on
d3-scale in this post.
scaleTime() are but a small part of the d3 ecosystem. Given the malleability of the
d3-scale module, it allows those who are unfamiliar with d3 the ability to still utilise these wonderful functions in abstract, useful ways with our own code.
1,200+ PEOPLE ALREADY JOINED ❤️️
Get fresh posts + news direct to your inbox.
No spam. We only send you relevant content.