Двойная ось y вместе с facet_wrap

В Как я могу поместить преобразованную шкалу в правую сторону ggplot2? было показано, как добавить две оси Y в один и тот же график, манипулируя и слияние объектов ggplot2 с gtable. В этом примере мне удалось расширить его для работы с facet_wrap. См. Пример ниже.

Однако есть три вещи, которые не идеальны.

  • Шкала всегда ставится на крайнее право. Было бы лучше, если бы он был связан с сюжетом в последней строке
  • Это не работает, если на всех графиках имеется одна ось y (т.е. вы помещаете scales="free_y" в facet_wrap)
  • Если я оставлю строки сетки (строка, которая закомментирована) Строки сетки из вторых графиков появляются перед первым графиком.

Любые идеи, если есть умный способ исправить эти, по общему признанию, небольшие проблемы?

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)

facet_wrap with dual axis

Ответ 1

Вот настройка, которую вы должны усовершенствовать для вашего удовлетворения. Разработка чего-то более точного и общего потребовала бы мне больше времени, чем я ушел в этот момент. Но я думаю, вам не составит труда сделать дополнительный шаг.

Первые несколько шагов не изменяются.

Здесь я копирую вашу процедуру для двух верхних панелей, не добавляя назад подстроенные оси внизу:

# 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]])
}

Здесь я обрабатываю нижнюю строку отдельно. Это бит, где я не обобщен. Вам нужно немного настроить расстояния между тиками и вертикальной осью. Вам также необходимо обобщить индексирование для случаев, когда внизу находится только один график, 2 графика и т.д.

# 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]])

Биты, которые должны быть обобщены, - это числа 12 и 9. Бит, который, вероятно, нужно настроить, - это строка с unit(0.15, "cm"), чтобы получить больше места, чем кажется на данный момент.

Для начала ваш объект g имеет ширину 12, которая представляет собой панель 3 на 3 плюс 3 вертикальные оси. Затем добавьте столбец для второй оси, и вы получите ширину 15. Число 12 выбрано так, чтобы оно находилось на rhs нижнего участка. Число 9 выбрано для размещения второй оси. Я думаю.

enter image description here