Creating an Animated Bar Chart Race with Tableau | by Ewe Zi Yi | Towards Data Science

A while ago I posted an animated bar chart on /r/dataisbeautiful on Reddit showing the changes in the number of international students by their country of origin in Australia. Such animated bar charts are perfect for showing the evolution of numerical data corresponding to different categories over a period of time in a very eye-catching (or sometimes annoying) way.
I didn’t quite get around to documenting how it was done half a month ago but I recently decided that it was probably best to document the steps that I had followed and my thought processes somewhere before forgetting them so here’s an article detailing the stuff that went through my head then:
Before I go on, I’d just like to say this article is heavily inspired by a tutorial that I read from Ludovic Tavernier on his blog greatified. I have the utmost respect and admiration for his discovery of this method to simulate animation frames for bar charts with Tableau so kudos to him for all that work!
In fact, I followed his steps closely to create the bar chart that I posted but after going through the tutorial, I thought that it’d probably be useful to refine and elaborate certain parts to make the tutorial more comprehensive and easily-understandable for beginners to Tableau, and to outline the motivation and logic behind the steps in the method involved. All the code in this article (except for some tweaks) were taken from Ludovic’s so once again, thanks a lot to him!
In the step-by-step guide I’ll be showing you how to create your own animated bar chart with Tableau, with my dataset on student numbers as an example. Hopefully, you get to see something moving by the end of the tutorial!
Tableau does not automatically create animated bar charts from a dataset. A quick workaround to this is to make Tableau create individual frames that simulate an animation when you string them together. This works the same way how cartoons are made.
Now to do this you would ideally want to get Tableau to create these frames automatically, which in turn are created from data you provide to Tableau. But unless you really like manually creating datasets, how do you actually create these “pseudo-data” automatically?
This method revolves around doing just that.
You’ll need to create 3 sheets of data (preferably all on the same Excel file) in this step. The goal is to create an even larger dataset containing additional rows such that each additional row represents 1) each animation frame and for 2) each point of the animation bar (more on this below).
Don’t get it? Here’s a simple example: you start off with a table containing 20 entries in the ‘year’ column, numbering from 2001 to 2020. Right now you’d like to keep more information about each month of each year so you create an additional column for ‘month’. And with each year containing 12 months, you need to ensure that each year, say 2001, gets repeated 12 times for each month. As a result, you get 20 x 12 entries. This is what we call a cartesian product.
Going back to what we are supposed to do, we’d like to duplicate entries in our dataset but we would want to do it in a more elegant manner than just creating rows for each entry manually. So we’ll do this by creating new generic entries (or in the case of the previous analogy, for each individual month) for each new column we’d like to introduce, in separate sheets. We then get Tableau to carry out a cartesian join on these sheets automatically.
Your first sheet will contain your original data, whereas the two other sheets will contain the new generic entries.
The first sheet is essentially the main dataset that you’d like to animate. It should contain basic information like year, category, and value. Besides that, you need to create an additional column link, which will be used later to to join data on the other data sheets.
year: a numerical value for the unit of time you’re observing
category: a name for whatever you’ll like to observe like country name
value: a numerical value for the value you are interested in like count
link: an identifier that acts as an anchor to join the various data sheets (use same value throughout all sheets; 1 in this case for simplicity)
This sheet essentially creates the 4 corners of a bar (that’ll appear in the chart) for each category in your dataset. Point 1 represents the point at the top left of a rectangle, with each subsequent point going clockwise and ending with point 4 representing the bottom left.
link: an identifier that acts as an anchor to join the various data sheets (use the same value throughout the entire dataset; 1 in this case for simplicity)
point: a point representing a certain corner of a bar(number them 1–4)
Your second sheet should look exactly like this:
This sheet creates animation frames to animate the time interval between the fixed intervals specified in your dataset. For example, if the smallest time interval in your data set is 1 year, this sheet will create k number of frames to animate the gap between how the bar chart looks like in year n to year n+1.
link: an identifier that acts as an anchor to join the various data sheets (use the same value throughout the entire dataset; 1 in this case for simplicity)
anim: an identifier for each frame created (number them 1 to k, the number of frames you’d like to create for each interval)
Your third sheet should look exactly like this:
Once you have the three sheets, open a new Tableau workbook and select the Excel file that you have created in the previous step. Perform a cartesian join on all the sheets on the link column.
Look back at the new data entries Tableau has created and you should see that each data entry in your original dataset should have been replicated with entries for each point of the bar, and for each animation frame:
Now with out dataset expanded greatly to accommodate more data entries for each animation frame and each animation bar, our goal now is to compute some values for these entries, which will define the sizes and positions of the bars at each particular frame.
Switching over to an empty Tableau sheet, we then proceed to create several calculated fields using the data contained in the columns of our dataset:
anim_inter: a number that contains the ratio current frame/total frame number per interval (eg. 2/10 = 0.2)
[Anim]/{MAX([Anim])}
frame: a number indicating the exact animation frame within a certain interval (eg. year 2001.2)
[Year]+[anim_inter]
value_current: a number indicating the actual value of the bar at the beginning of the interval (eg. 190 at the beginning of the interval between 2001 and 2002)
{FIXED [Year],[Category]:MIN([Value])}
value_next: a number indicating the actual value of the bar the end of the interval (eg. 195 at the end of the interval between 2001 and 2002)
LOOKUP(ATTR([value_current]),1)
value_inter: a number indicating the interpolated value of the bar at an exact animation frame within the interval (eg. 193.2 at the interval 2001.3)
ATTR([value_current])+([value_next]-ATTR([value_current]))*ATTR([anim_inter])
rank_current: a number showing the rank of the bar relative to other bars, based on their value_current at the beginning of the interval
RANK_UNIQUE(ATTR([value_current]),”desc”)
rank_next: a number showing the rank of the bar at the end of the interval
LOOKUP([rank_current],1)
rank_inter: a number showing the ‘intermediate’ rank of the bar at an exact animation frame within the interval
[rank_current]+([rank_next]-[rank_current])*ATTR([anim_inter])
@x_inter: a number indicating the x-value of the specified corner of the bar (i.e. the values for points 1 and 4 should always be 0 as the left side the bar doesn’t move; while those for points 2 and 3 should always indicate the current length of the bar)
IF ATTR([Point])=1 THEN 0ELSEIF ATTR([Point])=2 THEN [value_inter]*1.0ELSEIF ATTR([Point])=3 THEN [value_inter]*1.0ELSEIF ATTR([Point])=4 THEN 0END
@y_inter: a number indicating the y-value of the specified corner of the bar (i.e. the values for points 1 and 2 should always be the same and represent the height of the top edge of the bar; while those for 3 and 4 should always be the same and represent the bottom edge of the bar. Any difference between the y-values indicates the height of the bar)
IF ATTR([Point])=1 THEN [rank_inter]*1.0 ELSEIF ATTR([Point])=2 THEN [rank_inter]*1.0ELSEIF ATTR([Point])=3 THEN [rank_inter]+0.5ELSEIF ATTR([Point])=4 THEN [rank_inter]+0.5END
filter: a number showing the rank of the value_inter of the bar; we’ll use this to filter out all the other bars that are not within the top x number of bars
RANK_UNIQUE([value_inter],”desc”)
label: a string to indicate the category and value_inter of the bar; category gets shown above the point 1 of the bar, while value_inter gets shown above point 3
IF ATTR([Point])=1 THEN ATTR([Category])ELSEIF ATTR([Point])=3 THEN STR(INT([value_inter]/1000))+”K”END
With the nitty-gritty details all worked out, we can now finally focus on the exciting part of this process: visualisation!
Let’s begin by setting up all the shelves and cards on the Tableau worksheet:
Drag @x_inter to the Columns shelf. Modify the table calculationsDrag @y_inter to the Rows shelf, and then repeat the exact same step once again. Set the second @y_inter as a Dual Axis.Right-click on any of the two y-axes on the view pane and select Synchronize Axis. Set the range of the axis to be fixed from 0 to 11, and reverse its scale.Drag frame to the Pages shelf and ensure that it is a discrete dimension.Drag filter to the Filters shelf.Select the Polygon mark under the @y_inter Marks card and add Year, Category, and anim_inter as Details, and lastly Point as a Path. Ensure that these are all added as dimensions.Select the Circle mark under the @y_inter(2) Marks card and add Category and label as Labels, as well as Year, anim_inter and Point as Details. Ensure that these are all added as dimensions.
Drag @x_inter to the Columns shelf. Modify the table calculations
Drag @y_inter to the Rows shelf, and then repeat the exact same step once again. Set the second @y_inter as a Dual Axis.
Right-click on any of the two y-axes on the view pane and select Synchronize Axis. Set the range of the axis to be fixed from 0 to 11, and reverse its scale.
Drag frame to the Pages shelf and ensure that it is a discrete dimension.
Drag filter to the Filters shelf.
Select the Polygon mark under the @y_inter Marks card and add Year, Category, and anim_inter as Details, and lastly Point as a Path. Ensure that these are all added as dimensions.
Select the Circle mark under the @y_inter(2) Marks card and add Category and label as Labels, as well as Year, anim_inter and Point as Details. Ensure that these are all added as dimensions.
At the end of this series of steps, your Tableau worksheet should look something like this:
With all the fields set up, it’s time to configure some table calculations to ensure that the calculated fields we have created earlier function well in the view pane:
Right-click on @x_inter on the Columns shelf to configure table calculations. Compute @x_inter using Year and frame. This makes Tableau calculate @x_inter by Year and frame for every anim_inter, Category and Point*.Right-click on the @y_inter on the Rows shelf to configure table calculations. Set it to compute rank_current using Category, and to compute rank_next using Category, Year and frame, with Restarting every Category selected. Repeat this for both @y_inter axes.Right-click on filter under the Filters card to set table calculations. Compute filter using Category and compute value_next using Year and frame. Specify 0–10 as the range of values.
Right-click on @x_inter on the Columns shelf to configure table calculations. Compute @x_inter using Year and frame. This makes Tableau calculate @x_inter by Year and frame for every anim_inter, Category and Point*.
Right-click on the @y_inter on the Rows shelf to configure table calculations. Set it to compute rank_current using Category, and to compute rank_next using Category, Year and frame, with Restarting every Category selected. Repeat this for both @y_inter axes.
Right-click on filter under the Filters card to set table calculations. Compute filter using Category and compute value_next using Year and frame. Specify 0–10 as the range of values.
*For those of you who don’t really understand what’s going on with table calculations, check out this awesome explanation by Andy Kriebel here.
Voilà! You finally have some semblance of a bar chart after working through so many steps! Click on play (top right of the interface) to see your bar chart move!
However as you can certainly notice, you chart is probably still on the ugly side and there’re still things to do to touch up a little on some aesthetics:
Under the @y_inter(2) Marks card, select Label. Edit the text by clicking on ‘...’. Delete <Category> from the field, and justify <AGG(label)> to the right. Next, change the Alignment of the Label from Automatic to Top Right. Lastly select the checkbox Allow labels to overlap other marks.Click on Size and select the smallest size on the slider. Now you no longer have goofy-looking rectangles!To add some cool colour gradients to your bars, add value_inter to the @y_inter Marks card as Color. Compute value_inter (just as in the previous series of steps) using Year and frame, and lastly select your desired colour gradient.
Under the @y_inter(2) Marks card, select Label. Edit the text by clicking on ‘...’. Delete <Category> from the field, and justify <AGG(label)> to the right. Next, change the Alignment of the Label from Automatic to Top Right. Lastly select the checkbox Allow labels to overlap other marks.
Click on Size and select the smallest size on the slider. Now you no longer have goofy-looking rectangles!
To add some cool colour gradients to your bars, add value_inter to the @y_inter Marks card as Color. Compute value_inter (just as in the previous series of steps) using Year and frame, and lastly select your desired colour gradient.
Those are some steps I believe are necessary to make your chart look presentable at the very least though there are other things you should also try out yourself to make your chart look better — try renaming/hiding your axes, axis titles, and chart titles!
You can also create a Tableau dashboard to feature multiple view panes in addition to the one with the bar chart. One such view pane can feature the year of the animation ticking away as your graph moves.
Let me know if you discover interesting ways to personalise your bar chart!
Last but not least, it’s currently not possible to export your simulated animated bar chart as an image or video directly using Tableau. For those of you who’d like to save your animated chart in another format, consider using the screen recording function on your computer (or on a 3rd party application) to record the animated frames on Tableau. You can then use a free tool on the internet to convert your video file to a .gif like I did if you’d like one. And there you go, a smart looking animated chart!
So that should be it for this tutorial! I think I’ve probably noted down most of my thoughts and the steps I followed to get to a pleasant-looking animated bar chart race, so it’ll probably be easy for me to get back and recreate one in the future should I not touch Tableau again for some time.
Let me know if you have any questions or issues following the tutorial and thanks for reading! Hope you have fun creating your charts!
For those of you who’d like to read a short reflection on the animated bar chart race that I created (the one on Australia right on top) and the huge discussion it generated on the Reddit, check out my other article!