dual y axis together with facet_wrap

2019-06-24 02:50发布

问题:

At How can I put a transformed scale on the right side of a ggplot2? it was shown how to add two y-axis in the same plot by manipulation and merging ggplot2 objects with gtable. From the example there I managed to extend it to work with facet_wrap. See the example below.

There are however three things that are not perfect.

  1. The scale is always put on the far right. It would be better if it connected with the plot in the last row
  2. It doesn't work if there is y-axis on all plots separately (i.e. you put scales="free_y" in facet_wrap)
  3. If I leave the grid-lines in (the line that is commented out) The grid-lines from the second plots appear in front of the first plot.

Any ideas if there is a smart way to fix these admittedly small issues?

library(ggplot2)
library(gtable)
library(grid)

p1 <- ggplot(diamonds, aes(y=carat,x=price))
p1 <- p1 + geom_point(color="red")
p1 <- p1 + facet_wrap(~ color)
p1 <- p1 + theme_bw()  %+replace%  theme(panel.background = element_rect(fill = NA)) # use white theme and set bg to transparent so they can merge nice
#p1 <- p1 + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) # remove gridlines
p1


p2 <- ggplot(diamonds, aes(x=price))
p2 <- p2 + geom_histogram( binwidth = 1000)
p2 <- p2 +  facet_wrap(~ color)
p2 <- p2 + theme_bw()  %+replace%  theme(panel.background = element_rect(fill = NA))
#p2 <- p2 + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

p2



## Putting plots together ##################
# extract gtable
g1 <- ggplot_gtable(ggplot_build(p1))
g2 <- ggplot_gtable(ggplot_build(p2))

# overlap the panel of 2nd plot on that of 1st plot
pp <- c(subset(g1$layout, grepl("panel",name) , se = t:r))
g <- gtable_add_grob(g1, g2$grobs[grep("panel",g2$layout$name)], pp$t, 
                     pp$l, pp$b, pp$l)



# axis tweaks
ia <- which(grepl("axis_l",g2$layout$name) |  grepl("axis-l",g2$layout$name)     )
ga <- g2$grobs[ia]


axis_idx <- as.numeric(which(sapply(ga,function(x) !is.null(x$children$axis))))

for(i in 1:length(axis_idx)){
  ax <- ga[[axis_idx[i]]]$children$axis
  ax$widths <- rev(ax$widths)
  ax$grobs <- rev(ax$grobs)
  ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
  g <- gtable_add_cols(g, g2$widths[g2$layout[ia[axis_idx[i]], ]$l], length(g$widths) - 1)
  g <- gtable_add_grob(g, ax, pp$t[axis_idx[i]], length(g$widths) - i, pp$b[axis_idx[i]])
}



# Plot!
grid.newpage()
grid.draw(g)

回答1:

Here is a tweak that you should be able to tweak further to your satisfaction. Working out something more precise and general would take me more time than I have left at this instant. But I think you'll have no difficulty in taking it the extra step.

The first few steps are unchanged.

Here I copy your procedure for the top 2 row-panels, not adding back the tweaked axes at the bottom:

# do not add back the bottom lhs axis
for(i in 1:(length(axis_idx)-1)) {
    ax <- ga[[axis_idx[i]]]$children$axis
    ax$widths <- rev(ax$widths)
    ax$grobs <- rev(ax$grobs)
    ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
    g <- gtable_add_cols(g, 
        g2$widths[g2$layout[ia[axis_idx[i]], ]$l], length(g$widths) - 1)
    g <- gtable_add_grob(g, 
        ax, pp$t[axis_idx[i]], length(g$widths) - i, pp$b[axis_idx[i]])
}

Here I treat the bottom row separately. This is the bit where I have not generalized. You'll need to tweak the distances between the ticks and the vertical axis a bit. You'll also need to generalize the indexing for cases where there is only one plot at the bottom, 2 plots, etc..

# Here I fix the index ``i`` to 3, to cater for your example.
i <- length(axis_idx)
    ax <- ga[[axis_idx[i]]]$children$axis
    ax$widths <- rev(ax$widths)
    ax$grobs <- rev(ax$grobs)
    ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
    g <- gtable_add_cols(g, g2$widths[3], 12)
    g <- gtable_add_grob(g, ax, pp$t[axis_idx[i]], length(g$widths) - 9, pp$b[axis_idx[i]])

The bits that need to be generalized are the numbers 12 and 9. The bit that probably needs to be tweaked is the line with unit(0.15, "cm") to get more space than there appears to be at the moment.

To begin with your g object has width 12, which is the 3 by 3 panel plus 3 vertical axes. Then you add a column to cater for the second axis, and you obtain a width of 15. The number 12 is chosen to be on the rhs of the bottom plot. The number 9 is chosen to place the second axis there. I think.



标签: r ggplot2 gtable