I've written this code:
ggplot() +
geom_sf(aes(fill = dat$color_province)) +
theme_void() +
geom_point(data = producer,
aes(x = producer$MX, y = producer$MY), size = 3, col = "green", shape = 17, alpha = 0.6) +
geom_point(data = distribution,
aes(x = distribution$MX, y = distribution$MY), size = 4.5, col = "yellow", shape = 15) +
geom_point(data = retailer,
aes(x = retailer$MX, y = retailer$MY), size = 3, col = "slateblue", shape = 16) +
geom_point(data = Demand,
aes(x = Demand$MX, y = Demand$MY, size = Demand$De), col = "slateblue", shape = 17, alpha = 0.7) +
scale_fill_manual(values = c("#ff3333", "#ffc266"),
name = "Situation")
and now I want to add a legend to identify all points in my plot. How can I do it?
Here's an example on some data that everyone can run, since it uses built-in datasets that come with R. Here, I made color and size be dynamic aesthetics with the name of the series, and then mapped those series values to different aesthetic values using scale_*_manual, where * are the aesthetics you want to vary by series. This generates an automatic legend. By giving each aesthetic the same name ("source" here), ggplot2 knows to combine them into one legend.
(By the way, it's unnecessary and can lead to errors to refer to variables in ggplot2 aesthetics using the form retailer$MY; each geom will assume the variable is within the data frame referred to with data =, so you can just use MY in that case.)
ggplot() +
geom_point(data = mtcars,
aes(x = wt, y = mpg, color = "mtcars", size = "mtcars")) +
geom_point(data = attitude,
aes(x = rating/20, y = complaints/3, color = "attitude", size = "attitude")) +
scale_color_manual(values = c("mtcars" = "slateblue", "attitude" = "red"), name = "source") +
scale_size_manual(values = c("mtcars" = 3, "attitude" = 4.5), name = "source")
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
I want to use ggplot to show points and lines, but I want there to be two legends - one for the points and one for the lines.
I managed to do this using the below code, but for some reason the 'size' option no longer responds in geom_point and they are stuck at the fairly ugly size you can see in the image
.
Note that I chose stroke = NA because I do not want the points to have a border. The code is below.
Any ideas?
ggplot(data = plot_data) +
geom_point(aes(x = z.1, y = obs, fill = treatcat), alpha = 0.4, shape = 21, stroke = NA, size = 1) +
geom_line(aes(x = z.1, y = under, colour = "True"), linetype = "dashed") +
geom_line(aes(x = z.1, y = crude, colour = "Crude"), size = 1.5) +
scale_fill_manual(name = "Treatment",
values = c("0" = "#F8766D", "1" = "#C77CFF"),
breaks = c("0", "1"),
labels = c("Untreated", "Treated")) +
scale_colour_manual(name = "Model",
values = c("Crude" = "orange", "True" = "black"),
breaks = c("Crude", "True"),
labels = c("Crude", "True")) +
ylim(-30,27.5) +
theme(plot.title = element_text(size = "12")) +
labs(title = "Fitted Values for Crude Model", x = "Z", y = "Y(1)")
Maybe you want two color scales, here a solution with ggnewscale. There are a couple of github packages with similar functionality on the horizon (relayer, and ggh4x), but currently this is the only CRAN option to my knowledge.
As per comment - I am using see::geom_point2 because I also don't like those strokes
library(ggplot2)
library(see)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point2(aes(color = Petal.Width), alpha = 0.4, size = 10) +
ggnewscale::new_scale_color() +
geom_smooth(aes(color = Species), linetype = "dashed", method = "lm")
Currently, there is a bug in ggplot2 that makes it impossible to change size once stroke = NA (https://github.com/tidyverse/ggplot2/issues/4624). Apprarently, setting 'stroke = 0' also does not eliminate the border.
To do what you want, you need to set set color to 'transparent':
library(ggplot2)
df = data.frame(x=rnorm(100), y=rnorm(100))
ggplot(df, aes(x, y)) + geom_point(shape=21, stroke=0, fill="orange", color="transparent", size=8)
Created on 2021-09-20 by the reprex package (v2.0.1)
Please consider the following:
I want to plot a step-wise curve (using geom_step()) and some smooth lines (using geom_line()) in one graph using ggplot2.
I manage to create a graph but the labels are wrong and cannot be corrected using scale_color_discrete().
Desired outcome: Based on the data (see below), line "hello" is the upper line, followed by "foo" and "bar", but the labels are not correct. In addition, I also need a label for the, now missing, geom_step()curve.
Question: What am I doing wrong?
Reproducible example:
library(ggplot2)
# Data
db <- data.frame(time = 0:100,
step = 1-pexp(0:100, rate = 1),
foo = 1-pexp(0:100, rate = 0.4),
bar = 1-pexp(0:100, rate = 0.5),
hello = 1-pexp(0:100, rate = 0.1)
)
# Plotted with wrong labels (automatically)
ggplot(data = db, aes(x = time, y = step)) +
geom_step(show.legend = T) +
geom_line(aes(x = time, y = foo, col = "red")) +
geom_line(aes(x = time, y = bar, col = "blue")) +
geom_line(aes(x = time, y = hello, col = "green"))
Looking at the labels, one can already see that the description of the color and the color of the line do not match.
# Still wrong labels
ggplot(data = db, aes(x = time, y = step)) +
geom_step(show.legend = T) +
geom_line(aes(x = time, y = foo, col = "red")) +
geom_line(aes(x = time, y = bar, col = "blue")) +
geom_line(aes(x = time, y = hello, col = "green")) +
scale_color_discrete(name = "Dose", labels = c("foo", "bar", "hello"))
Changing the labels obviously wont help.
Created on 2019-04-15 by the reprex
package (v0.2.0).
You are specifying the color you want to have inside the aesthetics-call. This means you match the color to the label "red" and not use the color "red".
You can fix this for example like this:
p <- ggplot(data = db, aes(x = time, y = step)) +
geom_step(aes(color = "step")) +
geom_line(aes(y = foo, color = "foo")) +
geom_line(aes(y = bar, color = "bar")) +
geom_line(aes(y = hello, color = "hello"))
p
Note that I dropped the x = time as this is inherited from the ggplot-call in each step. If you want to change the color for each of the lines, you should now use for example scale_color_manual like the following:
p +
scale_color_manual(name = "Dose",
values = c("step" = "black", "foo" = "red",
"bar" = "blue", "hello" = "green"))
Another option would be to transform you data to the long format:
library(tidyr)
library(dplyr)
new_db <- gather(db, type, value, -time)
ggplot(data = filter(new_db, type != "step"), aes(x = time, y = value, color = type)) +
geom_line() +
geom_step(data = filter(new_db, type == "step"))
I woul like to be able to make the geom_text inside the geom_point to follow the re-positioning when applying position_dodge. That is, I would like to go from the code below:
Q <- as_tibble(data.frame(series = rep(c("diax","diay"),3),
value = c(3.25,3.30,3.31,3.36,3.38,3.42),
year = c(2018,2018,2019,2019,2020,2020))) %>%
select(year, series, value)
ggplot(data = Q, mapping = aes(x = year, y = value, color = series, label = sprintf("%.2f",value))) +
geom_point(size = 13) +
geom_text(vjust = 0.4,color = "white", size = 4, fontface = "bold", show.legend = FALSE)
which produces the following chart:
to the following change:
ggplot(data = Q, mapping = aes(x = year, y = value, color = series, label = sprintf("%.2f",value))) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(position = position_dodge(width = 1), vjust = 0.4,
color = "white", size = 4, fontface = "bold",
show.legend = FALSE)
which produces the following chart:
The curious thing about this is the fact that excatly the same change works just fine if I change from geom_point to geom_bar:
ggplot(Q, aes(year, value, fill = factor(series), label = sprintf("%.2f",value))) +
geom_bar(stat = "identity", position = position_dodge(width = 1)) +
geom_text(color = "black", size = 4,fontface= "bold",
position = position_dodge(width = 1), vjust = 0.4, show.legend = FALSE)
This happens because the the dodging is based on the group aesthetic, automatically set in this case to series because of the mapping to color. The issue is that the text layer has it's own color ("white") and so the grouping is dropped. Manually set the grouping, and all is good:
ggplot(Q, aes(x = year, y = value, color = series, label = sprintf("%.2f",value), group = series)) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(position = position_dodge(width = 1), vjust = 0.4, color = "white", size = 4,
fontface = "bold", show.legend = FALSE)
One patch work would be the following. Since you cannot add labels on top of the data point using geom_text() right away, you may want to go round a bit. I first created a temporary graphic with geom_point(). Then, I accessed to the data frame which is used for drawing the graphic. You can find the values of x and y axis. Using them, I created a new data frame called temp which include the axis information and the label information. Once I had this data frame, I could draw the expected outcome using temp. Make sure that you use inherit.aes = FALSE in geom_text() since you are using another data frame.
library(dplyr)
library(ggplot2)
g <- ggplot(data = Q, aes(x = year, y = value, color = series)) +
geom_point(size = 13, position = position_dodge(width = 1))
temp <- as.data.frame(ggplot_build(g)$data) %>%
select(x, y) %>%
arrange(x) %>%
mutate(label = sprintf("%.2f",Q$value))
ggplot(data = Q, aes(x = year, y = value, color = series)) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(data = temp, aes(x = x, y = y, label = label),
color = "white", inherit.aes = FALSE)
I need help:
I cannot seem to add a legend to the following piece of code for ggplot R STDUIO
ggplot(Report_Data,
aes(x=Report_Data$Transect Point), show.legend = TRUE) +
geom_point(aes(y=Report_Data$Q1North),
shape = 6, size = 5, colour = label , show.legend = TRUE) +
geom_point(aes(y=Report_Data$Q1South),
shape = 4, size = 5, colour = label, show.legend = TRUE)+
labs(title="Density of Trees Species found North & South of the creek using two sampling methods",
y="Density in Tree Species Found", x="Transect Points",caption = "n7180853")+
geom_line(aes(y=Report_Data$Q1North, colour = Q1North),
colour = "green", size = 1, show.legend = TRUE)+
geom_line(aes(y=Report_Data$Q1South, color = Q1South),
colour = "pink4", size = 1, show.legend = TRUE)+ theme(legend.position = "right")
Hard to replicate without your data, but if you want to have your plot to have a legend for point shape, you need to include it within aes(...) for that layer. Similar with colour, or size, if you want. Then, add scale_shape_manual(...) or scale_colour_manual(...) as needed, specifying your specific values.
Here's a toy example using the default diamonds dataset.
data("diamonds")
ggplot(diamonds, aes(x = carat)) +
geom_point(data = diamonds[diamonds$cut == "Ideal",],
aes(y= price, shape = cut)) +
geom_point(data = diamonds[diamonds$cut == "Good",],
aes(y = price, shape = cut)) +
scale_shape_manual(values = c(6,4))