This R code produces a ggplot2 graph in which the legend key contains the letter "a" repeated in red, blue and green.
x <- rnorm(9); y <- rnorm(9); s <- rep(c("F","G","K"), each = 3)
df <- data.frame(x, y, s)
require(ggplot2)
ggplot(df, aes(x = x, y = y, col = s, label = s)) +
geom_text() +
scale_colour_discrete(name = "My name", breaks = c("F","K","G"), labels = c("Fbig","Kbig","Gbig"))
I would like to replace the repeated "a" in the legend key with "F", "K" and "G".
Is this possible please? Thank you.
Adapting code for this answer:
The idea is to inhibit the geom_text legend, but to allow a legend for geom_point, but make the point size zero so the points are not visible in the plot, then set size and shape of the points in the legend in the guides statement
x <- rnorm(9); y <- rnorm(9); s <- rep(c("F","G","K"), each = 3)
df <- data.frame(x, y, s)
#
require(ggplot2)
#
ggplot(df, aes(x = x, y = y, colour = s, label = s)) +
geom_point(size = 0, stroke = 0) + # OR geom_point(shape = "") +
geom_text(show.legend = FALSE) +
guides(colour = guide_legend(override.aes = list(size = 5, shape = c(utf8ToInt("F"), utf8ToInt("K"), utf8ToInt("G"))))) +
scale_colour_discrete(name = "My name", breaks = c("F","K","G"), labels = c("Fbig","Kbig","Gbig"))
to manually rename the legend add
+ scale_x_continuous(breaks=c(x1,x2,x3), labels=c("F", "K", "G"))
where x1,x2,x3 are the point number
Related
I am confused of this problem for a long time. A simple data frame is constructed as follows
data <- data.frame(
x = 1:5,
y = 5:1,
fill = c(rep("pink", 3), rep("blue", 2)),
shape = c(rep(21, 3), rep(22, 2))
)
Suppose I wand to show the legend of the fill
uniFill <- unique(data$fill)
p <- ggplot(data,
mapping = aes(x = x,
y = y,
fill = fill)) +
geom_point(shape = data$shape) +
# show legend so that I do not call `scale_fill_identity()`
scale_fill_manual(values = uniFill,
labels = uniFill,
breaks = uniFill)
p
The graphics are OK, however, the legend is not correct
I guess, maybe different shapes (21 to 25) cannot be merged? Then, I partition the data into two subsets where the first set has shape 21 and the second has shape 22.
data1 <- data[1:3, ]
data2 <- data[4:5, ]
# > data1$shape
# [1] 21 21 21
# > data2$shape
# [1] 22 22
ggplot(mapping = aes(x = x,
y = y,
fill = fill)) +
geom_point(data = data1, shape = data1$shape) +
geom_point(data = data2, shape = data2$shape) +
scale_fill_manual(values = uniFill,
labels = uniFill,
breaks = uniFill)
Unfortunately, the legend does not change. Then, I changed the shape from a vector to a scalar, as in
ggplot(mapping = aes(x = x,
y = y,
fill = fill)) +
geom_point(data = data1, shape = 21) +
geom_point(data = data2, shape = 22) +
scale_fill_manual(values = uniFill,
labels = uniFill,
breaks = uniFill)
The legend of the fill color is correct finally...
So what happens here? Is it a bug? Is it possible to just add a single layer but with different shapes (21 to 25)?
A possible solution is that one can add component guides(), as in
p +
guides(fill = guide_legend(override.aes = list(fill = uniFill,
shape = 21)))
But I am more interested in why p does not work (legend)
The main reason your legend is not working in your first example is because you did not put your shape in the aesthetics.
I have a couple other suggestions: Do not define colors in your data frame; instead define a column to change the aesthetics using a code. Then define your fill and shape values explicitly. Each of the scales needs to have the same name - in this case "Legend."
Give this edit a try.
data <- data.frame(
x = 1:5,
y = 5:1,
fill = c(rep("p", 3), rep("b", 2))
)
uniFill <- c("p"="pink", "b"="blue")
uniShape <- c("p" = 21, "b" = 22)
p <- ggplot(data,
mapping = aes(x = x,
y = y,
fill = fill,
shape = fill)) +
geom_point() +
# show legend so that I do not call `scale_fill_identity()`
scale_fill_manual("Legend",values = uniFill,
labels = uniFill)+
scale_shape_manual("Legend",values = uniShape,
labels = uniFill)
p
(edit) If your fill and shape aesthetics do not match up, I don't see any other way than to use guides and two legends. Notice that if your attribute column is descriptive, you do not need to set the labels and your code will be cleaner (see shape vs fill aesthetics).
data <- data.frame(
x = 1:5,
y = 5:1,
fill = c(rep("p", 3), rep("b", 2)),
shape = c(rep("circles", 2), rep("squares", 3))
)
uniFill <- c("p"="pink", "b"="blue")
uniShape <- c("circles" = 21, "squares" = 22)
p <- ggplot(data,
mapping = aes(x = x,
y = y,
fill = fill,
shape = shape)) +
geom_point() +
# show legend so that I do not call `scale_fill_identity()`
scale_fill_manual("Legend fill",values = uniFill,
labels = uniFill)+
scale_shape_manual("Legend shape",values = uniShape )+
guides(fill = guide_legend("Legend fill", override.aes = list(shape = 21)))
p
Problem
In ggplot2, legends for different scales are usually integrated into a single, combined legend whenever possible. This worked fine for me so far. However, when I try parsing the scale labels to include mathematical symbols in the legend, this does not seem to work.
See this example:
# example data
d <- data.frame(x = 1:3, y = rep(0,3), f = c("a[1]", "a[2]", "a[3]"))
# plot
p <- ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
geom_point() +
guides(
color = guide_legend(title = "F"),
shape = guide_legend(title = "F")
)
The following gives the plot with custom values for shapes/colors and with the legends combined as intended.
# plot + custom shapes/colors
p +
scale_color_manual(name = "F", values = c("red", "blue", "green")) +
scale_shape_manual(name = "F", values = c(16, 15, 18))
However, when parsing the labels, the labels come out as expected, but the legends are no longer combined.
# plot + custom shapes/colors + parsed labels
parse.labels <- function(x) parse(text = x)
p +
scale_color_manual(name = "F", labels = parse.labels, values = c("red", "blue", "green")) +
scale_shape_manual(name = "F", labels = parse.labels, values = c(16, 15, 18))
Note that the result is the same with scale_._discrete instead of scale_._manual. Similarly, specifying identical names for the two scales with guides(shape = guide_legend(title = "F"), color = guide_legend(title = "F")) does not change this behavior.
Question
How can I integrate the two legends while maintaining the parsed labels?
Use scales::parse_format() instead of the parse() function from base R, and you should be fine:
library(scales)
ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
geom_point() +
scale_color_manual(name = "F",
labels = parse_format(),
values = c("red", "blue", "green")) +
scale_shape_manual(name = "F",
labels = parse_format(),
values = c(16, 15, 18))
I think this has something to do with how parse returns an expression tagged with automatically-generated srcfile / wholeSrcref attributes by default, while parse_format does not. These additional attributes prevent the two scales from being merged together, since they are not identical.
(Using function(x) parse(x = text, srcfile = NULL) in both scales will also work, same as above, but I find the function from scales to be less verbose.)
I would suggest this approach using the labels argument in scale_*_discrete() and saving your values for labels in a new vector:
library(ggplot2)
# example data
d <- data.frame(x = 1:3, y = rep(0,3), f = c("a[1]", "a[2]", "a[3]"))
#Labs
lab1 <- c(expression(a[1]),
expression(a[2]),
expression(a[3]))
# plot
ggplot(data = d, aes(x = x, y = y, color = f, shape = f)) +
geom_point() +
guides(
color = guide_legend(title = "F"),
shape = guide_legend(title = "F")
)+
scale_color_discrete(labels = lab1) +
scale_shape_discrete(labels = lab1)
Output:
As you can see on the image, R automatically assigns the values 0, 0.25... 1 for the size of the point. I was wondering if I could replace the 0, 0.25... 1 and make these text values instead while keeping the actual numerical values from the data.
library(ggplot2)
library(scales)
data(SLC4A1, package="ggplot2")
SLC4A1 <- read.csv(file.choose(), header = TRUE)
# bubble chart showing position of polymorphisms on gene, the frequency of each of these
# polymorphisms, where they are prominent on earth, and p-value
SLC4A1ggplot <- ggplot(SLC4A1, aes(Position, log10(Frequency)))+
geom_jitter(aes(col=Geographical.Location, size =(p.value)))+
labs(subtitle="Frequency of Various Polymorphisms", title="SLC4A1 Gene") +
labs(color = "Geographical Location") +
labs(size = "p-value") + labs(x = "Position of Polymorphism on SLC4A1 Gene") +
scale_size_continuous(range=c(1,4.5), trans = "reverse") +
guides(size = guide_legend(reverse = TRUE))
library(tidyver)
df <- data.frame(x = 1:5, y = 1:5,z = 1:5)
ggplot(df,aes(x = x, y = y, size = z)) +
geom_point()
ggplot(df,aes(x = x, y = y, size = z)) +
geom_point() +
scale_size_continuous(range = 1:2) # control range of circle size
See more here:
https://ggplot2.tidyverse.org/reference/scale_size.html
I have a time series with different variables and different units that I want to display on the same plot.
ggplot does not support multiple axis (as explained here), so I followed the advice and tried to plot the curves with facets:
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi/4)
y3 <- cos(x)
my.df <- data.frame(time = x, currentA = y1, currentB = y2, voltage = y3)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
ggplot(my.df, aes(x = time, y = value)) + geom_line(aes(color = variable)) + facet_wrap(~Unit, scales = "free_y", nrow = 2)
Here is the result:
The thing is that there is only one y label, saying "value" and I would like two: one with "Currents (A)" and the other one with "Voltage (V)".
Is this possible?
In ggplot2_2.2.1 you could move the panel strips to be the y axis labels by using the strip.position argument in facet_wrap. Using this method you don't have both strip labels and different y axis labels, though, which may not be ideal.
Once you've put the strip labels to be on the y axis (the "left"), you can change the labels by giving a named vector to labeller to be used as a look-up table.
The strip labels can be moved outside the y-axis via strip.placement in theme.
Remove the strip background and y-axis labels to get a final graphic with two panes and distinct y-axis labels.
ggplot(my.df, aes(x = time, y = value) ) +
geom_line( aes(color = variable) ) +
facet_wrap(~Unit, scales = "free_y", nrow = 2,
strip.position = "left",
labeller = as_labeller(c(A = "Currents (A)", V = "Voltage (V)") ) ) +
ylab(NULL) +
theme(strip.background = element_blank(),
strip.placement = "outside")
Removing the strip from the top makes the two panes pretty close together. To change the spacing you can add, e.g., panel.margin = unit(1, "lines") to theme.
Here is a manual solution, this question can be solved faster and more easily with other methods:
Working with margins and labs will permit you to stick the 2 plot if you really need it.
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi/4)
y3 <- cos(x)
my.df <- data.frame(time = x, currentA = y1, currentB = y2, voltage = y3)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
# Create 3 plots :
# A: currentA and currentB plot
A = ggplot(my.df, aes(x = time, y = value, color = variable, alpha = variable)) +
geom_line() + ylab("A") +
scale_alpha_manual(values = c("currentA" = 1, "currentB" = 1, "voltage" = 0)) +
guides(alpha = F, color = F)
# B: voltage plot
B = ggplot(my.df, aes(x = time, y = value, color = variable, alpha = variable)) +
geom_line() + ylab("A") +
scale_alpha_manual(values = c("currentA" = 0, "currentB" = 0, "voltage" = 1)) +
guides(alpha = F, color = F)
# C: get the legend
C = ggplot(my.df, aes(x = time, y = value, color = variable)) + geom_line() + ylab("A")
library(gridExtra)
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
# use this trick to get the legend as a grob object
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]]
legend
}
#extract legend from C plot
legend = g_legend(C)
#arrange grob (the 2 plots)
plots = arrangeGrob(A,B)
# arrange the plots and the legend
grid.arrange(plots, legend , ncol = 2, widths = c(3/4,1/4))
Super late entry, but just solved this for myself...A super simple hack that lets you retain all strip.text is to just enter a bunch of spaces in ylab(" Voltage (V) Current (A)\n") in order to put the title across both plots. You can left-justify using axis.title.y = element_text(hjust = 0.25) to align everything well.
library(tidyverse)
library(reshape2)
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi / 4)
y3 <- cos(x)
my.df <-
data.frame(
time = x,
currentA = y1,
currentB = y2,
voltage = y3
)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
ggplot(my.df, aes(x = time, y = value)) +
geom_line(aes(color = variable)) +
facet_wrap( ~Unit, scales = "free_y", nrow = 2) +
ylab(" Voltage (V) Current (A)\n") +
theme(
axis.title.y = element_text(hjust = 0.25)
)
ggsave("Volts_Amps.png",
height = 6,
width = 8,
units = "in",
dpi = 600)
I am trying to create a legend when using ggplot2 and geom_point. I have a dataframe that looks like this
d <- data.frame(variable = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J"),
value = rnorm(10, 3),
Schoolave = rnorm(10, 3),
districtave = rnorm(10, 3),
max = rnorm(10, 3),
min = rnorm(10, 3))
I want to make a plot that looks like this.
plot <- ggplot(data = d, aes(x = variable, y = value)) + geom_errorbar(ymax = d$max, ymin = d$min)
plot <- plot + coord_flip()
plot <- plot + geom_point(data = d, aes(x = variable, y = value),
shape = 1, size = 5)
plot <- plot + geom_point(data = d, aes(x = variable, y = districtave), shape = 0, size = 4)
plot <- plot + geom_point(data = d, aes(x = variable, y = Schoolave), shape = 2, size = 3)
plot <- plot + theme_bw() + theme(legend.position= "bottom")
plot
I would like a legend that tells them the circle for your average score. The triangle is the average score for your school. The square is the average for the district. I have looked for a way to do this can't find a way. Any help would be appreciated.
ggplot likes tidy data, which means you have to melt your data into long format.
library(reshape2)
d.points = melt(d[, c("variable", "value", "Schoolave", "districtave")],
id = "variable",
variable.name = "type")
Now your data has a single column that we'll map to shape, so the auto legend will work.
And there's no need to add to the same plot and save it every single line, just add it all at once.
And please don't specify the data$column inside of aes()! It will cause problems if you ever want to facet or do more advanced plots. You specify the data upfront, so you don't have to say it again inside aes(). And do use aes() for all your aesthetic mappings, like the min and max of the errorbar.
plot <- ggplot(data = d, aes(x = variable, y = value)) +
geom_errorbar(aes(ymax = max, ymin = min)) +
coord_flip() +
geom_point(data = d.points,
aes(x = variable, y = value, shape = type),
size = 5) +
scale_shape_manual(values = 1:3, name = "") +
theme_bw() +
theme(legend.position= "bottom")
plot
If you want nicer labels, you can specify them in the shape scale. On a horizontal legend, I like to add in a little white space, something like this:
scale_shape_manual(values = 1:3, name = "",
labels = c("Individual Average ", "School Average ", "District Average ")) +