ggplot2: divisez la légende en deux colonnes, chacune avec son propre titre

j'ai ces facteurs

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

que je veux diviser visuellement en deux groupes dans la légende (en indiquant aussi le nom du groupe):

"Premier groupe" -> "Juste", "Bon"

et

"Deuxième groupe" -> "Très Bon", "Premium", "Idéal"

à partir de ce tracé

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

je veux obtenir

enter image description here

(notez que "très bon" a glissé dans la deuxième colonne / Groupe)

12
demandé sur Make42 2015-01-06 20:12:55

3 réponses

vous pouvez déplacer la catégorie" très bon " à la deuxième colonne de la légende en ajoutant un facteur fictif niveau et en réglant sa couleur au blanc dans la légende, de sorte qu'il ne peut pas être vu. Dans le code ci-dessous, nous ajoutons un niveau de facteur blanc Entre "bon" et "très bon", donc maintenant nous avons six niveaux. Ensuite, nous utilisons scale_fill_manual pour définir la couleur de ce nouveau niveau de "blanc". drop=FALSE force ggplot pour garder le niveau vide dans la légende. Il pourrait y avoir une façon plus élégante de contrôle où ggplot lieux de la des valeurs légendaires, mais au moins cela fera le travail.

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

mise à jour: j'espère qu'il y a une meilleure façon d'ajouter des titres à chaque groupe dans la légende, mais la seule option que je peux trouver pour le moment est de recourir à grobs, ce qui me donne toujours un mal de tête. Le code ci-dessous est adapté de la réponse à ce DONC, la question. Il ajoute deux texte grobs, un pour chaque étiquette, mais les étiquettes doivent être positionnés à la main, ce qui est un énorme douleur. Le code de la parcelle doit également être modifié pour créer plus d'espace pour la légende. De plus, même si j'ai désactivé le découpage pour tous les grobs, les étiquettes sont toujours découpées par le Grob legend. Vous pouvez placer les étiquettes en dehors de la zone coupée, mais elles sont trop loin de la légende. J'espère que quelqu'un qui vraiment sait comment travailler avec grobs peut corriger cela et plus généralement améliorer le code ci-dessous (@baptiste, êtes-vous là?).

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)

Et voici le résultat:

enter image description here

17
répondu eipi10 2017-05-23 12:17:34

ceci ajoute les titres au gtable de la légende. Il utilise la technique de @eipi10 pour déplacer la catégorie" très bon " dans la deuxième colonne de la légende (merci).

la méthode extrait la légende de l'intrigue. Le gtable de la légende peut être manipulé. Ici, une ligne supplémentaire est ajouté à la gtable, et les titres sont ajoutés à la nouvelle ligne. La légende (après un petit réglage) est alors remise dans l'intrigue.

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

3
répondu Sandy Muspratt 2016-06-25 07:29:24

suivant l'idée de @eipi10, vous pouvez ajouter le nom des titres comme étiquettes, avec white valeurs:

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

je suis d'introduire des espaces blancs après "Title 1 " pour séparer les colonnes et d'améliorer la conception, mais il pourrait y avoir une option pour augmenter l'espace.

Le seul problème est que je n'ai aucune idée de comment changer le format de la "titre" étiquettes (j'ai essayé bquote ou expression mais il n'a pas travail.)

_____________________________________________________________

selon le graphe que vous tentez, un alignement droit de la légende pourrait être une meilleure alternative, et ce tour semble mieux (IMHO). Il sépare la légende en deux, et utilise l'Espace mieux. Tout ce que vous avez à faire est de changer le ncol retour 1 et "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

Dans ce cas, il pourrait être judicieux de laisser le titre dans cette version, en supprimant legend.title=element_blank()

3
répondu toto_tico 2018-07-16 14:30:23