9

I'd like to force facet_wrap to fill from the top-left, but in such a way as to always fully fill the bottom row. (That is, from the top-left plus whatever horizontal offset is required to fully fill the bottom row.)

library(ggplot2)

n <- 1000
df <- data.frame(x=runif(n), y=rnorm(n), label=sample(letters[1:7], size=n, replace=TRUE))
df$label.rev <- factor(df$label, levels=sort(unique(df$label), decreasing=TRUE))

p <- ggplot(df, aes(x=x, y=y)) + geom_point()
p1 <- p + facet_wrap(~ label, ncol=3)
p2 <- p + facet_wrap(~ label, ncol=3, as.table=FALSE)
p3 <- p + facet_wrap(~ label.rev, ncol=3, as.table=FALSE)

p1: I'm happy with the left-to-right, top-to-bottom ordering of the labels, but I'd like the gap to be at the top left instead of the bottom right.

p2: Gap is now in the top row (top right rather than top left, unfortunately), but the label order is wrong.

p3: Similar to p2; attempts to fix the label order, and fails.

I'd like the facets to be ordered like this:

_ _ A
B C D
E F G

...so that there are axes along the entire bottom row, and because I think it would look better than the default. How can I do this?

Adrian
  • 3,138
  • 2
  • 28
  • 39

1 Answers1

9

Would this fix suffice?

library(ggplot2)
n <- 1000
df <- data.frame(x = runif(n), y=rnorm(n), label = sample(letters[1:7], 
                 size = n, replace = TRUE), stringsAsFactors=TRUE)
# following @Didzis' suggestion (with some minor changes)
df$label.new <- factor(df$label, levels=sort(c(""," ",levels(df$label))))
p <- ggplot(df, aes(x=x, y=y)) + geom_point() + 
         facet_wrap(~ label.new, ncol=3,drop=FALSE)

enter image description here


EDIT (from @baptiste):

Starting from the last solution, it's easier to remove the grobs from the gtable,

g = ggplotGrob(p)
## remove empty panels
g$grobs[names(g$grobs) %in% c("panel1", "panel2", "strip_t.1", "strip_t.2")] = NULL
## remove them from the layout
g$layout = g$layout[!(g$layout$name %in% c("panel-1", "panel-2", 
                                                "strip_t-1", "strip_t-2")),]
## move axis closer to panel
g$layout[g$layout$name == "axis_l-1", c("l", "r")] = c(9,9)
grid.newpage()
grid.draw(g)

enter image description here

Arun
  • 116,683
  • 26
  • 284
  • 387
  • 2
    The same can be achieved without reversing labels. Just add before original levels `df$label.new<-factor(df$label, levels=c(" "," ",levels(df$label)))` and then `p+facet_wrap(~ label.new, ncol=3,drop=FALSE)` – Didzis Elferts Aug 20 '13 at 15:00
  • I'd like to avoid those empty panels if possible. Could they be made invisible? Is it possible to control the styles of individual panels? – Adrian Aug 20 '13 at 15:07
  • The line df$label.new <- factor(df$label, levels=sort(c(""," ",unique(df$label)))) doesn't work as intended on my machine -- my df$label.new are all NAs. Are we running different versions of R? I have 3.0.1 (2013-05-16) -- "Good Sport". – Adrian Aug 20 '13 at 15:20
  • it just occurred to me that you might have `stringsAsFactors=FALSE` in your .Rprofile, which would explain why I had to use levels(df$label) instead of unique() to make it work. – baptiste Aug 22 '13 at 11:28
  • When I execute the above code (including the **edit** section by @baptiste), I get the following warning message: "In mapply(wrap_gtableChild, x$grobs, children_vps, SIMPLIFY = FALSE) : longer argument not a multiple of length of shorter". In addition, the [generated figure](https://www.dropbox.com/s/68q1y89p1hhap06/3_by_3_ggplot.png?dl=0) is quite mixed-up. Any ideas on that? – fdetsch Mar 17 '15 at 09:46
  • 1
    @fdetsch, replace `"strip_t.1", "strip_t.2"` with `"strip_t1", "strip_t2"` – Sandy Muspratt Jun 12 '15 at 05:33
  • is that still working under latest R/ggplot2? Line `names(g$grobs) %in% c("panel1", "panel2", "strip_t.1", "strip_t.2")` seems to return NULL? Thanks! – Matifou Apr 14 '19 at 23:41