Getting fun with ggdogs, ggcats and gganimate

[This article was first published on R in the Lab, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

From time to time we all need some fun, so why not make some funny graphs since we are R users, yeah! This post is based on the tutorials on R CHARTS, do not forget to pick a look.

Bar dogs

First I want to show you how to add some good doggos to an old-fashioned bar chart. If you have not installed ggdogs, run the next code (you need devtools):


Now, let’s begin with some fake data. Yeah, real data is overrated:

# Packages

# Some fake data
fake_data <- data.frame(
  treatment = c("1", "2", "3"),
  mean      = c(0.3, 10, 5),
  de        = c(.05, 0.1, 3)

Then, represent your fake data with a bar chart. There are things that even columns and deviation bars cannot hide! I use geom_dog() to represent significant differences, geom_errobar() to include deviation bars, and geom_label() to put some nice text:

ggplot(fake_data, aes(x = treatment, y = mean)) +
  geom_col(fill = "lightskyblue4", color = "black", width = 0.4, size = 0.6) +
    aes(ymin = mean - de, ymax = mean + de), width = 0.1, color = "black", size = 0.6
  ) +
      dog = c("chihuahua", "surprised", "thisisfine"), 
      y = c(1.6, 11.1, 9)
    size = 4
  ) +
  geom_label(aes(x = "3", y = 10.5, label = "this is fine")) +
  ylim(c(0, 12)) +
    x = element_blank(),
    y = "The average response",
    caption = "Graph by JPCH. Generated with `ggplot2` and `ggdogs`"
  ) +
  scale_x_discrete(labels = c("Control (-)", "Control (+)", "My best treatment")) +
  theme_classic() +
    axis.text.x = element_text(color = "black", size = 11, face = "bold"),
    axis.text.y = element_text(color = "black", size = 11),
    axis.title  = element_text(face = "bold", color = "black", size = 13)

You can save your graph in your working folder with ggsave(), just run the next chunk of code:

  filename = "dogo_bar_en.png", 
  dpi = 300,
  height = 5,
  width = 7.5,
  units = "cm"

As you see, you can manipulate your image’s resolution with dpi and its size with heightand width. Default units are inches (“in”), but you can choose centimeters (“cm”), millimeters (“mm”) or pixels (“px”) with the units argument.

Animated cats

Now let’s do something a little more advanced, let’s animate a graph and include some cats. Everyone loves cats! Actually, this is easy with the ggcats and gganimete packages, once you have the right data. If you want to install ggcats run the next code:


First let’s simulate some data. For this I defined the next function to make easy generate data sets:

# Packages

# A function to generate a data frame with simulated data and cats
data_cat <- function(from = 0, to = 80, by = 1, fun = rnorm, 
                     cat = "", sd = 3, category = "") {
    x = seq(from, to, by),
    y = fun(x) + rnorm(length(x), sd = sd),
    category = rep(category, length(x)),
    cat = rep(cat, length(x))

This function allows me to generate data within a sequence, and based on this sequence and a function, numbers with some noise are calculated. cat is used to specify a cat name included in ggcats and with category you can set anything of your interest and that will be repeated in the data set in its own column. Perhaps this will become clearer later when we generate the animation.

The next step is make the data sets, one for the circadian cycle that is my focus during, I don’t know, all time, another for my duties, self-imposed or not, and one more to represent my faithful companion anxiety:

# Data for my focus
concentration <- data_cat(
  fun = function(x) 4 * sin(1.5 * x) + 4, 
  cat = "lil_bub", 
  sd = 1, category = "focus"

# Data for my duties

duties <- data_cat(
  fun = function(x) 5 + 1.5 * exp(x / 20), 
  cat = "pusheen_pc", 
  sd = 2, category = "duties"

# Data for my anxiety
anxiety <- data_cat(
  fun = function(x) 10 + exp(x / 15) + 4 * sin(x), 
  cat = "nyancat", 
  sd = 1, category = "anxiety"

# Complete data
full_data <- rbind(concentration, duties, anxiety)

Note how I defined some anonymous functions, or helpers, within each call of data_cat. If want to know more about anonymous functions check out the next link: Anonymous functions.

With rbind() I just joined all data sets for the next step, which is to generate the animation itself:

ggplot(full_data, aes(x, y)) +
  geom_line(aes(color = category), size = 1) +
  geom_cat(aes(cat = cat), size = 4) +
    y = element_blank(),
    x = "Time",
    caption = "Graph by JPCH. Generated with `ggplot2`, `ggcats` and `gganimate`",
  ) +
    values = c("#EE2C2C", "#FF8C00", "#68228B"),
    labels = c("Anxiety", "My focus", "Duties")) +
  theme_fivethirtyeight() +
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.text.x = element_text(size = 12, color = "black"),
    axis.title.x = element_text(size = 14, face = "bold"),
    legend.text = element_text(size = 14, face = "bold"),
    legend.position = "top",
    legend.title = element_blank()
  ) +

For some reason, R Markdown did not display the animation, but don’t worry, you can save it with anim_save():

anim_save(filename = "cat_animation.gif")

And then we can display our gif with include_graphics() from knitr package. Don’t forget to include the right path to your animation:

knitr::include_graphics(path = "images/cat_animation.gif")

transition_reveal() is in charge to make our animation, basically allow us to let data gradually appear, based on a given time dimension. It’s not restricted to “time”, you can use sequential data and that will serve. If you want to know more about this function just type ?transition_reveal in your console.

geom_cat() just positions the images of our cats at the final point of the sequence. If a graph is generated for an increasing interval within the sequence, we will have the necessary frames to produce our animation. Great!


It seems like things in the world are going from bad to worse, from increasingly overwhelming and underpaid jobs to wars and climate change. It’s not a bad thing to relax and have some fun once in a while, even if it means laughing at yourself, why not. Wherever you are and whoever you are, I wish you the best, I wish you peace of mind. Thanks for reading this post.

Juan Pablo Carreón Hidalgo 🤓

The text and code on this tutorial is under Creative Commons Attribution 4.0 International License.

CC BY 4.0

To leave a comment for the author, please follow the link and comment on their blog: R in the Lab. offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)