Добавить метки плавающей оси на графике facet_wrap
У меня та же проблема, что и у этого пользователя - у меня есть "зубчатый" граненый график, в котором в нижнем ряду меньше панелей, чем в других строках, и я хотел бы иметь отметки по оси X в нижней части каждого столбца.
Предложенное решение для этой проблемы должно было установить scales="free_x"
, (В ggplot 0.9.2.1; я считаю, что поведение, которое я искал, было по умолчанию в более ранних версиях.) В моем случае это плохое решение: мои фактические метки оси будут довольно длинными, поэтому размещение их под каждой строкой будет занимать слишком много номер. Результаты примерно такие:
x <- gl(3, 1, 15, labels=paste("this is a very long axis label ", letters[1:5]))
y <- rnorm(length(x))
l <- gl(5, 3, 15)
d <- data.frame(x=x, y=y, l=l)
ggplot(d, aes(x=x, y=y)) + geom_point() + facet_wrap(~l, scales="free_x") +
theme(axis.text.x=element_text(angle=90, hjust=1))
В комментарии здесь, Андри предполагает, что это можно сделать вручную в grid
но я понятия не имею, с чего начать.
1 ответ
Если я правильно помню, возникли вопросы о том, как добавить все метки в одну и ту же строку под последним столбцом и как поднять эти последние метки до следующей строки. Итак, вот функция для обоих случаев:
Изменить: так как это как замена print.ggplot
(увидеть getAnywhere(print.ggplot)
) Я добавил несколько строк из него, чтобы сохранить функциональность.
Редактировать 2: я улучшил это немного больше: не нужно указывать nrow
а также ncol
больше можно печатать графики со всеми панелями.
library(grid)
# pos - where to add new labels
# newpage, vp - see ?print.ggplot
facetAdjust <- function(x, pos = c("up", "down"),
newpage = is.null(vp), vp = NULL)
{
# part of print.ggplot
ggplot2:::set_last_plot(x)
if(newpage)
grid.newpage()
pos <- match.arg(pos)
p <- ggplot_build(x)
gtable <- ggplot_gtable(p)
# finding dimensions
dims <- apply(p$panel$layout[2:3], 2, max)
nrow <- dims[1]
ncol <- dims[2]
# number of panels in the plot
panels <- sum(grepl("panel", names(gtable$grobs)))
space <- ncol * nrow
# missing panels
n <- space - panels
# checking whether modifications are needed
if(panels != space){
# indices of panels to fix
idx <- (space - ncol - n + 1):(space - ncol)
# copying x-axis of the last existing panel to the chosen panels
# in the row above
gtable$grobs[paste0("axis_b",idx)] <- list(gtable$grobs[[paste0("axis_b",panels)]])
if(pos == "down"){
# if pos == down then shifting labels down to the same level as
# the x-axis of last panel
rows <- grep(paste0("axis_b\\-[", idx[1], "-", idx[n], "]"),
gtable$layout$name)
lastAxis <- grep(paste0("axis_b\\-", panels), gtable$layout$name)
gtable$layout[rows, c("t","b")] <- gtable$layout[lastAxis, c("t")]
}
}
# again part of print.ggplot, plotting adjusted version
if(is.null(vp)){
grid.draw(gtable)
}
else{
if (is.character(vp))
seekViewport(vp)
else pushViewport(vp)
grid.draw(gtable)
upViewport()
}
invisible(p)
}
А вот как это выглядит
d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
xlim(0, 2) + stat_binhex(na.rm = TRUE) + theme(aspect.ratio = 1) +
facet_wrap(~ color)
facetAdjust(d)
facetAdjust(d, "down")
Изменить 3:
Это альтернативное решение, выше тоже хорошо.
Есть некоторые проблемы, когда кто-то хочет использовать ggsave
вместе с facetAdjust
, Сюжет о классе ggplot
требуется из-за двух частей в исходном коде ggsave
: print(plot)
а также default_name(plot)
в случае, если один не предоставляет имя файла вручную (в соответствии с ?ggsave
кажется, что это не должно работать, хотя). Следовательно, учитывая имя файла, существует обходной путь (возможно, с побочными эффектами в некоторых случаях):
Сначала рассмотрим отдельную функцию, которая достигает основного эффекта плавающей оси. Обычно это вернуло бы gtable
объект, однако мы используем class(gtable) <- c("facetAdjust", "gtable", "ggplot")
, Таким образом, разрешено использовать ggsave
а также print(plot)
работает как требуется (см. ниже для print.facetAdjust
)
facetAdjust <- function(x, pos = c("up", "down"))
{
pos <- match.arg(pos)
p <- ggplot_build(x)
gtable <- ggplot_gtable(p); dev.off()
dims <- apply(p$panel$layout[2:3], 2, max)
nrow <- dims[1]
ncol <- dims[2]
panels <- sum(grepl("panel", names(gtable$grobs)))
space <- ncol * nrow
n <- space - panels
if(panels != space){
idx <- (space - ncol - n + 1):(space - ncol)
gtable$grobs[paste0("axis_b",idx)] <- list(gtable$grobs[[paste0("axis_b",panels)]])
if(pos == "down"){
rows <- grep(paste0("axis_b\\-[", idx[1], "-", idx[n], "]"),
gtable$layout$name)
lastAxis <- grep(paste0("axis_b\\-", panels), gtable$layout$name)
gtable$layout[rows, c("t","b")] <- gtable$layout[lastAxis, c("t")]
}
}
class(gtable) <- c("facetAdjust", "gtable", "ggplot"); gtable
}
Функция для печати, которая отличается только на несколько строк от ggplot2:::print.ggplot
:
print.facetAdjust <- function(x, newpage = is.null(vp), vp = NULL) {
if(newpage)
grid.newpage()
if(is.null(vp)){
grid.draw(x)
} else {
if (is.character(vp))
seekViewport(vp)
else pushViewport(vp)
grid.draw(x)
upViewport()
}
invisible(x)
}
Пример:
d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
xlim(0, 2) + stat_binhex(na.rm = TRUE) + theme(aspect.ratio = 1) +
facet_wrap(~ color)
p <- facetAdjust(d) # No output
print(p) # The same output as with the old version of facetAdjust()
ggsave("name.pdf", p) # Works, a filename is necessary