Anthony Anthony - 1 year ago 126
R Question

R ggplot2 facet_grid-like results but with independent columns

I'd like to plot a few measurements on different individuals in two treatment groups. I'd like to show two columns, one for the first treatment, one for the second treatment. Each column would have a plot for each individual in that group. Something like this:

t <- seq(from=0, to=10, length.out=100)
ids <- c(1, 17, 22, 4, 55, 74, 88)
treatment <- c('A', 'A', 'A', 'B', 'B', 'B', 'B')
df <- NULL
for (i in 1:length(ids))
df <- rbind(df, data.frame(time=t, treatment=treatment[i], id=ids[i], value=rnorm(length(t))))
ggplot(df, aes(y=value, x=time)) +
geom_line() +
facet_grid(id ~ treatment, scale='free_y')

which produces

Resulting plot

The IDs will almost certainly not line up so you get a number of empty graphs in the middle. The IDs don't correspond to the same individuals anyway, so lining them up is not necessary. I'd like for the ID to be independent in the two columns. Is there any convenient way to do this in ggplot without resorting to anything overly hacky or am I stuck looking at different plots side by side? I could give them "pseudo" IDs which would remove the gaps but removes the utility of the row labels.

Answer Source

This is just a hair hacky, but you can generate a new variable with both the group and the id, then facet_wrap on that instead of using facet_grid. The only hacky part is making sure that you have the same number of individuals in each group (here, adding place holders for the empty ones)

# Generate a label with the individual and id
df$label <-
        , df$id
        , sep = ": ")

# Count the number of individuals in each treatment
counts <-
  by(df$id, df$treatment, function(x){length(unique(x))})

# For each group, check how many there are
# If it is less than the max, add a dummy row as a placeholder
for(i in names(counts)){
  if(counts[i] < max(counts)){
    df <- rbind(df,data.frame(time=0, treatment=i, id=NA, value=0, label= paste(i, "holder", 1:(max(counts) - counts[i]))))

# Plot the result
ggplot(df, aes(y=value, x=time)) +
  geom_line() +
  # Facet on the contstructed label
             , scale='free_y'
             # Make sure that you put them in columns, not rows
             , dir = "v"
             # Set the number of columns to be the number of groups
             , ncol = length(unique(df$treatment)))

enter image description here