2

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?

alberto
  • 2,625
  • 4
  • 29
  • 48
  • I dont think you can do this with `facet_grid`, try using `facet_wrap` or else tweak your data..`df.edges[c("from", "to")] <- lapply(df.edges[c("from", "to")], gsub, pattern="forum\\d+_", replacement="")` – user20650 Jul 23 '16 at 20:31

1 Answers1

3

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

eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Thanks eipi10, that works ! However, just for the record, if you want to you the user label (it won't be my case because I have one thousand) in the axis and their usernames have different lengths, each matrix has a different size. – alberto Jul 23 '16 at 22:05
  • 1
    You can ensure that the plot area is the same size in each plot by using the `plot_grid` function from the `cowplot` pacakge. Also see [this SO answer](http://stackoverflow.com/a/35304121/496488). – eipi10 Jul 23 '16 at 23:10