ggplot2: разделить легенду на два столбца, каждый со своим названием

у меня есть эти факторы

require(ggplot2)
names(table(diamonds$cut))
# [1] "Fair"      "Good"      "Very Good" "Premium"   "Ideal" 

что я хочу визуально разделить на две группы В легенде (с указанием также наименования группы):

"первая группа" - > "справедливо","хорошо"

и

"вторая группа" - > "очень хорошо", "премиум", "идеально"

начиная с этого участка

ggplot(diamonds, aes(color, fill=cut)) + geom_bar() + 
  guides(fill=guide_legend(ncol=2)) +
  theme(legend.position="bottom")

Я хочу сделать

enter image description here

(обратите внимание, что "очень хорошо" проскользнуло во второй колонке / группе)

3 ответов


вы можете переместить категорию "очень хорошо" во второй столбец легенды, добавив уровень фиктивного фактора и установив его цвет в белый в легенде, чтобы его нельзя было увидеть. В приведенном ниже коде мы добавляем пустой уровень фактора между "хорошо" и "очень хорошо", поэтому теперь у нас есть шесть уровней. Затем мы используем scale_fill_manual чтобы установить цвет этого пустого уровня в "белый". drop=FALSE сил ggplot сохранить пустой уровень в легенде. Возможно, есть более элегантный способ контролировать, где ggplot места значение легенды, но, по крайней мере, это позволит выполнить работу.

diamonds$cut = factor(diamonds$cut, levels=c("Fair","Good"," ","Very Good",
                                             "Premium","Ideal"))

ggplot(diamonds, aes(color, fill=cut)) + geom_bar() + 
  scale_fill_manual(values=c(hcl(seq(15,325,length.out=5), 100, 65)[1:2], 
                             "white",
                             hcl(seq(15,325,length.out=5), 100, 65)[3:5]),
                    drop=FALSE) +
  guides(fill=guide_legend(ncol=2)) +
  theme(legend.position="bottom")

enter image description here

обновление: Я надеюсь, что есть лучший способ добавить названия к каждой группе в легенде, но единственный вариант, который я могу придумать сейчас, - это прибегнуть к grobs, что всегда дает мне головную боль. Приведенный ниже код взят из ответа это так вопрос. Он добавляет два текстовых grobs, по одному для каждой метки, но метки должны быть расположены вручную, что огромная боль. Код для участок также должен быть изменен, чтобы создать больше места для легенды. Кроме того, несмотря на то, что я отключил вырезку для всех grobs, ярлыки все еще обрезаны легендой grob. Вы можете расположить метки вне обрезанной области, но тогда они слишком далеки от легенды. Я надеюсь, кто-то, кто действительно знает, как работать с grobs может исправить это и в целом улучшить код ниже (@baptiste, вы там?).

library(gtable)

p = ggplot(diamonds, aes(color, fill=cut)) + geom_bar() + 
  scale_fill_manual(values=c(hcl(seq(15,325,length.out=5), 100, 65)[1:2], 
                             "white",
                             hcl(seq(15,325,length.out=5), 100, 65)[3:5]),
                    drop=FALSE) +
  guides(fill=guide_legend(ncol=2)) +
  theme(legend.position=c(0.5,-0.26),  
        plot.margin=unit(c(1,1,7,1),"lines")) +
  labs(fill="") 

# Add two text grobs
p = p + annotation_custom(
    grob = textGrob(label = "First\nGroup", 
                    hjust = 0.5, gp = gpar(cex = 0.7)),
    ymin = -2200, ymax = -2200, xmin = 3.45, xmax = 3.45) +
  annotation_custom(
    grob = textGrob(label = "Second\nGroup",
                    hjust = 0.5, gp = gpar(cex = 0.7)),
    ymin = -2200, ymax = -2200, xmin = 4.2, xmax = 4.2)

# Override clipping
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip <- "off"
grid.draw(gt)

и вот результат:

enter image description here


Это добавляет заголовки в файлы gtable легенды. Он использует метод @eipi10 для перемещения категории "очень хорошо" во второй столбец Легенды (Спасибо).

метод извлекает легенду из сюжета. Gtable легенды можно манипулировать. Здесь в таблицу gtable добавляется дополнительная строка,а в новую строку добавляются заголовки. Легенда (после небольшой тонкой настройки)затем возвращается в сюжет.

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

diamonds$cut = factor(diamonds$cut, levels=c("Fair","Good"," ","Very Good",
                                             "Premium","Ideal"))

p = ggplot(diamonds, aes(color, fill = cut)) + 
       geom_bar() + 
       scale_fill_manual(values = 
              c(hcl(seq(15, 325, length.out = 5), 100, 65)[1:2], 
              "white",
              hcl(seq(15, 325, length.out = 5), 100, 65)[3:5]),
              drop = FALSE) +
  guides(fill = guide_legend(ncol = 2, title.position = "top")) +
  theme(legend.position = "bottom", 
        legend.key = element_rect(fill = "white"))

# Get the ggplot grob
g = ggplotGrob(p)

# Get the legend
leg = g$grobs[[which(g$layout$name == "guide-box")]]$grobs[[1]]

# Set up the two sub-titles as text grobs
st = lapply(c("First group", "Second group"), function(x) {
   textGrob(x, x = 0, just = "left", gp = gpar(cex = 0.8)) } )

# Add a row to the legend gtable to take the legend sub-titles
leg = gtable_add_rows(leg, unit(1, "grobheight", st[[1]]) + unit(0.2, "cm"), pos =  3)

# Add the sub-titles to the new row
leg = gtable_add_grob(leg, st, 
            t = 4, l = c(2, 6), r = c(4, 8), clip = "off")

# Add a little more space between the two columns
leg$widths[[5]] = unit(.6, "cm")

# Move the legend to the right
 leg$vp = viewport(x = unit(.95, "npc"), width = sum(leg$widths), just = "right")

# Put the legend back into the plot
g$grobs[[which(g$layout$name == "guide-box")]] = leg

# Draw the plot
grid.newpage()
grid.draw(g)

enter image description here


следуя идее @eipi10, вы можете добавить название названий в качестве меток, с white значения:

diamonds$cut = factor(diamonds$cut, levels=c("Title 1           ","Fair","Good"," ","Title 2","Very Good",
                                         "Premium","Ideal"))

ggplot(diamonds, aes(color, fill=cut)) + geom_bar() + 
   scale_fill_manual(values=c("white",hcl(seq(15,325,length.out=5), 100, 65)[1:2], 
                              "white","white",
                              hcl(seq(15,325,length.out=5), 100, 65)[3:5]),
                     drop=FALSE) +
   guides(fill=guide_legend(ncol=2)) +
   theme(legend.position="bottom", 
         legend.key = element_rect(fill=NA),
         legend.title=element_blank())

enter image description here

я ввожу некоторые пробелы после "Title 1 " для разделения столбцов и улучшения дизайна, но может быть возможность увеличить пространство.

единственная проблема в том, что я понятия не имею, как изменить формат надписей "title" (я пробовал bquote или expression но это не работа.)

_____________________________________________________________

в зависимости от графика, который вы пытаетесь, правильное выравнивание легенды может быть лучшей альтернативой, и этот трюк выглядит лучше (IMHO). Он разделяет легенду на две части и лучше использует пространство. Все, что вам нужно сделать, это изменить ncol на 1 и "bottom" (legend.position) в "right":

diamonds$cut = factor(diamonds$cut, levels=c("Title 1","Fair","Good"," ","Title 2","Very Good","Premium","Ideal"))


ggplot(diamonds, aes(color, fill=cut)) + geom_bar() + 
   scale_fill_manual(values=c("white",hcl(seq(15,325,length.out=5), 100, 65)[1:2], 
                              "white","white",
                              hcl(seq(15,325,length.out=5), 100, 65)[3:5]),
                     drop=FALSE) +
   guides(fill=guide_legend(ncol=1)) +
   theme(legend.position="bottom", 
         legend.key = element_rect(fill=NA),
         legend.title=element_blank())

enter image description here

в этом случае имеет смысл оставить заголовок в этой версии, удалив legend.title=element_blank()