rggplot2geom-raster

Plot multiple matrices in facets with different x-y axis


I collected the data from a set of online forums and wanted to plot, using ggplot and facets (one facet per forum), the matrix that represent how many times user A replied to user B.

Here is the code to load a toy example:

library(ggplot2)
library(dplyr)

df.edges <- data.frame(from = c('forum1_user1', 'forum1_user1',
                                'forum1_user2', 'forum1_user2',
                                'forum2_user1', 'forum2_user1',
                                'forum2_user2', 'forum2_user2',
                                'forum3_user1', 'forum3_user1',
                                'forum3_user2', 'forum3_user2'),
                         to = c('forum1_user1', 'forum1_user2',
                                'forum1_user1', 'forum1_user2',
                                'forum2_user1', 'forum2_user2',
                                'forum2_user1', 'forum2_user2',
                                'forum3_user1', 'forum3_user2',
                                'forum3_user1', 'forum3_user2'),
                        weight = 1:12,
                        timestamp = 1:12,
                        subforum = c('forum1', 'forum1', 'forum1', 'forum1',
                                     'forum2', 'forum2', 'forum2', 'forum2',
                                     'forum3', 'forum3', 'forum3', 'forum3'))

I try this:

# Sort for later use in scale_discrete
df.edges <- df.edges %>% arrange(timestamp)


gg <- ggplot(df.edges, aes(x = from, y = to, fill = weight)) +
  geom_raster() + coord_fixed() + 
  facet_grid(. ~subforum, scales='fixed') +
  scale_x_discrete("from", aes(limits = from))+
  scale_y_discrete("to", aes(limits = from)) + 
  theme_bw() +
  theme(axis.line        = element_blank(),
        axis.text.x      = element_text(angle = 90, hjust=1, size=8),
        axis.text.y      = element_text(hjust=1, size=10),
        axis.ticks       = element_blank(),
        strip.background = element_rect(fill = 'white'), 
        aspect.ratio = 1) +
  ggtitle("Matrix of interactions") + xlab('from') + ylab('to')
print(gg)

which gives this:

enter image description here

And if I set the facet scale scale='free':

enter image description here

However, I want each facet to show only those users belonging to that forum. The matrices should be completely filled with 4 cells in each one.

Any idea?


Solution

  • You could create a separate plot for each level of subforum and then lay them out together using grid.arrange:

    library(gridExtra)
    library(grid)
    

    First, create the separate plots and store in a list. We add scale_fill_continuous(limits=range(df.edges$weight)) to ensure a consistent fill gradient across the three plots:

    pl = lapply(split(df.edges, df.edges$subforum), function(df) {
        ggplot(df, aes(x = from, y = to, fill = weight)) +
          geom_raster() + coord_fixed() + 
          facet_grid(. ~subforum, scales='fixed') +
          scale_x_discrete("from", aes(limits = from))+
          scale_y_discrete("to", aes(limits = from)) + 
          scale_fill_continuous(limits=range(df.edges$weight)) +
          theme_bw() +
          theme(axis.line        = element_blank(),
                axis.text.x      = element_text(angle = 90, hjust=1, size=8),
                axis.text.y      = element_text(hjust=1, size=10),
                axis.ticks       = element_blank(),
                strip.background = element_rect(fill = 'white'), 
                aspect.ratio = 1) +
          xlab('from') + ylab('to') 
      })
    

    Extract the legend, as we want only one legend, rather than a separate legend for each plot:

    # Function to extract legend
    #https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
    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]]
      return(legend) }
    
    # Extract legend as a grob
    leg = g_legend(pl[[1]])
    

    Arrange the plots with legend and title:

    grid.arrange(
      textGrob("Matrix of Interactions"),
      arrangeGrob(
        arrangeGrob(grobs=lapply(pl, function(x) x + guides(fill=FALSE)), ncol=3), 
        leg, ncol=2, widths=c(10,1)
      ),
      heights=c(1,20)
    )
    

    enter image description here