Hexbins in Tableau
One of the features of Tableau v9 is it’s new Hexbin functions – hexbinX() and hexbinY() – in this post we explore how to use them.
Hexbins are, in short, a simple way of grouping two sets of numeric values into groups of similar values for purposes of visualisation. Why Hexagons? Well they tessellate nicely and don’t suffer from the same problems as grid squares which have corner points a long way from the grid center.
The Hexbin functions in Tableau simply provide a straight-forward translation of your field values to the centre of the nearest hexagon, allowing these values to then be used in visualisation, e.g. instead of this:
[tableau url=”http://public.tableau.com/profile/chrisluv#!/vizhome/HexbinExample/Scatter” width=”600px” height=”700px”][/tableau]
The spread of the results is easier to see using hexagons:
[tableau url=”http://public.tableau.com/profile/chrisluv#!/vizhome/HexbinExample/Hexbin” width=”600px” height=”700px”][/tableau]
Basically the creation of these Hexbins comes down to two new functions in Tableau v9: HexbinX and HexbinY these functions simply calculate the values of the centroid of each hexagon – allowing you to use Shape marks to display the actual hexagons. e.g for the above visualisation I have created two calculated fields:
[hexbinX] = HexbixX(X,Y)
[hexbinY] = HexbixY(X,Y)
This will return the X and Y values of the Hexagons, and I then used Hexagon Shapes (downloaded from Kristopher Erikson’s excellent post on the same subject – see bottom of his post here) dropped into my Tableau Shapes Repository (instructions here).
You’ll notice that the example above is very simple as the Scale is simply one Hexagon width = one value on each axis – which suits perfectly what the Tableay functions expect. More often that not though you won’t have data this clean, what to do? The Tableau help is less that helpful here: “The bins have side length 1, so the inputs may need to be scaled appropriately.”
I’ve taken a slightly different approach to Kristopher in my scaling in that I tend to create two parameters [ScaleX] and [ScaleY] and use them as follows:
[hexbinX] = HexbixX(X*[ScaleX],Y*[ScaleY])/[ScaleX]
[hexbinY] = HexbixY(X*[ScaleX],Y*[ScaleY])/[ScaleY]
Here the aim of [ScaleX] and [ScaleY] are simply to move the X and Y values to an axis with units of 1 (by multiplying each value by the Scale factor), then moving the corresponding point back to the original axis by dividing by the Scale factor.
e.g. if you have values ranging from 1,000 to 10,000 and want 10 hexagons across the range, you would use use a Scale of 1/1000, this would first change the 1,000 and 10,000 to values to 1 to 10 for use in the functions – as Tableau requires – then back again to the original axis for display.
It really is that simple, but first some gotchas to be aware of:
- you’ll have to play with the size of the shape marks (using the Size slider on the marks card) to get them spaced just right on the page
- as you change the size of your visualisation the size of your marks will need to change again
- ensure your axes are appropriate, it may be necessary to fix them to ensure they are square or the hexagons will look squashed in one direction
The Good: Hexbins can make a great mapping visualisation tool if used correctly and sparingly and locally as they allow data to be shown not in arbitary polygons, e.g. State, County. etc but in areas of known size.
Before embarking on a Hexbin mapping visualisation please please be aware though that using these functions in Tableau will skew your results and potentially mislead /confuse your audience for two reasons:
THE BAD: Firstly Tableau’s Hexbin functions do not account for the curvature of the earth, so longitudes in more Northern latitudes (where the distance of 1 degree of longitude is much smaller than at the equator) will suffer from inaccurancies using these functions.
THE UGLY: Secondly since Tableau uses the Mercator projection the values from more northerly latitudes will stretch compared to these in the south to give an uneven tessellation.
To illustrate the latter point take a look at the below visualisation showing the Hexbins Postcodes in the Uk fall into when scaled 1 to 1 with Latitude / Longitude. Imagine if this was being shown with values from other countries near the equator, whose hexagons where much more regular… so please be aware
Fixing Scaling issues for the UK, I’ve modified my scaling functions for mapping using two new parameters:
[hexbinX] = HexbixX(X*[Zoom],Y*[Zoom]*[Ratio])/[Zoom] [hexbinY] = HexbixY(X*[Zoom],Y*[Zoom]*[Ratio])/([Zoom]*[Ratio])
In the UK a Ratio of 1.61 appears to give a pleasant grid of hexagons – NB here I am sacrificing some data accuracy for looks, note that the length of the hexagons vertically and horizontally may not be the same, thought they look it due to the projection. As with all thing mapping related this is due to trying to display a curved earth on a flat screen.
NB the Hexbin functions will not work with generated Latitude / Longitude values from Tableau Geocoding, the values need to be present in the data.
Finally below I present a use case for these functions. I have take two open data sources, one for Crime data from Police.uk (5.6 million points) the other from the ONS using their Codepoint dataset of postcodes (2.5 million points).
I used the latitude and longitudes in each set to bin the data into Hexagons as above – I used a Zoom parameter of 10 to create a dense set of hexagon grids.
I then blended the data on the Hexagon centroids (X / Y) to join the two populations together and visualise crime per 1k of Population per grid. Feel free to deconstruct the workbook to find the details, I’m pleased with the performance given the size of the data.
Issues: One issue is that the bins near the coast may contain crimes but due to the locations of postcodes and the way hexagons fall there may be crimes without population, these skew the results and so I have removed populations under a certain threshold.
[tableau server=”public.tableau.com” workbook=”HexbinMappingExample” view=”Crimesper1kPop” tabs=”” toolbar=”” revert=”” refresh=”” width=”600px” height=”1100px”]