How to use directlabels to label lines in ggplot2 - r

library(ggplot2)
library(directlabels)
mydat <- structure(list(Name = c("Ana", "Josh", "Bart", "Ana", "Josh",
"Bart"), color_line = c("purple", "purple", "orange", "purple",
"purple", "orange"), x = c(0.864864864864865, 0.810810810810811,
0.472972972972973, 0.851351351351351, 0.702702702702703, 0.648648648648649
), y = c(0.702702702702703, 0.675675675675676, 0.797297297297297,
0.797297297297297, 0.72972972972973, 0.635135135135135), Class = c("A",
"A", "A", "B", "B", "B")), class = c("data.table", "data.frame"
), row.names = c(NA, -6L))
mydat
Name color_line x y Class
1: Ana purple 0.8648649 0.7027027 A
2: Josh purple 0.8108108 0.6756757 A
3: Bart orange 0.4729730 0.7972973 A
4: Ana purple 0.8513514 0.7972973 B
5: Josh purple 0.7027027 0.7297297 B
6: Bart orange 0.6486486 0.6351351 B
I have the above data set, and I plotted the results as follows:
g <- ggplot(mydat, aes(x = x, y = y, color = Class)) +
theme_classic() +
geom_line(mapping = aes(group = Name), color = mydat$color_line) +
geom_point() +
scale_color_manual(values=c("springgreen4", "royalblue3"))
g
Now, I would like to add the Name of each individual to each line. Something that might look like this:
The closest thing that I found is the angled.boxes in library(directlabels). You can see how it looks here.
However, when I tried the following, I got a different plot.
direct.label(g, "angled.boxes")

One option to achieve your desired result would be to use the geomtextpath package which adds lots of options to add direct labels to lines and even allows for curved text. For your use case you could simply replace geom_line by geomtextpath::geom_textline to add your labels.
Note: Additionally I slightly adjusted your code to make use of the color aes for the lines and the fill aes to color the points.
library(ggplot2)
library(geomtextpath)
pal_color <- c("purple", "purple", "orange")
names(pal_color) <- c("Ana", "Josh", "Bart")
pal_fill <- c("springgreen4", "royalblue3")
names(pal_fill) <- c("A", "B")
base <- ggplot(mydat, aes(x = x, y = y)) +
scale_color_manual(values = pal_color) +
scale_fill_manual(values= pal_fill) +
theme_classic() +
guides(color = "none")
base +
geomtextpath::geom_textline(
aes(group = Name, color = Name, label = Name), textcolour = "black") +
geom_point(aes(fill = Class), shape = 21, stroke = 0, size = 2)
Or using the offset and gap arguments you could add the labels on top of the lines:
base +
geomtextpath::geom_textline(
aes(group = Name, color = Name, label = Name),
offset = unit(5, "pt"), gap = FALSE, textcolour = "black") +
geom_point(aes(fill = Class), shape = 21, stroke = 0, size = 2)

It's not ideal, but I did this a long time ago using some math and manual adjustments
mydat %>%
group_by(Name) %>%
mutate(
posx = mean(x)*1.01,
posy = mean(y)*1.01,
angle = -60*diff(range(y))/diff(range(x))
) %>%
ggplot(aes(x = x, y = y, color = Class)) + theme_classic() +
geom_line(mapping = aes(group = Name), color = mydat$color_line) +
geom_point() + scale_color_manual(values=c("springgreen4", "royalblue3"))+
geom_text(aes(x = posx, y = posy, group = Name, label = Name, angle = angle),
size = 6, show.legend = FALSE, color = "black")

Related

Change ggplot2 legend order without changing the manually specified aesthetics

I need to make a graph with multiple kinds of data on it, and I'm plotting one type of data with lines and one type with points. I've added a manually specified legend to show which type is points and which is lines (admittedly, my approach is a bit hacky), and that's working except for the legend order. Here's a dummy example:
DF1 <- data.frame(X = 1:10,
Y = c(1:10*0.5, 1:10*0.25),
Fruit = rep(c("mango", "kiwi"), each = 10))
DF2 <- data.frame(X = 1:10,
Y = c(1:10*2, 1:10*4),
Cat = rep(c("tabby", "calico"), each = 10))
Empty <- data.frame(X = mean(DF$X),
Y = as.numeric(NA),
# Q = c(0, 1))
Type = c("Cat", "Fruit"))
Mygraph <- ggplot(DF1, aes(x = X, y = Y, color = Fruit)) +
geom_point() +
geom_line(data = DF2, aes(x = X, y = Y, linetype = Cat),
inherit.aes = F) +
labs(color = NULL, linetype = NULL) +
geom_point(data = Empty, aes(x = X, y = Y, alpha = Type),
inherit.aes = F) +
geom_line(data = Empty, aes(x = X, y = Y, alpha = Type),
inherit.aes = F) +
scale_alpha_manual(
name = "Type of item", values = c(1, 1),
breaks = c("Fruit", "Cat"),
guide = guide_legend(override.aes =
list(linetype = c("blank", "solid"),
shape = c(16, NA)))) +
theme_bw()
Mygraph
This graph looks pretty good:
But check out what happens to the "Type of item" bit when I try to specify the order:
Mygraph +
guides(alpha = guide_legend(order = 1),
linetype = guide_legend(order = 2),
color = guide_legend(order = 3))
Why do my specified aesthetics go away? How can I both specify what that part of the legend should look like and also specify that the order of the three parts of the legend should be 1. alpha, 2. linetype, and then 3. color?
You were attempting to override aesthetics for alpha in two places (ie guides() and scale_alpha...()), and ggplot was choosing to just interpret one of them. I suggest including your shape override with your legend order override, like this:
library(ggplot2)
ggplot(DF1, aes(x = X, y = Y, color = Fruit)) +
geom_point() +
geom_line(data = DF2, aes(x = X, y = Y, linetype = Cat), inherit.aes = F) +
labs(color = NULL, linetype = NULL) +
geom_point(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
geom_line(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
scale_alpha_manual(name = "Type of item", values = c(1, 1), breaks = c("Fruit", "Cat")) +
guides(alpha = guide_legend(order = 1,
override.aes=list(linetype = c("blank", "solid"),
shape = c(16,NA))),
linetype = guide_legend(order = 2),
color = guide_legend(order = 3)) +
theme_bw()
data:
DF1 <- data.frame(X = 1:10,
Y = c(1:10*0.5, 1:10*0.25),
Fruit = rep(c("mango", "kiwi"), each = 10))
DF2 <- data.frame(X = 1:10,
Y = c(1:10*2, 1:10*4),
Cat = rep(c("tabby", "calico"), each = 10))
Empty <- data.frame(X = mean(DF1$X),
Y = as.numeric(NA),
Type = c("Cat", "Fruit"))

How to differentiate groups in a geom_point plot via point types

I have a dataframe which has two different sample types (A and B). I would like to differentiate these by using different shape options. Here is a dataframe and my current attempt at performing this.
output of dput(head(df))
structure(list(Mean.Count = c(30404.8407153174, 15689.4221807262, 30404.8407153174, 15689.4221807262),
Log2FC = c(-0.00357013689574257, -0.00417251481039714, 0.306809506669248, 0.224653107007472),
Adj.P.Value = c(0.988865360408676, 0.981816989495127, 0.00202882891738576,
2.72576774009609e-05),
TimeKD = c("A", "A", "B", "B"),
Gene = c("HSPA5","MYH9", "HSPA5", "MYH9")),
row.names = c("HSPA5", "MYH9", "HSPA51", "MYH91"),
class = "data.frame")
Current attempt
ggplot(df, aes(x = Gene, y = Log2FC, group=TimeKD)) +
geom_point(aes(color = -Adj.P.Value, size = Mean.Count), alpha = 0.5)+
coord_flip() +
scale_colour_gradientn(
colours = grDevices::colorRampPalette(c("black", "cyan", "violet"))(n = 200),
values = NULL,
space = "Lab",
na.value = "grey50",
guide = "colourbar",
aesthetics = "colour"
)
Currently both A and B samples are circles. Can I use ggplot2 to change one of them into another shape?
Any help would be appreciated.
You can add shape = TimeKD to the aes of the geom_point call like this...
ggplot(df, aes(x = Gene, y = Log2FC, group=TimeKD)) +
geom_point(aes(color = -Adj.P.Value,
size = Mean.Count,
shape = TimeKD), # <-- Right here!
alpha = 0.5)+
coord_flip() +
scale_colour_gradientn(
colours = grDevices::colorRampPalette(c("black", "cyan", "violet"))(n = 200),
values = NULL,
space = "Lab",
na.value = "grey50",
guide = "colourbar",
aesthetics = "colour"
)
Which would look like this...

ggplot custom legend instead of default

I've searched and tried a bunch of suggestions to be able to display a custom legend instead of the default one in a grouped scatter ggplot. I've tried this and this and following this among others.
For instance, let's say I have a df like this one:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
and I want to display the values of each id as round dots, but also put the reference values and minimum values for each id as a differently shaped dot, as follows:
p = ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id)), shape = 19, size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id)), shape = 0, size = 8) +
geom_point(aes(x = id, y = min, color = factor(id)), shape = 2, size = 8) +
xlab("") +
ylab("Value")
#print(p)
Now all is fine, but my legend doesn't add anything to the interpretation of the plot, as the X axis and colors are enough to understand it. I know I can remove the legend via theme(legend.position = "none").
Instead, I would like to have a legend of what the actual shapes of each dot represent (e.g., filled round dot = value, triangle = min, square = ref).
Among trying to manually set the scale values via scale_fill_manual and something along those lines
override.shape = shapes$shape
override.linetype = shapes$pch
guides(colour = guide_legend(override.aes = list(shape = override.shape, linetype = override.linetype)))...
....
I've also tried making a secondary plot, but not display it, using something suggested in one of the links pasted above:
shapes = data.frame(shape = c("value", "reference", "minimum"), pch = c(19,0,2), col = c("gray", "gray", "gray"))
p2 = ggplot(shapes, aes(shape, pch)) + geom_point()
#print(p2)
g_legend <- function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)
}
legend <- g_legend(p2)
library(gridExtra)
pp <- arrangeGrob(p1 ,legend,
widths=c(5/4, 1/4),
ncol = 2)
but then I get the error:
> legend <- g_legend(p2)
Error in tmp$grobs[[leg]] :
attempt to select less than one element in get1index
for which I did not find a working solution.. so yeah.. any suggestion on how I could only show a legend related to the different dot shapes would be welcome.
Thank you
You can manually build a shape legend using scale_shape_manual:
library(ggplot2)
ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id), shape = 'value'), size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id), shape = 'ref'), size = 8) +
geom_point(aes(x = id, y = min, color = factor(id), shape = 'min'), size = 8) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
But a better way to do this would be to reshape the df to a long format, and map each aes to a variable:
library(dplyr)
library(tidyr)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
To remove the legend for the color use guide_none():
library(tidyr)
library(ggplot2)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
guides(color = guide_none()) +
xlab("") +
ylab("Value")
Created on 2020-04-16 by the reprex package (v0.3.0)
Data:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
You can tidy your data first using tidyr, and then map the aes shape to the new variable
library(tidyr)
df2 <- pivot_longer(df, -id)
ggplot(data = df2) +
geom_point(aes(x = id, y = value, shape = name), size = 6) +
xlab("") +
ylab("Value")

How to change the text and title of legend in ggplot with several variables

I'm trying to fix my legend text so that the text is representing the appropriate symbols and color. However, I have a lot of variables that I need to include in the legend, and they are all in different columns. Does anyone know a quick way to indicate what the colours and symbol are in the ggplot legend?
Here is some sample code
#sample data
temps = data.frame(Temperature= c(15,25,35),
Growth.Phase = c("exponential", "stationary", "death"),
Carbohydrates = sample(c(3:10), 9, replace = T),
Lipids = sample(c(10:25), 9, replace = T),
Chlorophyll = sample(c(2:15), 9),
DNA.RNA = sample(c(3:15), 9),
Protein = sample(c(5:20), 9))
temps$Shape = if_else(temps$Growth.Phase == "exponential", 21,
if_else(temps$Growth.Phase == "stationary", 22, 23))
#Graph code
ggplot(data = temps, aes(x = Temperature, y = "Proportions", shape = factor(Shape))) +
geom_point(aes(y = Carbohydrates),colour = "darkred",
fill = "darkred", size = 3) +
geom_line(aes(y = Carbohydrates), size = 1, col = "darkred") +
geom_point(aes(y = Lipids), colour = "darkblue",
fill = "darkblue", size = 3, col ="darkblue") +
geom_line(aes(y = Lipids), size = 1) +
geom_point(aes(y = Protein), colour = "violet",
fill = "violet", size = 3) +
geom_line(aes(y = Protein), size = 1, col ="violet") +
geom_point(aes(y = DNA.RNA), colour = "darkorange",
fill = "darkorange", size = 3) +
geom_line(aes(y = DNA.RNA), size = 1, col = "darkorange") +
geom_point(aes(y = Chlorophyll), size = 3, colour = "darkgreen",
fill = "darkgreen") +
geom_line(aes(y = Chlorophyll), size = 1, col = "darkgreen") +
labs(x = "Temperature (°C)", y = "Proportion")
This is the image I am getting
But as you can see it's not giving me the correct text in the legend. I would like the symbols to specify which Growth.Phase they are and the colour to specify what column I have plotted (ie. Carbohydrate, Protein etc....). Does anyone know a quick fix?
When I use my own data this is what the graph looks like, please note the lines are going through the same symbols, and are the same colours
I'm not sure whether I got the legend right. But the idea is the same as in #dc37's answer. Your plot can be considerably simplified using pivot_longer:
#sample data
temps = data.frame(Temperature= c(15,25,35),
Growth.Phase = c("exponential", "stationary", "death"),
Carbohydrates = sample(c(3:10), 9, replace = T),
Lipids = sample(c(10:25), 9, replace = T),
Chlorophyll = sample(c(2:15), 9),
DNA.RNA = sample(c(3:15), 9),
Protein = sample(c(5:20), 9))
library(ggplot2)
library(dplyr)
library(tidyr)
library(tibble)
temps_long <- temps %>%
pivot_longer(-c(Temperature, Growth.Phase)) %>%
mutate(
shape = case_when(
Growth.Phase == "exponential" ~ 21,
Growth.Phase == "stationary" ~ 22,
TRUE ~ 23
),
color = case_when(
name == "Carbohydrates" ~ "darkred",
name == "Lipids" ~ "darkblue",
name == "Protein" ~ "violet",
name == "DNA.RNA" ~ "darkorange",
name == "Chlorophyll" ~ "darkgreen",
TRUE ~ NA_character_
),
)
# named color vector
colors <- select(temps_long, name, color) %>%
distinct() %>%
deframe()
# named shape vector
shapes <- select(temps_long, Growth.Phase, shape) %>%
distinct() %>%
deframe()
ggplot(data = temps_long, aes(x = Temperature, y = value, shape = Growth.Phase, color = name, fill = name, group = Temperature)) +
geom_point(size = 3) +
geom_line(size = 1) +
scale_shape_manual(values = shapes) +
scale_fill_manual(values = colors) +
scale_color_manual(values = colors) +
labs(x = "Temperature (C)", y = "Proportion", color = "XXXX") +
guides(fill = FALSE, shape = guide_legend(override.aes = list(fill = "black")))
Created on 2020-04-04 by the reprex package (v0.3.0)
In order to make your code simpler and not have to repeat several times the same line, you can transform your data into a longer format and then use those new variables to attribute color, fill and shape arguments in your aes.
Then, using scale_color_manual or scale_shape_manual, you can set appropriate color and shape.
In order to add lines between appropriate points, I add a "rep" column in order to mimick the rpesence of replicate in your experiments. Otherwise, geom_line can't decide which points are associated together.
library(tidyr)
library(dplyr)
library(ggplot2)
temps %>% mutate(Rep = rep(1:3,each = 3)) %>%
pivot_longer(cols = Carbohydrates:Protein, names_to = "Type", values_to = "proportions") %>%
ggplot(aes(x = Temperature, y = proportions))+
geom_point(aes(fill = Type, shape = Growth.Phase, color = Type), size = 3)+
geom_line(aes( color = Type, group =interaction(Rep, Type)))+
scale_color_manual(values = c("darkred","darkgreen","darkorange","darkblue","violet"))+
scale_fill_manual(values = c("darkred","darkgreen","darkorange","darkblue","violet"))+
scale_shape_manual(values = c(23,21,22))+
labs(x = "Temperature (°C)", y = "Proportion")
Does it answer your question ?

add a legend to ggalt::geom_dumbbell plot AND sort y axis

In this SO answer, user #Crops shows how to add a legend to a ggalt::geom_dumbbell plot. Very nice.
library(ggalt)
df <- data.frame(trt=LETTERS[1:5], l=c(20, 40, 10, 30, 50), r=c(70, 50, 30, 60, 80))
df2 = tidyr::gather(df, group, value, -trt)
ggplot(df, aes(y = trt)) +
geom_point(data = df2, aes(x = value, color = group), size = 3) +
geom_dumbbell(aes(x = l, xend = r), size=3, color="#e3e2e1",
colour_x = "red", colour_xend = "blue",
dot_guide=TRUE, dot_guide_size=0.25) +
theme_bw() +
scale_color_manual(name = "", values = c("red", "blue") )
I want to sort trt descending on r. I tried replacing y = trt with y = reorder(trt, r), but I get an error that object r is not found.
Here is a way where we reorder the factor levels of trt in df and df2 before we plot.
# reorder factor levels
df$trt <- reorder(df$trt, df$r)
df2$trt <- factor(df2$trt, levels = levels(df$trt))
ggplot(df, aes(y = trt)) +
geom_point(data = df2, aes(x = value, color = group), size = 3) +
geom_dumbbell(aes(x = l, xend = r), size=3, color="#e3e2e1",
colour_x = "red", colour_xend = "blue",
dot_guide=TRUE, dot_guide_size=0.25) +
theme_bw() +
scale_color_manual(name = "", values = c("red", "blue") )
Using the dumbbell package
##Reformat data
df3<-df %>% arrange(r)
df2<-df%>% mutate("key"="trt")
df2$trt<-factor(df2$trt,df3$trt)
##plot
dumbbell::dumbbell(df2, id="trt", column1="l", column2="r",key="key", delt =1, textsize=3, lab1 = "l", lab2="r", pt_val = 1, pointsize = 3,pt_alpha = 0.6, arrow=1, leg = "Add legend title", pval=2) + xlim(8,85) + facet_wrap(key ~.)
Added in some bells and whistles, you can remove them toggling with the options.
I dont have enough points to embed for here is the link. Hope someone finds it useful.

Resources