11

I think I have a tricky case. I'm plotting the evolution plant disease levels in time, using geom_raster: x and y are arbitrary field coordinates, and z is the disease level measured at several time points, and I want to have each date plotted in a different facet. So far, no problem. Below is a mock dataset and code:

library(ggplot2)
data <- data_frame(month=factor(rep(c("march","april","may","june"), each=100), levels=c("march","april","may","june")),
              x=rep(rep(1:10, each=10), 4),
              y=rep(rep(1:10, 10), 4),
              z=c(rnorm(100, 0.5, 1), rnorm(100, 3, 1.5), rnorm(100, 6, 2), rnorm(100, 9, 1)))
ggplot(data, aes(x=x, y=y, fill=z)) +
  geom_raster(color="white") +
  scale_fill_gradient2(low="white", mid=mean(range(dat$z)), high="red") +
  scale_x_discrete(limit=1:10, expand = c(0, 0)) +
  scale_y_discrete(limit=1:10, expand = c(0, 0)) +
  coord_equal() +
  facet_wrap(~month)

But what I'd really like, is to have each facet rotated at a certain angle (for example 15°), to reflect the fact that my field is not oriented perfectly according to north (i.e., the top is not North, and bottom is not South). Is there a possibility in ggplot2, or any grid-related tools, to do this automatically? Even an automatic way to savec individual facets to images, rotate them, and printing the rotated images on new page would be enough for my needs. Here's an example of image I would like to obtain (facets rotated 15° in an image editor): https://i.stack.imgur.com/UiLzj.jpgrotated facets

  • Base `ggplot2` -- definitely not possible, and I highly doubt that is possible even with `grid/gridExtra`. – tonytonov Dec 18 '15 at 15:24
  • maybe, you can come up with something using system calls to ImageMagick, see e.g. http://www.r-bloggers.com/animate-gif-images-in-r-imagemagick/ – Marat Talipov Dec 18 '15 at 15:33
  • 2
    Is it not an option to rotate your data to make it face north instead? (via e.g. multiplication by the appropriate 2x2 rotation matrix) – asachet Dec 18 '15 at 15:47
  • 1
    I'm not positive, but I think you're likely going to be stuck using a post-processing software as you already have. To my knowledge, `ggplot` isn't designed to handle this case. In fact, I'd guess that the grammar of graphics would actively design against it. – alexwhitworth Dec 18 '15 at 16:13
  • 2
    This question is relevant: http://stackoverflow.com/q/33396168/1412059 – Roland Dec 18 '15 at 16:24
  • @Roland, indeed I saw this question, but in my case it is complicated by the fact that I need to rotate not the whole plot, but each facet independently. So to follow the solution proposed, it would need to send each individual facet to a respective rotated viewport. – Sebastien Guyader Dec 18 '15 at 17:17
  • @antoine-sac, I don't know what you mean, but if you could provide me with an example, I would try. – Sebastien Guyader Dec 18 '15 at 17:19

1 Answers1

16

Here's a way to rotate the facets independently. We create a list containing a separate rotated plot for each level of month, and then use grid.arrange to lay out the four plots together. I've also removed the legend from the individual plots and plotted the legend separately. The code below includes a helper function to extract the legend.

I extract the legend object into the global environment within the lapply function below (not to mention repeating the extraction multiple times). There's probably a better way, but this way was quick.

library(gridExtra)

# Helper function to extract the legend from a ggplot
# Source: http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  legend
}

# Create a list containing a rotated plot for each level of month
pl = lapply(unique(data$month), function(m) {

  # Create a plot for the current level of month
  p1 = ggplot(data[data$month==m,], aes(x=x, y=y, fill=z)) +
    geom_raster(color="white") +
    scale_fill_gradient2(low="white", high="red", 
                         limits=c(floor(min(data$z)), ceiling(max(data$z)))) +
    scale_x_discrete(limit=1:10, expand = c(0, 0)) +
    scale_y_discrete(limit=1:10, expand = c(0, 0)) +
    coord_equal() +
    facet_wrap(~month) 

  # Extract legend into global environment
  leg <<- g_legend(p1)

  # Remove legend from plot
  p1 = p1 + guides(fill=FALSE)

  # Return rotated plot
  editGrob(ggplotGrob(p1), vp=viewport(angle=-20, width=unit(0.85,"npc"), 
                                       height=unit(0.85,"npc")))                    
})

# Lay out the rotated plots and the legend and save to a png file
png("rotated.png", 1100, 1000)
grid.arrange(do.call(arrangeGrob, c(pl, ncol=2)),
             leg, ncol=2, widths=c(0.9,0.1))
dev.off()

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Neat! The only thing which could be improved in my opinion is the legend, in this example the legend scales are drawn independently for each facet, whereas it would be preferable to have a common scale. – Sebastien Guyader Dec 18 '15 at 17:30
  • 1
    I've added an additional update that creates the plot with a single legend, rather than separate legends for each plot. – eipi10 Dec 18 '15 at 18:02
  • Thank you so much eipi10, I was in fact looking at the solution you gave to another question (http://stackoverflow.com/questions/34211735/r-how-to-allocate-screen-space-to-complex-ggplot-images/34212381#34212381) and was trying to figure out how to implement it. – Sebastien Guyader Dec 18 '15 at 18:20