Skip to content

Instantly share code, notes, and snippets.

@jonocarroll
Created June 2, 2016 12:10
Show Gist options
  • Save jonocarroll/1d1bdb00a7b3910d62bf3eec8a77b4a7 to your computer and use it in GitHub Desktop.
Save jonocarroll/1d1bdb00a7b3910d62bf3eec8a77b4a7 to your computer and use it in GitHub Desktop.
Replace categorical x-axis labels with images
#' Replace categorical x-axis labels with images
#'
#' Pipe a ggplot2 graph (with categorical x-axis) into this function with the argument of a list of
#' pictures (e.g. loaded via readImage) and it builds a new grob with the x-axis categories
#' now labelled by the images. Solves a problem that you perhaps shouldn't have.
#'
#' @author J. Carroll, \email{jono@@jcarroll.com.au}
#' @references \url{http://stackoverflow.com/questions/29939447/icons-as-x-axis-labels-in-r-ggplot2}
#'
#' @param g ggplot graph with categorical x axis
#' @param pics ordered list of pictures to place along x-axis
#'
#' @return NULL (called for the side-effect of producing a new grob with images for x-axis labels)
#'
#' @import grid
#' @import ggplot2
#'
#' @export
#'
#' @example
#' \dontrun{ggplot(data, aes(x=factor(x),y=y)) + geom_point() %>% add_images_as_xlabels(pics)}
#'
add_images_as_xlabels <- function(g, pics) {
## ensure that the input is a ggplot
if(!inherits(g, "ggplot")) stop("Requires a valid ggplot to attach images to.")
## extract the components of the ggplot
gb <- ggplot_build(gg)
xpos <- gb$panel$ranges[[1]]$x.major
yrng <- gb$panel$ranges[[1]]$y.range
## ensure that the number of pictures to use for labels
## matches the number of x categories
if(length(xpos) != length(pics)) stop("Detected a different number of pictures to x categories")
## create a new grob of the images aligned to the x-axis
## at the categorical x positions
my_g <- do.call("grobTree", Map(rasterGrob, pics, x=xpos, y=0))
## annotate the original ggplot with the new grob
gg <- gg + annotation_custom(my_g,
xmin = -Inf,
xmax = Inf,
ymax = yrng[1] + 0.25*(yrng[2]-yrng[1])/npoints,
ymin = yrng[1] - 0.50*(yrng[2]-yrng[1])/npoints)
## turn off clipping to allow plotting outside of the plot area
gg2 <- ggplotGrob(gg)
gg2$layout$clip[gg2$layout$name=="panel"] <- "off"
## produce the final, combined grob
grid.newpage()
grid.draw(gg2)
return(invisible(NULL))
}
@baptiste
Copy link

baptiste commented Jun 2, 2016

annotation_custom is really a hacky way of doing this; better post-process the gtable, or bypass the element_zzz checks with element_blank. http://stackoverflow.com/questions/14070953/photo-alignment-with-graph-in-r/14078391
tidyverse/ggplot2#1240

@jonocarroll
Copy link
Author

@baptiste you're 100% correct and your method is vastly superior/exactly what I was after. New gist: https://gist.github.com/jonocarroll/2f9490f1f5e7c82ef8b791a4b91fc9ca and blog post: http://jcarroll.com.au/2016/06/03/images-as-x-axis-labels-updated/ (will be scraped to R-bloggers sometime today). Thank you very kindly for your help.

Any chance of a new push to get this into ggplot2 officially?

@yeedle
Copy link

yeedle commented Mar 6, 2017

 gb   <- ggplot_build(gg)

that's a typo, should be ggplot_build(g)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment