Waffle charts with svg images
Earlier this month I saw a couple of waffle charts on Twitter used as a nice alternative to showing counts and proportions with bar graphs.
Bar charts are easy and precise. But they're boring as hell. If you need your chart to stand out, that's bad.
— Albert Rapp (@rappa753) September 6, 2022
Waffle charts can be an eye-catching alternative. Especially, if you use icons. Use waffle charts to show counts or parts of a whole.
Here's how you build them. #rstats pic.twitter.com/RqhaxTeOLy
#TidyTuesday week 36 Lego Bricks, data from https://t.co/1l8zxz3rpU, courtesy of @geokaramanis.
— Lee Olney (@leeolney3) September 6, 2022
Waffle plot inspired by @issa_madjid #rstats code: https://t.co/7TLlBIL2gu pic.twitter.com/N6SazCysof
##
Coincidentally, I had struggled with this a few months back for some freelance work and was meaning to document this alternative approach that uses ggsvg
to draw svg (Scalable Vector Graphics) image files arranged in a waffle-like grid. Using svg images has the advantage of us being able to map aesthetics such as size, fill, or color to different elements of the svg. This way we don’t need separate image files or icons if we only need them to vary in size or color.
A quick example:
Let’s load some libraries and set up some example data in long format. In this case we have different regions and varying numbers of dogs of different age classes for each region.
If we want a multi-panel plot with the total number of dogs per region arranged in a waffle grid with one icon per observation, we can group the data by Region, calculate the group sizes, and create a named vector with this bit of information.
To arrange the points along an xy grid using expand.grid()
, I borrowed some logic from the waffle
package and enforced some simple cutoff points for how many rows I wanted depending on the number of observations. I’m sure this can be improved upon.
With the grid set up, it can be bound to the original data (arranging first to keep the groups together). Then some minor preparation for the plotting can help, such as re-leveling factors and setting up labels.
To plot the xy data as points with a nicer distribution of space, we can draw them on top of a transparent grid from geom_raster()
. This would be the basis of the waffle chart. This already shows the total number of dogs per region and the overall distribution by age class within each one.
Next, we need an svg image. I downloaded this jumping dog (below) from freesvg.org into my working directory. Next,ggsvg
needs this image as text, so we read its contents as a string.
For fun, let’s map the age class to the color of the dog’s collar. The ggsvg
documentation explains that “we can use the css()
helper function to target aesthetics at selected elements within an SVG using css(selector, property = value)
”.
To identify the dog collar in the image, I opened the svg in a browser, used the Inspect Element tool, and copied the css selector that I needed.
This selector feeds into geom_svg_point()
and as the aesthetics
argument to scale_svg_fill_manual()
. Point size can be mapped to age class, so that puppies appear smaller than adults, and the rest is some minor tweaking to get a nice look. For reference, this includes putting the legend at the bottom with the title above and the labels below.
Looks nice! Thanks to Mike FC for developing ggsvg
and for help with understanding the css()
function.
All feedback welcome.