Treemaps in Tableau? can be done.
Tableau can do many things natively but there are a couple of basic primitives that are not built in because they behave somewhat differently from the overall logic. And treemaps is one of them. Then again treemaps are arguably one of the best way to express complex hierarchical information, i.e. to show the proportions in a large dataset.
Fortunately, thanks to Tableau flexibility there are ways to do that. In the tutorial I'm going to cover 2 cases. First, we'll create a somewhat complex treemap off data which will not change in runtime. Then, we'll create mini-treemaps which can change dynamically.
I'm creating 4 lines per "leaf" node. So in this example which has 220 nodes, that amounts to 880 lines. Why 4? Because to draw a rectangle in Tableau you really need to define 4 corners. This is why there is a column "Corner" which is worth 0,1,2 and 3. This, we will use to tell Tableau to read our corners in bottom left, bottom right, top right, top left order which produces a nice convex rectangle and not a concave hourglass shape.
Now off to Tableau with this data.
Now it's just a matter of doing like this screen. Unsurprisingly the columns and rows are going to be determined by x and y. You want a polygon mark, and you absolutely must use your corner measure in the path. For color, you'll have a choice, you can use the top-level category column (as I have) or the full path which will divide your treemap in finer parts. Finally, level of detail: you must use the Id and not the name in case several of your nodes have the same name. It's quite important at this point to uncheck aggregate measures in Analysis. You do NOT want aggregate measures (though it's quite pretty). To be able to use the name, you must first make a measure out of it. And finally, you'll want to update your infotip slightly.
All of this you can see if you download the tableau file.
And voilà! Treemaps for your Tableau workbooks.
Caveat: the polygon mark doesn't support labels so you can't write on top of the small rectangles what they are but that's not the point of the treemap, which is instead to give an immediate first impression of the relative size of large groups of your data, then allow you to explore them, to that end the infotip function works just fine.
There are just 5 rectangles. But they will change for any representative that we choose. Can this be done with Tableau? Obviously.
Now the Tableau part of this is slightly trickier than above. The idea is that we are going to use formulas to generate the coordinates of all 20 corners of the rectangles, in other words we are going to let Tableau calculate the layout. We can do it because the way that rectangles are going to be arranged is quite predictible. There is one on the left, then 4 stacked on the right one on top of the other. Again, we could compute all of these coordinates outside of Tableau but that would be a hassle and so for a large number of cases it becomes easier and more reliable to do this inside of Tableau.
A complex treemap
Before we go in the details the main ideas are deceptively simple.- we use the polygon mark,
- we generate the treemap layout outside of tableau.
| Id | Path | Top-level category | Name | Value | Corner | x | y |
| 0 | flare>analytics>cluster | flare | AgglomerativeCluster | 3938 | 0 | 89 | 167 |
| 0 | flare>analytics>cluster | flare | AgglomerativeCluster | 3938 | 1 | 167 | 167 |
| 0 | flare>analytics>cluster | flare | AgglomerativeCluster | 3938 | 2 | 167 | 192 |
| 0 | flare>analytics>cluster | flare | AgglomerativeCluster | 3938 | 3 | 89 | 192 |
| 1 | flare>analytics>cluster | flare | CommunityStructure | 3812 | 0 | 102 | 138 |
| 1 | flare>analytics>cluster | flare | CommunityStructure | 3812 | 1 | 167 | 138 |
| 1 | flare>analytics>cluster | flare | CommunityStructure | 3812 | 2 | 167 | 167 |
| 1 | flare>analytics>cluster | flare | CommunityStructure | 3812 | 3 | 102 | 167 |
| 2 | flare>analytics>cluster | flare | HierarchicalCluster | 6714 | 0 | 89 | 192 |
| 2 | flare>analytics>cluster | flare | HierarchicalCluster | 6714 | 1 | 167 | 192 |
| 2 | flare>analytics>cluster | flare | HierarchicalCluster | 6714 | 2 | 167 | 236 |
| 2 | flare>analytics>cluster | flare | HierarchicalCluster | 6714 | 3 | 89 | 236 |
Now it's just a matter of doing like this screen. Unsurprisingly the columns and rows are going to be determined by x and y. You want a polygon mark, and you absolutely must use your corner measure in the path. For color, you'll have a choice, you can use the top-level category column (as I have) or the full path which will divide your treemap in finer parts. Finally, level of detail: you must use the Id and not the name in case several of your nodes have the same name. It's quite important at this point to uncheck aggregate measures in Analysis. You do NOT want aggregate measures (though it's quite pretty). To be able to use the name, you must first make a measure out of it. And finally, you'll want to update your infotip slightly.
All of this you can see if you download the tableau file.
And voilà! Treemaps for your Tableau workbooks.
Caveat: the polygon mark doesn't support labels so you can't write on top of the small rectangles what they are but that's not the point of the treemap, which is instead to give an immediate first impression of the relative size of large groups of your data, then allow you to explore them, to that end the infotip function works just fine.
Simpler but dynamic treemaps
This is fine and dandy if your data doesn't change but it won't scale if you need to make many treemaps based on selections. What to do? You could use pie charts, but let's not. To that end I've tried to emulate the Congress speaks visualization by Periscopic. I really like it. When you've selected representatives at the end of the process you are taken to a screen which shows the following mini-treemap:
There are just 5 rectangles. But they will change for any representative that we choose. Can this be done with Tableau? Obviously.
Now the Tableau part of this is slightly trickier than above. The idea is that we are going to use formulas to generate the coordinates of all 20 corners of the rectangles, in other words we are going to let Tableau calculate the layout. We can do it because the way that rectangles are going to be arranged is quite predictible. There is one on the left, then 4 stacked on the right one on top of the other. Again, we could compute all of these coordinates outside of Tableau but that would be a hassle and so for a large number of cases it becomes easier and more reliable to do this inside of Tableau.
Data
For this I have used completely random data. I have generated 20 names, and for each I have generated 5 values in a likely range, number of possible votes, number of votes the representative actually voted, number of times they voted yes, number of times they voted yes with their party, and the same for no. (or nay, technically). At the end of the day I need 20 records per representative (5 rectangles of 4 corners each), so I can either replicate the line 20 times, or use linked tables. The idea is to get something like this for all of the representatives that can somehow get into Tableau.| Id | representative | corner | rectangle | possible votes | total votes | voted yes | yes with party | voted no | no with party |
| 16 | Nelson Thiede | 0 | no against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 1 | no against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 2 | no against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 3 | no against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 0 | no vote | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 1 | no vote | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 2 | no vote | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 3 | no vote | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 0 | no with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 1 | no with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 2 | no with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 3 | no with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 0 | yes against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 1 | yes against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 2 | yes against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 3 | yes against party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 0 | yes with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 1 | yes with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 2 | yes with party | 888 | 784 | 320 | 274 | 464 | 373 |
| 16 | Nelson Thiede | 3 | yes with party | 888 | 784 | 320 | 274 | 464 | 373 |
In Tableau
In Tableau we are going to use the same idea as above: polygon mark, disable aggregate measures, and use x and y for columns and rows. Only, x and y are going to be much more complex. Sorry about that. Well, not that complex but definitely longer. Here's x:case [rectangle] when "no vote" then case [corner] when 0 then 0 when 1 then (([possible votes]-[total votes])/[possible votes]) when 2 then (([possible votes]-[total votes])/[possible votes]) when 3 then 0 end else case [corner] when 0 then (([possible votes]-[total votes])/[possible votes]) when 1 then 1 when 2 then 1 when 3 then (([possible votes]-[total votes])/[possible votes]) end endDepending on the rectangle we are trying to draw we can find ourselves in one of two cases (hence the use of case). If we draw "no vote" then we are on the left of our vis. The left corners are on the leftmost side of the vis (hence value: 0) and the right corners correspond to the proportion of possible votes which where not cast by this representative, which we can compute as ([possible votes]-[total votes])/[possible votes]. In the other case, we are drawing one of the 4 stacked rectangles, so the right corners are on the rightmost side of the vis (hence value: 1) and the left corners correspond to the value we just computed. And now, y:
case [rectangle] when "no vote" then case [corner] when 0 then 0 when 1 then 0 when 2 then 1 when 3 then 1 end when "yes against party" then case [corner] when 0 then 0 when 1 then 0 when 2 then (([voted yes]-[yes with party])/[total votes]) when 3 then (([voted yes]-[yes with party])/[total votes]) end when "yes with party" then case [corner] when 0 then (([voted yes]-[yes with party])/[total votes]) when 1 then (([voted yes]-[yes with party])/[total votes]) when 2 then ((2*[voted yes]-[yes with party])/[total votes]) when 3 then ((2*[voted yes]-[yes with party])/[total votes]) end when "no with party" then case [corner] when 0 then ((2*[voted yes]-[yes with party])/[total votes]) when 1 then ((2*[voted yes]-[yes with party])/[total votes]) when 2 then ((2*[voted yes]+[no with party]-[yes with party])/[total votes]) when 3 then ((2*[voted yes]+[no with party]-[yes with party])/[total votes]) end when "no against party" then case [corner] when 0 then ((2*[voted yes]+[no with party]-[yes with party])/[total votes]) when 1 then ((2*[voted yes]+[no with party]-[yes with party])/[total votes]) when 2 then 1 when 3 then 1 end endy is longer but this is the same general idea. For the "no vote" rectangle, the corners are either to the top or bottom of the vis. But for the other, we can predict where the rectangle will start and when it will end, as a proportion of the [possible votes] field. The values we want are going to be correspond to these proportions, plus that of all the rectangles below so we can achieve that stacked effect (as opposed to have all rectangles superimposed at the bottom of the vis). This is why I am entering the rectangles in stacking order. Each time, the bottom corners get the value of the top corners of the previous rectangle. Here is the final result:
Comments
Comment from Courtney Smith
Time May 16, 2012 at 7:08 am
I have been trying to figure this out. I would like to see if this can be applied to a more complex set of shapes. Thank you!
Comment from victor blær
Time June 6, 2012 at 1:38 pm
dude, that´s just ridiculous… d3 and tableau…stay frosty!
Comment from Ryan Robitaille
Time April 19, 2012 at 5:15 pm
Very nice work, Jerome. I love it. I’m going to have to try this out myself. Cheers!