Removing ggplot legend symbol while retaining label - r

Example code and figure:
data <- data.frame( ID = c(LETTERS[1:26], paste0("A",LETTERS[1:26])),
Group = rep(c("Control","Treatment"),26),
x = rnorm(52,50,20),
y = rnorm(52,50,10))
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
theme(legend.text = element_text(color=c("blue","red")))
What I'm trying to solve is removing the legend symbols (the "a") and coloring the Group labels (Control and Treatment) as they appear in the plot (Blue and Red respectively).
I've tried:
geom_text(show_guide = F)
But that just removes the legend entirely.
To keep it simple I could just use annotate...but wondering if there's a legend specific solution.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8, show_guide=F) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
annotate("text",label="Control", color="blue",x=20,y=80,size=8) +
annotate("text",label="Treatment", color="Red",x=23,y=77,size=8)

Another option is to use point markers (instead of the letter "a") as the legend symbols, which you can do with the following workaround:
Remove the geom_text legend.
Add a "dummy" point geom and set the point marker size to NA, so no points are actually plotted, but a legend will be generated.
Override the size of the point markers in the legend, so that point markers will appear in the legend key to distinguish each group.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8, show.legend=FALSE) +
geom_point(size=NA) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
labs(colour="") +
guides(colour=guide_legend(override.aes=list(size=4)))

Beginning with ggplot2 2.3.2, you can specify the glyph used in the legend using the argument key_glyph:
ggplot(data, aes(x=x, y=y, label=ID, color=Group)) +
geom_text(size=8, key_glyph="point") +
scale_color_manual(values=c("blue", "red")) +
labs(color=NULL) +
theme_classic()
For a full list of glyphs, refer to the ggplot2 documentation for draw_key. Credit to R Data Berlin for alerting me to this simple solution. Emil Hvitfeldt also has a nice blog post showcasing the options.

As a quick fix you can tweak the legend key, by hard coding the info you want, although around the other way - keep the key and remove the label.
library(grid)
GeomText$draw_key <- function (data, params, size) {
txt <- ifelse(data$colour=="blue", "Control", "Treatment")
# change x=0 and left justify
textGrob(txt, 0, 0.5,
just="left",
gp = gpar(col = alpha(data$colour, data$alpha),
fontfamily = data$family,
fontface = data$fontface,
# also added 0.5 to reduce size
fontsize = data$size * .pt* 0.5))
}
And when you plot you suppress the legend labels, and make legend key a bit wider to fit text.
ggplot(data, aes(y=y,x=x, label=ID, color=Group)) +
geom_text(size=8) +
scale_color_manual(values=c("blue","red")) +
theme_classic() +
theme(legend.text = element_blank(),
legend.key.width = unit(1.5, "cm"))

Related

Add a legend for multiple geom_segments while maintaining a "solid" linetype in ggplot2

I've created a plot using geom_segment and I'd like to add a legend for the different line colours. The only solution I've found suggests modifying the linetype in aes(), but this only works for one segment - when I add a second label to another geom_segment then the linetype becomes dashed and the legend just changes to the new colour.
Here's the code for the plot:
library(ggplot2)
rib_x <- seq(1,10,0.5)
rib_ymin <- seq(3,12,0.5)
rib_ymax <- c(3.0,4.4,5.55,6.55,7.28,8.1,8.6,9.1,9.52,9.98,10.3,10.62,10.98,11.2,11.4,11.52,11.7,11.8,12)
ggplot(data.frame())+
geom_segment(aes(x=1, xend=10, y=12, yend=3),colour="dark red",size=1.5)+
geom_segment(aes(x=1, y=3,xend=10,yend=12),colour="green",size=1.5)+
stat_smooth(aes(x=rib_x,y=rib_ymax),se=FALSE,colour="dark green",size=1.5)+
xlab("Agroecological zone")+
ylab("Productivity")+
geom_segment(aes(x=0, xend = 0, y=2, yend=12), size=1.5, arrow=arrow(length=unit(0.6,"cm")))+
theme_bw()+
annotate("text", label="Arid", x=2, y=1)+
annotate("text", label="Semi-arid", x=5, y=1)+
annotate("text", label="Humid", x=8, y=1)+
theme(axis.title=element_text(size=14),axis.text=element_blank(),legend.title=element_blank(),
axis.ticks=element_blank(),panel.border=element_blank(),
panel.grid.major = element_blank(),panel.grid.minor = element_blank())
I'd like to add a legend for the three line colours, where I can also specify the text used in the legend.
Many thanks!
Move the colour aesthetics inside aes() and name them with the actual strings you want to use to label each color. Then add scale_colour_manual() at the end to get the specific colors you want. See the example below.
In the "standard" ggplot2 workflow, you would map some categorical column to colour inside aes() (e.g., ggplot(iris, aes(x=Petal.Width, y=Sepal.Width, colour=Species)) + geom_smooth(se=FALSE, method="lm")) and then the geoms (e.g., points, lines, smooths, etc.) would appear in a different color for each unique value of the categorical column that was mapped to colour. Here, we're not using a data frame, so we create "dummy" mappings to the colour aesthetic. Putting them inside aes() causes ggplot to generate the legend.
ggplot() +
geom_segment(aes(x=1, xend=10, y=12, yend=3, colour="name1"), size=1.5) +
geom_segment(aes(x=1, y=3,xend=10,yend=12, colour="name2"), size=1.5) +
stat_smooth(aes(x=rib_x,y=rib_ymax, colour="name3"),se=FALSE,size=1.5) +
labs(x="Agroecological zone", y="Productivity", colour="Type") +
geom_segment(aes(x=0, xend = 0, y=2, yend=12), size=1.5, arrow=arrow(length=unit(0.6,"cm"))) +
annotate("text", label="Arid", x=2, y=1) +
annotate("text", label="Semi-arid", x=5, y=1) +
annotate("text", label="Humid", x=8, y=1) +
theme_void() +
theme(axis.title=element_text(size=14, margin=margin(b=3)),
axis.title.y=element_text(angle=90)) +
scale_color_manual(values=c("name1"="darkred", "name2"="green", "name3"="darkgreen"))

Colours in ggplot (geom_segment)

how can color geom_segments according to a factor in the data when facet_grid is used? My approach fails, as the assignment of colors is wrong.
Here's some data:
visual_data=data.frame(Values = 10:1, Words = c("yeah","what","is","up","and","how", "are", "things","for", "you"), group = c("a","b","a","b","a","b","a","b","a","b"), importance=c("#EF2A2A","#EF2A2A", "#E4FA11", "#E4FA11", "#E4FA11", "#E4FA11","#EF2A2A","#EF2A2A","#EF2A2A", "#E4FA11"))
This code creates a plot:
graphic=ggplot(visual_data, aes(xend=Values, x=0, y=reorder(Words, Values), yend=reorder(Words, Values))) +
geom_text(aes(x=Values, label=Values, hjust=-0.3), color="#389912",family="sans") +
geom_segment(size=4,colour=visual_data$importance) +
theme(axis.text=element_text(size=10,family="sans"),axis.title=element_text(size=13,face="bold",family="sans"),strip.text.y = element_text(size=12,family="sans"), plot.title=element_text(size=14,face="bold",family="sans")) +
facet_grid(group~., scales = "free")+
theme_bw()
graphic
What can be seen is that "yeah" and "what", for instance, do not share the same bar color, although they should according to my data specification.
Has anyone a solution to this?
You need to put colour in aes() and add scale_colour_identity():
ggplot(visual_data,
aes(x=0, xend=Values, y=reorder(Words, Values), yend=reorder(Words, Values))) +
geom_text(aes(x=Values, label=Values, hjust=-0.3), color="#389912",family="sans") +
geom_segment(size=4, aes(colour=importance)) +
scale_colour_identity() +
theme(axis.text=element_text(size=10,family="sans"),
axis.title=element_text(size=13,face="bold",family="sans"),
strip.text.y = element_text(size=12,family="sans"),
plot.title=element_text(size=14,face="bold",family="sans")) +
facet_grid(group~., scales = "free")+
theme_bw()

ggplot2 legend with only one category / with only the shape and no scale

How can I have a section of the legend with only one category? I tried to mess with the override.aes without sucess. Alternatively, the desired output could be seen as a legend with only the shape but not the scale.
ggplot(iris) +
geom_point(aes(x=Sepal.Width, y=Sepal.Length, color=Species, size=Sepal.Length))+
scale_size_continuous("Legend with \n only 1 circle ",range = c(5,10))+
guides(size = guide_legend( override.aes=list(range= c(1,5))))
An illustration of the type of product I am trying to get to:
Points are scaled but the legend does not report the scale.
Just create one break in the scale. You can add a custom label to it as well (here it is ""). You can also control the size of the point in the legend with the break you choose.
The scale_color_discrete() line is there because otherwise the 1-point legend would be on top, not what you had in your desired picture.
require(ggplot2)
g <- ggplot(iris) + geom_point(aes(x = Sepal.Width, y = Sepal.Length,
color = Species, size = Sepal.Length)) +
scale_color_discrete(name = "Color") +
scale_size_continuous(name = "Legend with \n only 1 circle",
breaks = 5, labels = "")
Although #choff solution is the best solution for the example I gave, here is the slightly different one I ended up using as I needed to have control over the range size of the circles.
ggplot(iris) +
geom_point(aes(x=Sepal.Width, y=Sepal.Length, color=Species, size=Sepal.Length))+
scale_size_continuous("Legend with \n only 1 circle ",range = c(5,10), labels=c("","","","",""))+
guides(size =guide_legend( override.aes=list(size=c(4,0,0)))) +
theme(legend.key = element_blank())
From your target map chart, it looks like you want your legend to separate your chart symbols by color and shape. Size used only to set the size of the symbols. Also, your data would likely have a column for separately countries with investments from those without. So we can add a column to iris which separates the rows by two values, map color and shape to that column, and then display the legend for those two aesthetics in a single, combined legend. The code looks like:
sp <- ggplot(transform(iris, Flower_size = ifelse(Petal.Width < 1, "Small Flower","Big Flower")))
sp <- sp + geom_point(aes(x=Sepal.Width, y=Sepal.Length, fill=Flower_size, shape=Flower_size, size=Sepal.Length), colour=NA)
sp <- sp + scale_size_continuous(range = c(4,7))
sp <- sp + scale_shape_manual(values=c(21, 22))
sp <- sp + scale_fill_manual(values=c("grey", "orange"))
sp <- sp + labs(fill="", shape="")
sp <- sp + guides(size=FALSE, fill=guide_legend(override.aes=list(size=7)))
sp <- sp + theme(legend.text=element_text(size=12))
plot(sp)

How to illustrate non available data points in a different shape using ggplot2?

Is there a way to change the shape of the points for missing data in R? I am plotting .csv files like this one in a lollipop style.
Name,chr,Pos,Reads...ME_016,Reads...ME_017,Reads...ME_018,Reads...ME_019
cg01389728,chr10,6620395,33.82,41.38,41.38,38.46
cg01389728,chr10,6620410,0,-,-,-
cg01389728,chr10,6620430,0,0,-,-
cg01389728,chr10,6620447,0,-,0,-
cg01389728,chr10,6620478,0,-,-,-
cg01389728,chr10,6620510,28.33,29.85,25.64,28.13
cg01389728,chr10,6620520,0,0,-,0
cg01389728,chr10,6620531,0,-,50,-
Using ggplot2, my graphs are created with this:
dataset <-read.table("testset", sep=",",na.strings="-", header=TRUE)
dataset <- subset(dataset, select=c(-Name, -chr))
dataset <- melt(dataset, id.vars="Pos")
dataset$variable <- gsub("\\.\\.\\.","_",dataset$variable)
xaxes <- unique(dataset$Pos)
dataset$Pos <- as.factor(dataset$Pos)
ggplot(dataset, aes(x=Pos, y=variable,fill=cut(value, breaks=10))) + geom_point(size=4, shape=21) + geom_line() + scale_fill_discrete(labels=c("0-10%","10-20%","20-30%","30-40%","40-50%","50-60%","60-70%","70-80%","80-90%","90-100%")) +
xlab("CpG Positions") +
ylab("Sample") +
labs(fill="Coverage in %") +
theme_bw() +
theme(axis.text.x = element_text(angle=90, hjust=1, vjust=0.5),plot.title = element_text(vjust=2),axis.title.x = element_text(vjust=-0.5),axis.title.y = element_text(vjust=1.5))
However, I want to set the shape of the missing points ("-") in the plot to an "x", (shape=4) and show them also in the legend.
I've tried approaches like:
scale_fill_manual(values=c(value, NA))
or:
scale_shape_manual(values=c(21,4))
By default, the "-" are also shown with shape 21 and grey colour. There must be a way to manipulate this? Writing a method like this might be the trick, but how to call it for the whole column?
formas <- function(x){
+ if(is.na(x)) forma <- 4
+ if(!is.na(x)) forma <- 21
+ return(forma)
+ }
This comes pretty close, I think.
ggplot(dataset, aes(x=Pos, y=variable,
color=cut(value, breaks=10),
shape=ifelse(is.na(value),"Missing","Present"))) +
geom_point(size=4) +
geom_line() +
scale_shape_manual(name="",values=c(Missing=4,Present=19))+
scale_color_discrete(labels=c("0-10%","10-20%","20-30%","30-40%","40-50%","50-60%","60-70%","70-80%","80-90%","90-100%")) +
xlab("CpG Positions") +
ylab("Sample") +
labs(color="Coverage in %") +
theme_bw() +
theme(axis.text.x = element_text(angle=90, hjust=1, vjust=0.5),plot.title = element_text(vjust=2),axis.title.x = element_text(vjust=-0.5),axis.title.y = element_text(vjust=1.5))
Change are:
used color instead of fill, with shape=19 for points with data
added shape aesthetic to ggplot(...) call.
removed shape=21 from geom_point(...) call.
added scale_shape_manual(...) to define the shapes for Missing and Present, and turn off the guide label.
I know you wanted filled points with a black outline (it does look better), but when I tried that with the added shape aesthetic, the fill legend does not display the colors correctly. Try it yourself.
Here is another approach that comes closer to producing the graph you specified (circular points with black outline and fill color determined by coverage).
fill.colors <- hcl(h=seq(15, 375, length=11), l=65, c=100)[1:10]
ggplot(dataset, aes(x=Pos, y=variable,
fill=cut(value, breaks=10),
shape=ifelse(is.na(value),"Missing","Present"))) +
geom_point(size=4) +
geom_line() +
scale_fill_manual(name="Coverage in %",
values=fill.colors,
labels=c("0-10%","10-20%","20-30%","30-40%","40-50%","50-60%","60-70%","70-80%","80-90%","90-100%"),
drop=FALSE) +
scale_shape_manual(name="",values=c(Missing=4,Present=21),limits=c("Missing"))+
xlab("CpG Positions") +
ylab("Sample") +
labs(color="Coverage in %") +
theme_bw() +
theme(axis.text.x = element_text(angle=90, hjust=1, vjust=0.5),
plot.title = element_text(vjust=2),
axis.title.x = element_text(vjust=-0.5),
axis.title.y = element_text(vjust=1.5))+
guides(fill=guide_legend(override.aes=list(colour=fill.colors),order=1))
The problem in the other answer with using point shape 21 and the fill aesthetic is that, while the fill colors are displayed correctly in the plot, they are not displayed correctly in the legend. One way around that is to force ggplot to set the legend fill colors using
guides(fill=guide_legend(override.aes=list(colour=fill.colors),order=1))
Unfortunately, to do that you have to specify the fill colors manually (so that the actual fill and the override fill are the same). This code does that using
fill.colors <- hcl(h=seq(15, 375, length=11), l=65, c=100)[1:10]
which creates a color palette that mimics the ggplot default. You could of course use your own color palette here.
While this does come closer to your original intent, I actually think the other answer provides a better data visualization. The black outlines around the points, while "attractive", make it much more difficult to distinguish between fill colors, especially with 10 possible colors (which is at the edge of discernability anyway).
I can't see, why this is not working:
fill.colors <- hcl(h=seq(15, 375, length=11), l=65, c=100)[1:10]
ggplot(dataset, aes(x=Pos, y=variable
,color=cut(value, breaks=c(-0.01,10,20,30,40,50,60,70,80,90,100))
,shape=ifelse(is.na(value),"Missing","Present"))) +
geom_point(size=4) +
scale_shape_manual(name="",values=c("Missing"=4,"Present"=19),limits=c("Missing"))+
scale_color_manual(name="Coverage in %",
values=ifelse(is.na(dataset$value),"grey",fill.colors),
labels=c("0-10%","10-20%","20-30%","30-40%","40-50%","50-60%","60-70%","70-80%","80-90%","90-100%"),drop=FALSE) +
theme_bw() +
theme(axis.text.x = element_text(angle=90, hjust=1, vjust=0.5),
plot.title = element_text(vjust=2),
axis.title.x = element_text(vjust=-0.5),
axis.title.y = element_text(vjust=1.5)) +
xlab("CpG Positions") +
ylab("Sample") +
labs(color="Coverage in %") +
guides(fill=guide_legend(override.aes=list(colour=fill.colors),order=1))
NA values are not shown anymore with an X, and instead of displaying them in "grey", the class 90-100% will be shown in grey. No error message is shown - what is the problem?

ggplot: legend for a plot the combines bars / lines?

I have a empirical PDF + CDF combo I'd like to plot on the same panel. distro.df has columns pdf, cdf, and day. I'd like the pdf values to be plotted as bars, and the cdf as lines. This does the trick for making the plot:
p <- ggplot(distro.df, aes(x=day))
p <- p + geom_bar(aes(y=pdf/max(pdf)), stat="identity", width=0.95, fill=fillCol)
p <- p + geom_line(aes(y=cdf))
p <- p + xlab("Day") + ylab("")
p <- p + theme_bw() + theme_update(panel.background = element_blank(), panel.border=element_blank())
However, I'm having trouble getting a legend to appear. I'd like a line for the cdf and a filled block for the pdf. I've tried various contortions with guides, but can't seem to get anything to appear.
Suggestions?
EDIT:
Per #Henrik's request: to make a suitable distro.df object:
df <- data.frame(day=0:10)
df$pdf <- runif(length(df$day))
df$pdf <- df$pdf / sum(df$pdf)
df$cdf <- cumsum(df$pdf)
Then the above to make the plot, then invoke p to see the plot.
This generally involves moving fill into aes and using it in both the geom_bar and geom_line layers. In this case, you also need to add show_guide = TRUE to geom_line.
Once you have that, you just need to set the fill colors in scale_fill_manual so CDF doesn't have a fill color and use override.aes to do the same thing for the lines. I didn't know what your fill color was, so I just used red.
ggplot(df, aes(x=day)) +
geom_bar(aes(y=pdf/max(pdf), fill = "PDF"), stat="identity", width=0.95) +
geom_line(aes(y=cdf, fill = "CDF"), show_guide = TRUE) +
xlab("Day") + ylab("") +
theme_bw() +
theme_update(panel.background = element_blank(),
panel.border=element_blank()) +
scale_fill_manual(values = c(NA, "red"),
breaks = c("PDF", "CDF"),
name = element_blank(),
guide = guide_legend(override.aes = list(linetype = c(0,1))))
I'd still like a solution to the above (and will checkout #aosmith's answer), but I am currently going with a slightly different approach to eliminate the need to solve the problem:
p <- ggplot(distro.df, aes(x=days, color=pdf, fill=pdf))
p <- p + geom_bar(aes(y=pdf/max(pdf)), stat="identity", width=0.95)
p <- p + geom_line(aes(y=cdf), color="black")
p <- p + xlab("Day") + ylab("CDF")
p <- p + theme_bw() + theme_update(panel.background = element_blank(), panel.border=element_blank())
p
This also has the advantage of displaying some of the previously missing information, namely the PDF values.

Resources