Поиск обходного пути для файла gtable_add_grob, нарушенного ggplot 2.2.0

На графиках с несколькими фасетными переменными ggplot2 повторяет метку фасета для «внешней» переменной, вместо того, чтобы иметь единую полосу фасета на всех уровнях «внутренней» переменной. У меня есть код, который я использовал, чтобы покрыть повторяющиеся внешние метки фасетов одной полосой фасета с использованием gtable_add_grob из пакета gtable .

К сожалению, этот код больше не работает с ggplot2 2.2.0 из-за изменений в структуре grob фасетных полос. В частности, в предыдущих версиях ggplot2 каждая строка меток с метками получила свой собственный набор гномов. Тем не менее, в версии 2.2.0 он выглядит как каждый вертикальный стек меток фасет – это один гун. Это нарушает мой код, и я не уверен, как его исправить.

Вот конкретный пример, взятый из вопроса SO, который я ответил несколько месяцев назад :

 # Data df = structure(list(location = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("SF", "SS"), class = "factor"), species = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("AGR", "LKA"), class = "factor"), position = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), .Label = c("top", "bottom"), class = "factor"), density = c(0.41, 0.41, 0.43, 0.33, 0.35, 0.43, 0.34, 0.46, 0.32, 0.32, 0.4, 0.4, 0.45, 0.34, 0.39, 0.39, 0.31, 0.38, 0.48, 0.3, 0.42, 0.34, 0.35, 0.4, 0.38, 0.42, 0.36, 0.34, 0.46, 0.38, 0.36, 0.39, 0.38, 0.39, 0.39, 0.39, 0.36, 0.39, 0.51, 0.38)), .Names = c("location", "species", "position", "density"), row.names = c(NA, -40L), class = "data.frame") # Begin with a regular ggplot with three facet levels p=ggplot(df, aes("", density)) + geom_boxplot(width=0.7, position=position_dodge(0.7)) + theme_bw() + facet_grid(. ~ species + location + position) + theme(panel.margin=unit(0,"lines"), strip.background=element_rect(color="grey30", fill="grey90"), panel.border=element_rect(color="grey90"), axis.ticks.x=element_blank()) + labs(x="") 

Мы начинаем с сюжета, который имеет три уровня граней.

введите описание изображения здесь

Теперь мы рассмотрим две верхние полосы фасетки с полосками, чтобы у нас не было повторных меток на полосах:

 pg = ggplotGrob(p) # Add spanning strip labels for species pos = c(4,11) for (i in 1:2) { pg <- gtable_add_grob(pg, list(rectGrob(gp=gpar(col="grey50", fill="grey90")), textGrob(unique(densityAGRLKA$species)[i], gp=gpar(cex=0.8))), t=3,l=pos[i],b=3,r=pos[i]+7, name=c("a","b")) } # Add spanning strip labels for location pos=c(4,7,11,15) for (i in 1:4) { pg = gtable_add_grob(pg, list(rectGrob(gp = gpar(col="grey50", fill="grey90")), textGrob(rep(unique(densityAGRLKA$location),2)[i], gp=gpar(cex=0.8))), t=4,l=pos[i],b=4,r=pos[i]+3, name = c("c","d")) } grid.draw(pg) 

Вот как выглядит этот сюжет с ggplot2 2.1.0:

введите описание изображения здесь

Тем не менее, если я попробую тот же код с ggplot2 2.2.0, я верну исходный график, без изменений в меток полосы. Взгляд на структуру grob исходного сюжета p предполагает, почему это происходит. Я вставил в столы гвоздя в нижней части этого вопроса. Чтобы сэкономить место, я включил только строки, связанные с полосами фасета.

Посмотрев на столбцы cells , обратите внимание, что в версии 2.1.0 графика первые два числа в каждой строке являются либо 3, 4, либо 5, что указывает на вертикальное положение grob относительно других грызков на графике. В приведенном выше коде аргументы t и l для gtable_add_grob устанавливаются в значения 3 или 4, потому что это строки полосы фасетки, которые я хотел бы покрыть с помощью охватывающих полосок.

Теперь посмотрите на столбец cells в версии 2.2.0 графика: Обратите внимание, что первые два числа всегда 6. Также обратите внимание, что полосы фасета состоят из 8 гномов вместо 24 в версии 2.1.0. В версии 2.2.0 кажется, что каждый стек из трех фасетных меток теперь представляет собой один гроб вместо трех отдельных грызунов. Поэтому, даже если я изменил аргументы t и b в gtable_add_grob на 6, все три полосы фасетов будут покрыты. Вот пример:

 pg = ggplotGrob(p) # Add spanning strip labels for species pos = c(4,11) for (i in 1:2) { pg <- gtable_add_grob(pg, list(rectGrob(gp=gpar(col="grey50", fill="grey90")), textGrob(unique(densityAGRLKA$species)[i], gp=gpar(cex=0.8))), t=6,l=pos[i],b=6,r=pos[i]+7, name=c("a","b")) } 

введите описание изображения здесь

Итак, после этого очень затянувшегося введения, вот мой вопрос: как я могу создать полоски фасетирования с ggplot2 версии 2.2.0, которые выглядят как те, которые я создал с помощью gtable_add_grob с ggplot2 версии 2.1.0? Я надеюсь, что есть простая настройка, но если это требует серьезной операции, ну, это тоже нормально.

ggplot 2.1.0

 pg 
 TableGrob (9 x 19) "layout": 45 grobs z cells name grob 2 1 ( 3- 3, 4- 4) strip-top absoluteGrob[strip.absoluteGrob.147] 3 2 ( 4- 4, 4- 4) strip-top absoluteGrob[strip.absoluteGrob.195] 4 3 ( 5- 5, 4- 4) strip-top absoluteGrob[strip.absoluteGrob.243] 5 4 ( 3- 3, 6- 6) strip-top absoluteGrob[strip.absoluteGrob.153] 6 5 ( 4- 4, 6- 6) strip-top absoluteGrob[strip.absoluteGrob.201] 7 6 ( 5- 5, 6- 6) strip-top absoluteGrob[strip.absoluteGrob.249] 8 7 ( 3- 3, 8- 8) strip-top absoluteGrob[strip.absoluteGrob.159] 9 8 ( 4- 4, 8- 8) strip-top absoluteGrob[strip.absoluteGrob.207] 10 9 ( 5- 5, 8- 8) strip-top absoluteGrob[strip.absoluteGrob.255] 11 10 ( 3- 3,10-10) strip-top absoluteGrob[strip.absoluteGrob.165] 12 11 ( 4- 4,10-10) strip-top absoluteGrob[strip.absoluteGrob.213] 13 12 ( 5- 5,10-10) strip-top absoluteGrob[strip.absoluteGrob.261] 14 13 ( 3- 3,12-12) strip-top absoluteGrob[strip.absoluteGrob.171] 15 14 ( 4- 4,12-12) strip-top absoluteGrob[strip.absoluteGrob.219] 16 15 ( 5- 5,12-12) strip-top absoluteGrob[strip.absoluteGrob.267] 17 16 ( 3- 3,14-14) strip-top absoluteGrob[strip.absoluteGrob.177] 18 17 ( 4- 4,14-14) strip-top absoluteGrob[strip.absoluteGrob.225] 19 18 ( 5- 5,14-14) strip-top absoluteGrob[strip.absoluteGrob.273] 20 19 ( 3- 3,16-16) strip-top absoluteGrob[strip.absoluteGrob.183] 21 20 ( 4- 4,16-16) strip-top absoluteGrob[strip.absoluteGrob.231] 22 21 ( 5- 5,16-16) strip-top absoluteGrob[strip.absoluteGrob.279] 23 22 ( 3- 3,18-18) strip-top absoluteGrob[strip.absoluteGrob.189] 24 23 ( 4- 4,18-18) strip-top absoluteGrob[strip.absoluteGrob.237] 25 24 ( 5- 5,18-18) strip-top absoluteGrob[strip.absoluteGrob.285] 

ggplot2 2.2.0

 pg 
 TableGrob (11 x 21) "layout": 42 grobs z cells name grob 28 2 ( 6- 6, 4- 4) strip-t-1 gtable[strip] 29 2 ( 6- 6, 6- 6) strip-t-2 gtable[strip] 30 2 ( 6- 6, 8- 8) strip-t-3 gtable[strip] 31 2 ( 6- 6,10-10) strip-t-4 gtable[strip] 32 2 ( 6- 6,12-12) strip-t-5 gtable[strip] 33 2 ( 6- 6,14-14) strip-t-6 gtable[strip] 34 2 ( 6- 6,16-16) strip-t-7 gtable[strip] 35 2 ( 6- 6,18-18) strip-t-8 gtable[strip] 

Действительно, ggplot2 v2.2.0 строит сложные столбцы столбцов по столбцу, причем каждый столбец представляет собой один grob. Это можно проверить, извлекая одну полоску, а затем исследуя ее структуру. Используя ваш сюжет:

 library(ggplot2) library(gtable) library(grid) # Your data df = structure(list(location = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("SF", "SS"), class = "factor"), species = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("AGR", "LKA"), class = "factor"), position = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), .Label = c("top", "bottom"), class = "factor"), density = c(0.41, 0.41, 0.43, 0.33, 0.35, 0.43, 0.34, 0.46, 0.32, 0.32, 0.4, 0.4, 0.45, 0.34, 0.39, 0.39, 0.31, 0.38, 0.48, 0.3, 0.42, 0.34, 0.35, 0.4, 0.38, 0.42, 0.36, 0.34, 0.46, 0.38, 0.36, 0.39, 0.38, 0.39, 0.39, 0.39, 0.36, 0.39, 0.51, 0.38)), .Names = c("location", "species", "position", "density"), row.names = c(NA, -40L), class = "data.frame") # Your ggplot with three facet levels p=ggplot(df, aes("", density)) + geom_boxplot(width=0.7, position=position_dodge(0.7)) + theme_bw() + facet_grid(. ~ species + location + position) + theme(panel.spacing=unit(0,"lines"), strip.background=element_rect(color="grey30", fill="grey90"), panel.border=element_rect(color="grey90"), axis.ticks.x=element_blank()) + labs(x="") # Get the ggplot grob pg = ggplotGrob(p) # Get the left most strip index = which(pg$layout$name == "strip-t-1") strip1 = pg$grobs[[index]] # Draw the strip grid.newpage() grid.draw(strip1) # Examine its layout strip1$layout gtable_show_layout(strip1) 

Одним из грубых способов наклеивания внутренних наклеек на наружную полоску является создание полосы с нуля:

 # Get the strips, as a list, from the original plot strip = list() for(i in 1:8) { index = which(pg$layout$name == paste0("strip-t-",i)) strip[[i]] = pg$grobs[[index]] } # Construct gtable to contain the new strip newStrip = gtable(widths = unit(rep(1, 8), "null"), heights = strip[[1]]$heights) ## Populate the gtable # Top row for(i in 1:2) { newStrip = gtable_add_grob(newStrip, strip[[4*i-3]][1], t = 1, l = 4*i-3, r = 4*i) } # Middle row for(i in 1:4){ newStrip = gtable_add_grob(newStrip, strip[[2*i-1]][2], t = 2, l = 2*i-1, r = 2*i) } # Bottom row for(i in 1:8) { newStrip = gtable_add_grob(newStrip, strip[[i]][3], t = 3, l = i) } # Put the strip into the plot # (It could be better to remove the original strip. # In this case, with a coloured background, it doesn't matter) pgNew = gtable_add_grob(pg, newStrip, t = 7, l = 5, r = 19) # Draw the plot grid.newpage() grid.draw(pgNew) 

ИЛИ используя векторный gtable_add_grob (см. Комментарии):

 pg = ggplotGrob(p) # Get a list of strips from the original plot strip = lapply(grep("strip-t", pg$layout$name), function(x) {pg$grobs[[x]]}) # Construct gtable to contain the new strip newStrip = gtable(widths = unit(rep(1, 8), "null"), heights = strip[[1]]$heights) ## Populate the gtable # Top row cols = seq(1, by = 4, length.out = 2) newStrip = gtable_add_grob(newStrip, lapply(strip[cols], `[`, 1), t = 1, l = cols, r = cols + 3) # Middle row cols = seq(1, by = 2, length.out = 4) newStrip = gtable_add_grob(newStrip, lapply(strip[cols], `[`, 2), t = 2, l = cols, r = cols + 1) # Bottom row newStrip = gtable_add_grob(newStrip, lapply(strip, `[`, 3), t = 3, l = 1:8) # Put the strip into the plot pgNew = gtable_add_grob(pg, newStrip, t = 7, l = 5, r = 19) # Draw the plot grid.newpage() grid.draw(pgNew) 

введите описание изображения здесь

РЕДАКТИРОВАТЬ Чтобы разрешить панели разной ширины (т. scales = "free_x" , space = "free_x" ).

Эта попытка берет оригинальный ggplot, извлекает некоторую информацию, а затем создает новый гриф, содержащий перекрывающиеся полосы. Функция не очень красивая, но она работает … пока. Он требует установки plyr .

 library(ggplot2) library(grid) library(gtable) df = structure(list(location = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("SF", "SS"), class = "factor"), species = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("AGR", "LKA"), class = "factor"), position = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), .Label = c("top", "bottom"), class = "factor"), density = c(0.41, 0.41, 0.43, 0.33, 0.35, 0.43, 0.34, 0.46, 0.32, 0.32, 0.4, 0.4, 0.45, 0.34, 0.39, 0.39, 0.31, 0.38, 0.48, 0.3, 0.42, 0.34, 0.35, 0.4, 0.38, 0.42, 0.36, 0.34, 0.46, 0.38, 0.36, 0.39, 0.38, 0.39, 0.39, 0.39, 0.36, 0.39, 0.51, 0.38)), .Names = c("location", "species", "position", "density"), row.names = c(NA, -40L), class = "data.frame") # Begin with a regular ggplot with three facet levels p=ggplot(df, aes("", density)) + geom_boxplot(width=0.7, position=position_dodge(0.7)) + theme_bw() + facet_grid(. ~ species + location + position) + theme(panel.spacing=unit(0,"lines"), strip.background=element_rect(color="grey30", fill="grey90"), panel.border=element_rect(color="grey90"), axis.ticks.x=element_blank()) + labs(x="") ## The function to get overlapping strip labels OverlappingStripLabels = function(plot) { # Get the ggplot grob g = ggplotGrob(plot) ### Collect some information about the strips from the plot # Get a list of strips strip = lapply(grep("strip-t", g$layout$name), function(x) {g$grobs[[x]]}) # Number of strips NumberOfStrips = sum(grepl(pattern = "strip-t", g$layout$name)) # Number of rows NumberOfRows = length(strip[[1]]) # Panel spacing and it's unit plot_theme <- function(p) { plyr::defaults(p$theme, theme_get()) } PanelSpacing = plot_theme(plot)$panel.spacing unit = attr(PanelSpacing, "unit") # Map the boundaries of the new strips Nlabel = vector("list", NumberOfRows) map = vector("list", NumberOfRows) for(i in 1:NumberOfRows) { for(j in 1:NumberOfStrips) { Nlabel[[i]][j] = getGrob(grid.force(strip[[j]][i]), gPath("GRID.text"), grep = TRUE)$label } map[[i]][1] = TRUE for(j in 2:NumberOfStrips) { map[[i]][j] = Nlabel[[i]][j] != Nlabel[[i]][j-1] } } ## Construct gtable to contain the new strip # Set the widths of the strips, based on widths of the panels and PanelSpacing panel = subset(g$layout, grepl("panel", g$layout$name), l, drop = TRUE) StripWidth = list() for(i in seq_along(panel)) StripWidth[[i]] = unit.c(g$width[panel[i]], PanelSpacing) newStrip = gtable(widths = unit.c(unit(unlist(StripWidth), c("null", unit)))[-2*NumberOfStrips], heights = strip[[1]]$heights) ## Populate the gtable seqLeft = list() for(i in 1:NumberOfRows) { Left = which(map[[i]] == TRUE) seqLeft[[i]] = if((i-1) < 1) 2*Left - 1 else sort(unique(c(seqLeft[[i-1]], 2*Left - 1))) seqRight = c(seqLeft[[i]][-1] -2, (2*NumberOfStrips-1)) newStrip = gtable_add_grob(newStrip, lapply(strip[(seqLeft[[i]]+1)/2], `[`, i), t = i, l = seqLeft[[i]], r = seqRight) } ## Put the strip into the plot # Get the locations of the original strips pos = subset(g$layout, grepl("strip-t", g$layout$name), t:r) ## Use these to position the new strip pgNew = gtable_add_grob(g, newStrip, t = unique(pos$t), l = min(pos$l), r = max(pos$r)) return(pgNew) } ## Draw the plot grid.newpage() grid.draw(OverlappingStripLabels(p)) 

введите описание изображения здесь

Вероятно, было бы не слишком сложно сломать функцию, но я попробовал ее на данных, где последовательность строк не такая четная.

 p1 = ggplot(mtcars, aes("", hp)) + geom_boxplot(width=0.7, position=position_dodge(0.7)) + theme_bw() + facet_grid(. ~ vs + am + carb, labeller = label_both) + theme(panel.spacing=unit(0.2,"lines"), strip.background=element_rect(color="grey30", fill="grey90"), panel.border=element_rect(color="grey90"), axis.ticks.x=element_blank()) + labs(x="") grid.draw(OverlappingStripLabels(p1)) p2 = ggplot(mtcars, aes("", hp)) + geom_boxplot(width=0.7, position=position_dodge(0.7)) + theme_bw() + facet_grid(. ~ vs + carb + am, labeller = label_both) + theme(panel.spacing=unit(0.2,"lines"), strip.background=element_rect(color="grey30", fill="grey90"), panel.border=element_rect(color="grey90"), axis.ticks.x=element_blank()) + labs(x="") grid.draw(OverlappingStripLabels(p2)) df = structure(list(id = 1:19, category1 = c("X", "X", "X", "X", "X", "X", "X", "X", "X", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), category2 = c(21L, 21L, 21L, 22L, 22L, 22L, 22L, 22L, 22L, 23L, 23L, 23L, 24L, 24L, 24L, 25L, 25L, 26L, 26L), category3 = c("C1", "C2", "C3", "D1", "D2", "D3", "D5", "D6", "D7", "E1", "E2", "E3", "F1", "F2", "F3", "G1", "G2", "H1", "H2"), freq = c(4L, 7L, 4L, 28L, 20L, 0L, 1L, 4L, 1L, 17L, 33L, 31L, 20L, 20L, 21L, 15L, 18L, 12L, 13L)), .Names = c("id", "category1", "category2", "category3", "freq"), class = "data.frame", row.names = c(NA, -19L)) p3 = ggplot(df, aes(category3, freq)) + geom_bar(stat = "identity") + facet_grid(. ~ category1 + category2, scale = "free_x", space = "free_x") grid.draw(OverlappingStripLabels(p3)) 
  • Как выбрать переменные в фрейме данных R, чьи имена содержат определенную строку?
  • ggplot2, facet_grid, свободные шкалы?
  • объединить data.frames в зависимости от года и заполнить отсутствующие значения
  • Как удалить выбросы из набора данных
  • Передавать аргументы функции dplyr
  • Заменить символы с помощью gsub, как создать функцию?
  • Как отправить электронное письмо с приложением из R в windows
  • Отображение значений данных на гистограмме с разбивкой по столбцам в ggplot2
  • R как перечислить все остальные элементы?
  • Извлеките столбец dplyr tbl в виде вектора
  • Подсчитайте слова в R
  • Interesting Posts

    SFINAE для проверки наследуемых функций-членов

    pandas group и присвоить идентификатор группы, а затем разгруппировать

    Начало деятельности с помощью уведомления: избегание повторяющихся действий

    Опасны ли внутренние ЖК-мониторы?

    Асинхронно ждать Task , чтобы завершить таймаут

    Как вызвать метод суперclassа с использованием отражения Java

    удалить все числовые символы из столбца mysql

    nvidia-smi Неустойчивое использование GPU-использования?

    Получите Cortana для работы на английском (Великобритания) для Windows 10

    Ярлык клавиатуры, чтобы скрыть / показать навигацию Explorer

    Есть ли какой-либо API для интеграции сервера обмена Microsoft с Java-приложением для синхронизации задач?

    Хороший инструмент выбора цвета, который работает в фоновом режиме?

    Может (a == 1 && a == 2 && a == 3) оценивать true в Java?

    Как создать изображение из UILabel?

    Ошибка при выпуске поврежденных файлов Windows 10 0x800f081f

    Давайте будем гением компьютера.