We migrated this website to a new platform, and are working to correct formatting errors in older blog posts as a result. If you encounter an error, please send an email to scholarslab@virginia.edu. Thanks!

Track-n-Treat

Halloween is great. Free candy. And I have six kids to go out and get it for me. :)

I cull some of the finest chocolates from their bags after trick-or-treating and enjoy them throughout the next week. We usually eat everything within a week…

This year I decided to track how much candy I ate, and which ones, in the week after Halloween. It was only because we needed to do something like this for Praxis. Otherwise I eat candy without a second thought.

Getting started with d3

D3 is a Decent tool for visualizing Data, hence the name Data-Driven Documents. It is basically a JavaScript framework for making charts, graphs, maps, or anything you can images, based on data from a file, database, etc.

The best way to learn d3 is to practice it over and over. I suggest looking at one or both of these d3 tutorials first.

Making the chart

This post is an expanded version of the actual code files which contain lots of comments.

A note about data

How you structure the data is actually pretty important. It can make it super easy or super hard to get at the information you want. The most simple method usually is the best way to start.

In my case, every time I ate a piece of candy, I took a note of what day it was, what time, and which candy. Something like this:

day: 2015-11-01, time: 11:15, candy: Snickers

As it so happens, this is a great format. Each day on it’s own line. The data gets a little redundant, the day and time information are repeated many times, but it makes it super easy to read and we can easily manipulate it later with d3.

For d3 to use this, we have several options. CSV and JSON are the easiest to work with, so I’ll pick one of them.

This is an array (designated with square brackets []) of objects (designated with curly braces {}). The object consists of names (day, time, candy) and their associated values. If we assign the above to the variable ‘array’, we can access the first element of the array with array[0]. That would return the first line above.

A good tutorial on JavaScript arrays is at Lynda.com (free subscription through the library). Search for ‘Introducing the JavaScript Language with Joe Chellman’.</blockquote>

Diving into D3

The rest of the code is written in JavaScript. The JavaScript can live anywhere in the code: in between the <header> and</header> tags, or anywhere in between the <body> and </body> tags. I just put it after the above code after the <body>tag.

To have JavaScript in the body of the HTML document, we’ll surround it with <script> tags like so:

This function surrounds all of the d3 code that makes the graph. Supply the path (relative to this html file) and a variable name for the data (within the function’s parenthesis) Here we pull in the data from a separate file. d3 calls the main d3 method, .json calls d3’s json method that takes care of loading all of the data from the json file. The json function needs a file path (data/track-n-treat.json) that is relative to where this file is, and a function that creates an internal variable/object to hold the data.

So now all of the data from the json file is available as a variable, in our case it is named ‘data’. (We could change that to anything we want.)

Axes

D3 can take care of a lot of the functionality of creating and placing the x and y axes. D3 puts the axis in the right spot, puts the tick marks on, spaces them appropriately, and labels the tick marks.

There are two parts to creating an axis. First, create a scale. Second, apply the scale to the axis.

Let’s start by making the x axis.

The X-Axis (scale)

We want the x axis to show the days.

We’ll use a time scale, since we’re plotting days. We’ll first set ‘x’ to be a function that converts dates that we plug into it, into pixels on the screen. Think of this as a range or scale converter.

The domain represents the minimum and maximum values that exist in the data. The range is the minimum and maximum values as represented on the web page. This will basically be a date to pixel converter.

D3’s time.scale function takes a ‘domain’ and a ‘range’, which both take a minimum and maximum value. We plug the minimum and maximum dates into the domain section and we set the pixel limits in the range section.

The minimum date is the first day I ate candy, the maximum date is the last day I ate candy. To calculate the first day, we can get the date from the data array: ‘data[0].day’ corresponds with the first element in the ‘data’ array (which is an object), and the value of that objects ‘day’ key.

Since the json file is assigned to the ‘data’ variable, we can get the first ‘day’ by accessing data[0].day. We put that in the default JavaScript Date function to return the date as a String.

Getting the last day is similar, we can also get the last day from the data in the json file. We just need to get the last object element in the array, and get the value of the day element. But how do we specify which is the last element in the array if we don’t know how many elements there are? We could count, but what if we change the data?

We can do a little math to calculate the last element in the array. The JavaScript builtin .length method gives us how many elements are in an array. Since array elements begin counting at 0, we just get the length, number of elements, minus 1 to give us the index of the last element. We can then we put that into d3’s time.day.offset function which adds or subracts a given amount of days from the day that you input. In our case we’ll offset by one day, so that the axis goes from the first day until the day after the last day that I ate candy.

The range is basically the width we specify above, but subtract some of the padding. So the range would be from 0 to 620 (700 - 40 - 40).

Again, this can all be written out on one line, but we separate each chained method onto its own line to make the code more legible.

We plug in the scale created above using the .scale method, and we assign an orientation using the .orient method.

.ticks sets the ticks or marks on the x axis. d3.time.days sets a range of days within the dates used in the scale (above). ‘1’ means show each day in that range (a ‘2’ would show every other day in the range).

.tickFormat sets the format for the tick to be a date using the d3.time.format function in the form ‘DDD ##’.

Y-Axis (scale)

The y-axis represents how many candies eaten each day. The height of this axis is determined by the maximum number of candies eaten in a single day. A few times I ate multiple candies at the same time. We’ll need to get the number of candies for each time period in a day and add them all up. This will determine the max height of the y axis.

We’ll use the d3 nest function which manipulates the data array. The key function pulls out all of the separate days as a key, the value is all of the times that are associated with that day.

We can then use the rollup function to turn the values into something else. In this case it returns the length of the values array, which is the number of candies eaten that day.

The d3.max function looks at an array and returns the highest value. It takes an array and a function. The array is the ‘timesPerDay’ array we created above. The function allows us to specify which part of the array to count. Here the v stands for the array of day objects, and v.values is the ‘values’ element within each day object, which holds the number of times a candy was eaten for that day. So d3.max is now just looking at the different ‘values’ fields and returns the highest number.

Since we’re just using numbers (not dates), we use a regular linear scale for the y axis. We use the maximum number of candy in a day as the maximum for the domain.

Most of this is self-explanatory. The .scale calls the y scale we made above. The .orient sets the axis on the left hand side. .tickPadding determines the space between the tick marks. We set all of this to the variable name ‘yAxis’.

We’ll use the variables ‘xAxis’ and ‘yAxis’ later in the code.

Put it all together

The main SVG element

This is where the magic happens. First, we create a variable/object for the svg elements to live under, because we want to add to it later. d3 calls the main d3 method. select specifies which part of the DOM we want to target. We’re going to target an HTML div tag with the id ‘graph’.

This targets, or selects, the HTML tag with the id of ‘graph’. We then append an svg tag to the div tag, give it a class of ‘graph’, and set the width and height.

Then we append an svg group element g where the chart will reside. We’re going to offset this group to create some padding where the axes will go. To better understand the transform and translate functions, take a minute to read through the section on ‘SVG Transform as a Coordinate Space Transformation’.

The data we use in the data method is the timesPerDay array created above. The .enter method represents the elements that will be appended to the svg group. The elements that we will append are rectangles, rect. Basically, this allows us to loop through the array and places the bar on the graph depending on the x and y positions.

The x position for the bar uses the x function created above, the ‘date’ and ‘14’ for some padding. We give it the date, because the x scaler that we created above will turn that into a pixel within the range that we can use.

The y axis starts from the top of the screen, so we set the y location of the bar to start at the highest value of the height (so it starts at the bottom of the screen), then subract from that value to move the bar up the screen. We move it up the height of the axis minus the number of candies that day. The height is the workingHeight minus the number of candies.