2016-05-10 13 views
24

Załóżmy, że mam następującą działkę w ggplot:Jak połączyć kolor, styl linii i legendy kształt w ggplot

ggplot graph

został wygenerowany za pomocą poniższego kodu:

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() 

bym lubię mieć tylko jedną legendę, która poprawnie wyświetla kształty, kolory i typy linii (legendy interakcji (typu, metody) są najbliższe temu, co bym chciał, ale nie ma właściwych kształtów/typów linii).

Wiem, że jeśli użyję scale_xxx_manual i podam te same etykiety dla wszystkich legend, to zostaną one scalone, ale nie chcę ustawiać etykiet ręcznie: jeśli pojawią się nowe metody lub typy, to ja "Chcę zmodyfikować mój kod: chcę czegoś ogólnego.

Edit

Jak wskazano w odpowiedzi poniżej, istnieje kilka sposobów, aby otrzymać pracę w tym konkretnym przypadku. Wszystkie proponowane rozwiązania wymagają ręcznego ustawiania typów linii i kształtów legendy za pomocą funkcji scale_xxx_manual function lub z funkcją guides.

Jednak proponowane rozwiązania nadal nie działają w ogólnym przypadku: na przykład, jeśli dodaję nową ramkę danych do zestawu danych za pomocą nowej metody "method3", to już nie działa, musimy ręcznie dodawać nowe kształty i rodzaje legenda linia:

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) 

Daje:

5 curves

Nasuwa się pytanie: w jaki sposób automatycznie generować override.shape i override.linetype wektory?

Należy zauważyć, że wielkość wektor jest 5 ponieważ mamy 5 krzywych, natomiast współczynnik interaction(Type, Method) ma rozmiar 6 (nie mam danych dla cos/kombinacji method3)

+1

Twoja skala kolorów jest zbędna w połączeniu ze skalą kształtu i rodzaju linii. Powinieneś używać tylko jednego z nich. – Roland

+0

Tak, jest to zbyteczne. Początkowo miałem kolory zmapowane na typ i rodzaj linii odwzorowany na metodę. Ale kiedy krzywe były zbyt blisko siebie, trudno było je odróżnić. Zatem redundancja – Ben

+1

Często właściwe jest posiadanie nadmiarowych definicji grup kształtów/kolorów. W wielu publikacjach naukowych kolor jest najbardziej wizualnie skutecznym sposobem rozróżniania grup, ale wiesz również, że duża część czytelników będzie drukować czarno-białe kopie papieru, a więc chcesz również dodać wizualną sygnaturę, która nie jest ". t zależy od koloru. – neuropsych

Odpowiedz

3

Tutaj jest rozwiązanie w przypadku ogólnym:

# 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) 

Wynik jest następujący:

The solution

  • krzywa sinus s są rysowane jako linie ciągłe i krzywe cosinus jako linie przerywane.
  • Dane "method1" używają wypełnionych kółek dla kształtu.
  • Dane "method2" wykorzystują wypełniony trójkąt dla kształtu.
  • Dane "method3" wykorzystują wypełnione diamenty dla kształtu.
  • Legenda dopasowuje krzywą

Podsumowując, sztuczki to:

  • użyć typu/metody interaction dla wszystkich reprezentacji danych (kolor, kształt, rodzaj linii, etc.)
  • Następnie ręcznie ustaw style krzywej i legendy na scale_xxx_manual.
  • scale_xxx_manual pozwala na dostarczenie wartości wektora, który jest dłuższy niż rzeczywistej liczby krzywych, więc łatwo obliczyć wartości wektora w stylu od wielkości od rodzaju i sposób czynniki
16

miałem ten problem na drugi dzień. Sekcja R Cookbook na Legends wyjaśnia:

Jeśli używasz zarówno kolor i kształt, oboje muszą być danej skali specyfikacji. W przeciwnym razie będą dwie dwie oddzielne legendy.

W twoim przypadku potrzebujesz specyfikacji dla shape i linetype.

Edycja

Jest ważne, aby te same dane tworzące kolory, kształty i linie, to w połączeniu Fazy interakcję, definiując kolumny bezpośrednio. Zamiast scale_linetype_discrete do utworzenia legendy użyłem scale_linetype_manual do określenia wartości, ponieważ będą one domyślnie przyjmowały cztery różne wartości.

Jeśli chciałbyś szczegółowy układ wszystkich możliwych kształtów i typów linii, sprawdzić this R Graphics site zobaczyć wszystkich identyfikatorów numerycznych:

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

+1

Cóż, to jest to samo, co różniące się legendy dla rodzaju linii i kształtu, ale myślę, że OP chce 4 symboli legendy o różnych kolorach/rodzajach/kształtach połączonych w każdym – beetroot

+0

Cóż, to prawie to, czego szukam: masz jedną legendę, prawdziwe. Ale mogę uzyskać ten sam wynik z scale_shape (guide = FALSE) i scale_linetype (guide = FALSE).I przy twoim rozwiązaniu nie mam stałych/przerywanych linii w legendzie, ani kształtu. – Ben

+0

@beetroot Zgadzam się – Ben

5

Kod poniżej wyników w pożądanym legendy jeśli rozumiem twoje pytanie, ale nie jestem pewien, czy rozumiem problem z etykietą, więc daj mi znać, jeśli nie tego szukasz.

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

To jeden ze sposobów robienia tego naprawdę. Wezwę inne rozwiązanie, które znalazłem. IMHO, oba nie są bardzo proste i żałuję, że nie było lepszego sposobu na załatwienie tej sprawy. – Ben

2

Jeden wystarczy imię oba prowadzą tak samo. Na przykład:

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

który faktycznie działa całkiem dobrze !!!!! –

Powiązane problemy