81 votes

Comment fusionner les légendes de couleur, de style de ligne et de forme dans ggplot ?

Supposons que j'ai le graphique suivant dans ggplot :

ggplot graph

Il a été généré en utilisant le code ci-dessous :

x <- seq(0, 10, by = 0.2)
y1 <- sin(x)
y2 <- cos(x)
y3 <- cos(x + pi / 4)
y4 <- sin(x + pi / 4)
df1 <- data.frame(x, y = y1, Type = as.factor("sin"), Method = as.factor("method1"))
df2 <- data.frame(x, y = y2, Type = as.factor("cos"), Method = as.factor("method1"))
df3 <- data.frame(x, y = y3, Type = as.factor("cos"), Method = as.factor("method2"))
df4 <- data.frame(x, y = y4, Type = as.factor("sin"), Method = as.factor("method2"))

df.merged <- rbind(df1, df2, df3, df4)

ggplot(df.merged, aes(x, y, colour = interaction(Type, Method), linetype = Method, shape = Type)) + geom_line() + geom_point()

Je voudrais n'avoir qu'une seule légende qui affiche correctement les formes, les couleurs et les types de lignes (les légendes interaction(Type, Method) sont les plus proches de ce que je voudrais, mais elles n'ont pas les formes/types de lignes corrects).

Je sais que si j'utilise scale_xxx_manual et que je spécifie les mêmes étiquettes pour toutes les légendes, elles seront fusionnées, mais je ne veux pas avoir à définir les étiquettes manuellement : s'il y a de nouvelles méthodes ou de nouveaux types, je ne veux pas avoir à modifier mon code : une envie de quelque chose de générique.

Modifier

Comme indiqué dans les réponses ci-dessous, il y a plusieurs façons de faire le travail dans ce cas particulier. Toutes les solutions proposées nécessitent de définir manuellement les types et les formes des lignes de la légende, soit en utilisant la fonction scale_xxx_manual function ou avec guides fonction.

Cependant, les solutions proposées ne fonctionnent toujours pas dans le cas général : par exemple, si j'ajoute un nouveau cadre de données à l'ensemble de données avec une nouvelle méthode "method3", cela ne fonctionne plus, nous devons ajouter manuellement les nouvelles formes de légende et les nouveaux types de lignes :

y5 <- sin(x - pi / 4)
df5 <- data.frame(x, y = y5, Type = as.factor("sin"), Method = as.factor("method3"))
df.merged <- rbind(df1, df2, df3, df4, df5)
override.shape <- c(16, 17, 16, 17, 16)
override.linetype <- c(1, 1, 3, 3, 4)

g <- ggplot(df.merged, aes(x, y, colour = interaction(Type, Method), linetype = Method, shape = Type)) + geom_line() + geom_point()
g <- g + guides(colour = guide_legend(override.aes = list(shape = override.shape, linetype = override.linetype)))
g <- g + scale_shape(guide = FALSE)
g <- g + scale_linetype(guide = FALSE)
print(g)

Cela donne :

5 curves

La question qui se pose maintenant est la suivante : comment générer automatiquement le fichier override.shape et override.linetype des vecteurs ?

Notez que la taille du vecteur est de 5 parce que nous avons 5 courbes, alors que le vecteur interaction(Type, Method) facteur a la taille 6 (je n'ai pas de données pour la combinaison cos/method3)

2 votes

Votre échelle de couleur est redondante par rapport aux échelles combinées de forme et de type de ligne. Vous ne devriez utiliser qu'une seule de ces échelles.

1 votes

Oui, c'est redondant. Au début, j'avais des couleurs mappées à Type et linetype mappé à Method. Mais ensuite, lorsque les courbes étaient trop proches les unes des autres, il était difficile de les distinguer. D'où la redondance

10 votes

Il est souvent approprié d'avoir des définitions de groupes de formes/couleurs redondantes. Dans de nombreuses publications scientifiques, la couleur est le moyen le plus efficace visuellement de distinguer les groupes, mais vous savez également qu'une grande partie des lecteurs imprimeront des copies en noir et blanc du document, et vous souhaitez donc inclure un indice visuel qui ne dépend pas de la couleur.

48voto

lillemets Points 150

Utilisez labs() et définir la même valeur pour toutes les esthétiques définissant l'apparence des géoms.

library('ggplot2')
ggplot(iris) + 
  aes(x = Sepal.Length, y = Sepal.Width, 
      color = Species, linetype = Species, shape = Species) +
  geom_line() +
  geom_point() +
  labs(color  = "Guide name", linetype = "Guide name", shape = "Guide name")

2 votes

C'est de loin l'option la plus simple qui permet de fusionner les types de légendes.

14 votes

Cela ne fonctionne que si la même variable est utilisée pour chaque esthétique, contrairement à la question de l'OP ?

1 votes

Belle solution facile, mais comment changer l'étiquette des articles ?

33voto

Pierre Lafortune Points 5107

La section du R Cookbook sur Légendes explique :

Si vous utilisez à la fois la couleur et la forme, il faut leur donner des spécifications d'échelle. des spécifications. Sinon, il y aura deux légendes distinctes.

Dans votre cas, vous avez besoin de spécifications pour shape et linetype .

Modifier

Il était important d'avoir les mêmes données créant les formes, les couleurs et les lignes, j'ai combiné votre phase d'interaction en définissant directement la colonne. Au lieu de scale_linetype_discrete pour créer la légende, j'ai utilisé scale_linetype_manual pour spécifier les valeurs puisqu'elles prendront quatre valeurs différentes par défaut.

Si vous souhaitez obtenir une présentation détaillée de toutes les formes et de tous les types de lignes possibles, consultez le site suivant ce site R Graphics pour voir tous les identifiants des numéros :

df.merged$int <- paste(df.merged$Type, df.merged$Method, sep=".")

ggplot(df.merged, aes(x, y, colour = int, linetype=int, shape=int)) +
  geom_line() +
  geom_point() +
  scale_colour_discrete("") +
  scale_linetype_manual("", values=c(1,2,1,2)) +
  scale_shape_manual("", values=c(17,17,16,16))

enter image description here

2 votes

Ce serait la même chose que de doubler les légendes pour le type de ligne et la forme, mais je pense que l'OP veut 4 symboles de légende avec une combinaison différente de couleur/type de ligne/forme dans chacun.

1 votes

C'est presque ce que je cherche : vous avez une seule légende, c'est vrai. Mais je pourrais obtenir le même résultat avec scale_shape(guide = FALSE) et scale_linetype(guide = FALSE). Et avec votre solution, je n'ai pas les lignes pleines/étiquetées dans la légende, ni la forme.

0 votes

Je vois. C'est très similaire à ma solution et à celle proposée par @eipi10. Dans votre cas cependant, les valeurs d'échelle de la forme et du type de ligne sont définies manuellement.

15voto

Ben Points 1511

Voici la solution dans le cas général :

# Create the data frames
x <- seq(0, 10, by = 0.2)
y1 <- sin(x)
y2 <- cos(x)
y3 <- cos(x + pi / 4)
y4 <- sin(x + pi / 4)
y5 <- sin(x - pi / 4)
df1 <- data.frame(x, y = y1, Type = as.factor("sin"), Method = as.factor("method1"))
df2 <- data.frame(x, y = y2, Type = as.factor("cos"), Method = as.factor("method1"))
df3 <- data.frame(x, y = y3, Type = as.factor("cos"), Method = as.factor("method2"))
df4 <- data.frame(x, y = y4, Type = as.factor("sin"), Method = as.factor("method2"))
df5 <- data.frame(x, y = y5, Type = as.factor("sin"), Method = as.factor("method3"))

# Merge the data frames
df.merged <- rbind(df1, df2, df3, df4, df5)

# Create the interaction
type.method.interaction <- interaction(df.merged$Type, df.merged$Method)

# Compute the number of types and methods
nb.types <- nlevels(df.merged$Type)
nb.methods <- nlevels(df.merged$Method)

# Set the legend title
legend.title <- "My title"

# Initialize the plot
g <- ggplot(df.merged, aes(x,
                           y,
                           colour = type.method.interaction,
                           linetype = type.method.interaction,
                           shape = type.method.interaction)) + geom_line() + geom_point()
# Here is the magic
g <- g + scale_color_discrete(legend.title)
g <- g + scale_linetype_manual(legend.title,
                               values = rep(1:nb.types, nb.methods))
g <- g + scale_shape_manual(legend.title,
                            values = 15 + rep(1:nb.methods, each = nb.types))
# Display the plot
print(g)

Le résultat est le suivant :

The solution

  • Les courbes sinus sont dessinées en lignes pleines et les courbes cosinus en lignes pointillées.
  • Les données "method1" utilisent des cercles remplis pour la forme.
  • Les données "method2" utilisent un triangle rempli pour la forme.
  • Les données "method3" utilisent des diamants remplis pour la forme.
  • La légende correspond à la courbe

Pour résumer, les astuces sont :

  • Utilisez le type/méthode interaction pour toutes les représentations de données (couleur, forme, type de ligne, etc.)
  • Ensuite, définissez manuellement les styles de courbe et les styles de légendes avec scale_xxx_manual .
  • scale_xxx_manual vous permet de fournir un vecteur de valeurs qui est plus long que le nombre réel de courbes, de sorte qu'il est facile de calculer les valeurs du vecteur de style à partir des tailles des facteurs Type et Méthode.

12voto

Il suffit de donner le même nom aux deux guides. Par exemple :

g+ scale_linetype_manual(name="Guide1",values= c('solid', 'solid', 'dotdash'))+
   scale_colour_manual(name="Guide1", values = c("blue", "green","red"))

0 votes

Non, malheureusement, cela ne fonctionne pas pour moi.

10voto

eipi10 Points 3549

Le code ci-dessous donne la légende souhaitée, si je comprends votre question, mais je ne suis pas sûr de comprendre le problème de l'étiquette, alors faites-moi savoir si ce n'est pas ce que vous recherchez.

p = ggplot(df.merged, aes(x, y, colour=interaction(Type, Method), 
                          linetype=interaction(Type, Method), 
                          shape=interaction(Type, Method))) + 
  geom_line() + 
  geom_point()

p + scale_shape_manual(values=rep(16:17, 2)) +
  scale_linetype_manual(values=rep(c(1,3),each=2))

enter image description here

0 votes

C'est en effet une façon de procéder. Je vais poster une autre solution que j'ai trouvée. IMHO, les deux ne sont pas très simples et j'aurais souhaité qu'il y ait une meilleure façon de traiter ce cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X