banner



How To Make Animated Bubble Chart For Overtime Data In D3.js

nyt bubble chart

This mail service is very quondam and no longer represents the current state of how to use D3 properly. You should check out my updated Creating Chimera Charts with D3v4 instead!

Update: I moved the code to its ain github repo - to make information technology easier to consume and maintain.

Update #2 I've rewritten this tutorial in straight JavaScript. So if y'all aren't that in to CoffeeScript, check the new one out!

Recently, the New York Times featured a chimera chart of the proposed budget for 2013 past Shan Carter . It features some nice, organic, animations, and smooth transitions that add a lot of visual appeal to the graphic. This was all done using D3.js .

Equally FlowingData commenters signal out , the use of bubbles may or may non be the best mode to display this dataset. Still, the style this visualization draws yous in and gets you to collaborate makes it a squeamish piece and one that makes you wonder how they did it.

In this post, we attempt to tease out some of the details of how this graphic works.

Uncomplicated Animated Chimera Chart

In order to amend understand the budget visualization, I've created a similar chimera chart that displays information about what teaching-based donations the Gates Foundation has made.

You lot can come across the full visualization hither

And the visualization lawmaking is on github

The bubbles are color coded based on grant corporeality (I know the double encoding using size and color isn't very helpful - only this data ready didn't have any quick and easy chiselled fields besides year).

**Warning Coffeescript** The example is written in [CoffeeScript](http://coffeescript.org/) as I find it much easier to read and write than javascript. Everything is packaged upwardly in a prissy `BubbleChart` form. Hopefully this does non get in the mode of you agreement what is going on. The original NYT visualization is in javascript. Let me know if something is confusing.

The data for this visualization comes from the Washington Posts DataPost . I've added a categorization to the amount for each grant (low, medium, high) and pulled out the outset yr, simply otherwise have left the data alone.

The balance of this tutorial will walk through the functionality behind this Gates Foundation visualization. This visualization is just a sketch of the functionality in the New York Times graphic - and then we tin can see the relevant parts clearly.

D3's Strength Layout

The Force Layout component of D3.js is used to swell effect to provide most of the functionality behind the transitions, animations, and collisions in the visualization.

This layout essentially starts upwards a piffling physics simulation in your visualization. Components push and pull at ane another, somewhen settling in to their final positions. Typically, this layout is used for graph visualization . Hither, notwithstanding, we forgo the use of edges and instead use it merely to motility around the nodes of a graph.

This ways we don't really need to know much nigh graph theory to understand how this graphic works. However, we exercise need to know the components that make up a forcefulness layout, and how to use them.

Force Layout Quick Reference

Here are some of the concepts this visualization will use.

nodes

nodes is an array of objects that will be used as the nodes (i.e. bubbles in this graph) in the layout. Each node in the assortment needs a few attributes like x and y.

In this visualization, we ready some of the required attributes manually - and then we are in full command of them. The rest we let D3 set and handle internally.

gravity

In D3's force layout, gravity isn't really a force pushing downwards. Rather, it is a force that can push nodes towards the center of the layout.

The closer to the center a node is, the less of an affect the gravity parameter has on it.

Setting gravity to 0 disables information technology. gravity can also be negative. This gives the nodes a button away from the middle.

friction

Equally the documentation states , maybe a more accurate term for this strength attribute would be velocity disuse.

At each step of the physics simulation (or tick as it is called in D3), node movement is scaled by this friction. The recommended range of friction is 0 to 1, with 0 being no movement and 1 existence no friction.

charge

The charge in a force layout refers to how nodes in the environment button away from one another or attract one some other. Kind of like magnets, nodes accept a charge that tin can be positive (attraction force) or negative (repelling force).

alpha

The alpha is the layout is described as the simulation's cooling gene . I don't know if that makes sense to me or not.

What I do know is that alpha starts at 0.i. After a few hundred ticks, alpha is decreased some amount. This continues until alpha is really pocket-sized (for example 0.005), and and so the simulation ends.

What this ways is that alpha tin can be used to calibration the movement of nodes in the simulation. So at the showtime, nodes will move relatively quickly. When the simulation is well-nigh over, the nodes will but barely be tweaking their positions.

This allows you to damper the furnishings of the forces in your visualization over time. Without taking alpha into business relationship, things like gravity would exert a consistent force on your nodes and pull them all into a dodder.

How This Bubble Chart Works

Ok, we didn't cover everything in the force layout, but we got through all the relevant $.25 for this visualization. Let me know if I messed annihilation upwardly.

At present on to the interesting role!

There are two features of this bubble chart I want to talk nigh in more detail: standoff detection and transitions.

Bubble Collision Detection

When I first saw the NYT nautical chart, I was intrigued by how they could use unlike sized bubbles but not have them overlap or sit on top of one another.

The charge of a forcefulness layout specifies node-node repulsions, so it could exist used to push nodes away from one another, creating this consequence. Merely how can this piece of work with different sizes if charge is merely a single parameter?

The trick is that along with a static value, charge can also take a function, which is evaluated for each node in the layout, passing in that node's data. Here is the charge part for this visualization:

                          charge              :              (              d              )              ->              -              Math              .              pow              (              d              .              radius              ,              2.0              )              /              eight                      

Pretty simple right? But, for me at least, it was unclear from the charge documentation , that this office based arroyo was possible.

Lets break it down. Over again this is coffeescript, so what we are seeing is a method called accuse in my BubbleChart class. This method takes in an input parameter, d, which is the information associated with a node. The method returns the negative of the diameter of the node, divided past viii.

The negative is for pushing away nodes. Dividing by eight scales the repulsion to an advisable scale for the size of the visualization.

A couple of other factors are at work that also contribute to the nice looking placement of these nodes.

Here is the code that is used to configure and startup the force directed simulation:

                          display_group_all              :              ()              =>              @              forcefulness              .              gravity              (              -              0.01              )              .              charge              (              this              .              charge              )              .              friction              (              0.nine              )              .              on              "tick"              ,              (              e              )              =>              @              circles              .              each              (              this              .              move_towards_center              (              due east              .              alpha              ))              .              attr              (              "cx"              ,              (              d              )              ->              d              .              x              )              .              attr              (              "cy"              ,              (              d              )              ->              d              .              y              )              @              force              .              start              ()                      

So @force is an instance variable of BubbleChart holding the force layout for the visualization. Y'all tin can see the use of the charge method for the layout's charge input. Nosotros set its gravity to -0.01 and its friction value to 0.9 (which is the default). This says that nodes should exist always then slightly pushed away from the center of the layout, but in that location should be just enough friction to prevent them from scattering abroad.

We will get to the tick function in the next department.

So, nodes push away from one another based on their diameter - providing nice looking standoff detection. The gravity and friction settings work along with this pushing to ensure nodes are sucked together or pushed abroad too far. Its a prissy combo.

Blitheness and Transitions

The original graphic has some nice transitions between views of the data, where bubbles are pulled autonomously into split groups. I've replicated this somewhat by having a view that divides up Gate's grants by year.

How is this washed? Well, lets get-go with the all-together view first. The position of each node is adamant past the function called for each tick of the simulation. This part gets passed in the tick event, which includes the alpha for this iteration of the simulation.

Nosotros can see this function in the code segment above. I'll highlight it once more hither:

                          # ...              .              on              "tick"              ,              (              e              )              =>              @              circles              .              each              (              this              .              move_towards_center              (              e              .              blastoff              ))              .              attr              (              "cx"              ,              (              d              )              ->              d              .              x              )              .              attr              (              "cy"              ,              (              d              )              ->              d              .              y              )                      

The @circles instance variable holds the svg circles that represent each node. So what this code does is for every tick upshot, for each circle in @circles, the move_towards_center method is called, with the current alpha value passed in. And then, The cx and cy of each circle is set up based on information technology's data's x and y values.

And then, move_towards_center must be doing something with the information'south 10 and y values to get things to move. And indeed it is:

                          motion              *              towards_center              :              (              alpha              )              =>              (              d              )              =>              d              .              x              =              d              .              x              +              (              @              center              .              10              -              d              .              10              )              *              (              @              damper              +              0.02              )              _              alpha              d              .              y              =              d              .              y              +              (              @              eye              .              y              -              d              .              y              )              _              (              @              damper              +              0.02              )              \              _              alpha                      

So move_towards_center returns a office that is chosen for each circle, passing in its data. Within this function, the x and y values of the information are pushed towards the @center point (which is prepare to the middle of the visualization). This push towards the middle is dampened by a abiding, 0.02 + @damper and alpha.

The blastoff dampening allows the push towards the center to be reduced over the course of the simulation, giving other forces like gravity and accuse the opportunity to push back.

The variable @damper is ready to 0.ane. This probably took some time to find a proficient value for.

Ok, we've now seen how the nodes in the simulation move towards one signal, what about multiple locations? The code is only near the aforementioned:

                          display_by_year              :              ()              =>              @              force              .              gravity              (              @              layout_gravity              )              .              charge              (              this              .              charge              )              .              friction              (              0.9              )              .              on              "tick"              ,              (              due east              )              =>              @              circles              .              each              (              this              .              move_towards_year              (              e              .              alpha              ))              .              attr              (              "cx"              ,              (              d              )              ->              d              .              x              )              .              attr              (              "cy"              ,              (              d              )              ->              d              .              y              )              @              force              .              outset              ()              move              *              towards_year              :              (              alpha              )              =>              (              d              )              =>              target              =              @              year_centers              [              d              .              year              ]              d              .              ten              =              d              .              ten              +              (              target              .              x              -              d              .              x              )              *              (              @              damper              +              0.02              )              _              alpha              _              i.one              d              .              y              =              d              .              y              +              (              target              .              y              -              d              .              y              )              _              (              @              damper              +              0.02              )              _              alpha              \              _              1.i                      

The switch to displaying past twelvemonth is done past restarting the force simulation. This time the tick function calls move_towards_year. Otherwise it's about the same as display_group_all.

move_towards_year is almost the aforementioned every bit move_towards_center. The difference being that outset the correct year signal is extracted from @year_centers. Here'due south what that variable looks like:

                          @              year_centers              =              {              "2008"              :              {              x              :              @              width              / three, y: @height /              2              },              "2009"              :              {              10              :              @              width              / 2, y: @summit /              two              },              "2010"              :              {              x              :              2              \              *              @              width              / 3, y: @top /              2              }              }                      

You can come across this is but an associative array where each yr has its ain location to motility towards.

move_towards_year also multiplies past 1.1 to speed up the transition a scrap. Again, these numbers take some tweaking and experimentation to detect. The code could be simplified a bit if you wanted to laissez passer in the unique multipliers for each transition.

So we've seen a general design for both of these views: setup the tick method to iterate over all the nodes to change their locations. This was done using the each method.

In fact you can chain multiple each methods that call different move functions. This would allow you to push and pull your nodes in various ways depending on their features. The NYT graphic uses at least ii motion functions for these transitions. One to movement nodes towards a group point (like to a higher place), and one to move nodes towards their respective colour band.

Hope this tutorial shows you some interesting ways to use D3's force layout. I thought the original NYT visualization was interesting enough to find out more virtually it. Over again, the full visualization code can be found on github. Information technology is but over 200 lines of code - including comments.

Forcefulness layouts: they are not just for force-directed graphs anymore!

Source: https://vallandingham.me/bubble_charts_in_d3.html

Posted by: harvardwithereas1986.blogspot.com

0 Response to "How To Make Animated Bubble Chart For Overtime Data In D3.js"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel