With ggplot2 I normally expect to be able to add a data point like so,
ggtern(df, aes(X, Y, Z, value = VALUE), aes(x, y, z)) +
geom_point(aes(fill = VALUE), size = 2, stroke = 0, shape = 21) +
scale_fill_gradient(low = "red",high = "yellow", guide = F) +
scale_color_gradient(low = "red",high = "yellow", guide = F) +
geom_point(aes(x = 10, y = 10, z = 50), shape = 21)
However, when using the ggtern package to generate a ternary diagram they are being inserted into the wrong locations (see example image) with the following warning:
Warning: Ignoring unknown aesthetics: z
This implies that ggplot2 is likely attempting to render the point and not ggtern. How can I add specific, labeled points to the ggtern plot?
It appears that there are two main points for this. The first is to create an annotation, although that might not be ideal since it is not as precise as a point. An example would be,
ggtern() +
annotate(geom = 'text',
x = c(0.5,1/3,0.0),
y = c(0.5,1/3,0.0),
z = c(0.0,1/3,1.0),
angle = c(0,30,60),
vjust = c(1.5,0.5,-0.5),
label = paste("Point",c("A","B","C")),
color = c("green","red",'blue')) +
theme_dark() +
theme_nomask()
The second option would be to create a new data frame and add that to the plot. While this has the advantage of having more control over the point, the disadvantage is that labeling will require additional work.
One possibility is to have a column that identifies the points you wish to label, in this example the column 'lab' and say I want to label points one and three:
df <- data.frame(x=c(10,20,30), y=c(15,25,35), z=c(75,55,35), VALUE=c(1,2,3), lab=c("One", "", "Three"))
Then geom_text or geom_label can be used to label those specific points, for example:
ggtern(df, aes(x, y, z, value = VALUE)) +
geom_point(aes(fill = VALUE), size = 2, stroke = 0, shape = 21) +
scale_fill_gradient(low = "red",high = "yellow", guide = F) +
scale_color_gradient(low = "red",high = "yellow", guide = F) +
geom_text(aes(label = lab), vjust=1)
Related
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")
I have the following code:
library(ggplot2)
library(RColorBrewer)
colours <-brewer.pal(n = 3, name = 'Paired')
ids <- c("TestA", "TestB", "TestC")
bg <-c(23, 13, 15)
s1 <- c(21,15,17)
s2 <- c(27,25,11)
s3 <- c(24,14,18)
df <- data.frame(ids, bg, s1,s2,s3)
colors <- c("bgs"= "grey", "tid1"=colours[1], "tid2"=colours[2], "tid3"=colours[3])
ggplot(df, aes(x = ids)) +
geom_col(aes(y = bg, color = 'bgs'), fill = 'grey', size = 1, ) +
geom_point(aes(y = s1, color= 'tid1'), size = 10, group = 1) +
geom_point(aes(y = s2, color= 'tid2'), size = 10, group = 1) +
geom_point(aes(y = s3, color='tid3'), size = 10, group = 1)+
labs(x = "Year",
y = "(%)",
color = "Legend") +
scale_color_manual(values = colors)+
theme(legend.position="bottom")
This code produces the following graph:
The issue here is with the legend, i have used the fill = 'grey' in order to fill the bar chart in geom_col(). However you can see that this now produces a grey box over which the other elements in the legend are placed. Is there a way to show just the gray box for the bg data, and show colored circles only for the other data in the legend
Correct me if I'm wrong but I think you just want to remove the graph box in the legend that shows nothing? I'd ask in a comment but I don't have enough reputation yet :(
This is happening because you are specifying color = inside the aes() argument. The easiest way to get rid of this is to get rid of color = bgs.
Since you are working with a barchart, what color = does is add a 'border' of specified color around your graph. Whereas fill = is the color that actually fills in the color of the bars. So here is what the code and the graph would look like:
library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 3.6.3
library(RColorBrewer)
colours <-brewer.pal(n = 3, name = 'Paired')
ids <- c("TestA", "TestB", "TestC")
bg <-c(23, 13, 15)
s1 <- c(21,15,17)
s2 <- c(27,25,11)
s3 <- c(24,14,18)
df <- data.frame(ids, bg, s1,s2,s3)
colors <- c("bgs"= "grey", "tid1"=colours[1], "tid2"=colours[2], "tid3"=colours[3])
ggplot(df, aes(x = ids)) +
geom_col(aes(y = bg), fill = 'grey', size = 1) +
geom_point(aes(y = s1, color = 'tid1'), size = 10, group = 1) +
geom_point(aes(y = s2, color = 'tid2'), size = 10, group = 1) +
geom_point(aes(y = s3, color = 'tid3'), size = 10, group = 1)+
labs(x = "Year",
y = "(%)",
color = "Legend") +
scale_color_manual(values = colors)+
theme(legend.position="bottom")
Created on 2020-09-16 by the reprex package (v0.3.0)
There are other ways to do this too, including override.aes() (I think). If you are curious take a look at Hadley Wickham's book on ggplot2. There is a free pdf here: ggplot2 book
I think you would want to look at chapter 6 which covers the legends
I have a chart that shows mobile usage by operating system. I'd like to add vertical lines to identify when those operating systems were released. I'll go through the chart and then the code.
The chart -
The code -
dev %>%
group_by(os) %>%
mutate(monthly_change = prop - lag(prop)) %>%
ggplot(aes(month, monthly_change, color = os)) +
geom_line() +
geom_vline(xintercept = as.numeric(ymd("2013-10-01"))) +
geom_text(label = "KitKat", x = as.numeric(ymd("2013-10-01")) + 80, y = -.5)
Instead of adding the text in the plot, I'd like to create a legend to identify each of the lines. I'd like to give each of them its own color and then have a legend to identify each. Something like this -
Can I make my own custom legend like that?
1) Define a data frame that contains the line data and then use geom_vline with it. Note that BOD is a data frame that comes with R.
line.data <- data.frame(xintercept = c(2, 4), Lines = c("lower", "upper"),
color = c("red", "blue"), stringsAsFactors = FALSE)
ggplot(BOD, aes( Time, demand ) ) +
geom_point() +
geom_vline(aes(xintercept = xintercept, color = Lines), line.data, size = 1) +
scale_colour_manual(values = line.data$color)
2) Alternately put the labels right on the plot itself to avoid an extra legend. Using the line.data frame above. This also has the advantage of avoiding possible multiple legends with the same aesthetic.
ggplot(BOD, aes( Time, demand ) ) +
geom_point() +
annotate("text", line.data$xintercept, max(BOD$demand), hjust = -.25,
label = line.data$Lines) +
geom_vline(aes(xintercept = xintercept), line.data, size = 1)
3) If the real problem is that you want two color legends then there are two packages that can help.
3a) ggnewscale Any color geom that appears after invoking new_scale_color will get its own scale.
library(ggnewscale)
BOD$g <- gl(2, 3, labels = c("group1", "group2"))
line.data <- data.frame(xintercept = c(2, 4), Lines = c("lower", "upper"),
color = c("red", "blue"), stringsAsFactors = FALSE)
ggplot(BOD, aes( Time, demand ) ) +
geom_point(aes(colour = g)) +
scale_colour_manual(values = c("red", "orange")) +
new_scale_color() +
geom_vline(aes(xintercept = xintercept, colour = line.data$color), line.data,
size = 1) +
scale_colour_manual(values = line.data$color)
3b) relayer The experimental relayer package (only on github) allows one to define two color aethetics, color and color2, say, and then have separate scales for each one.
library(dplyr)
library(relayer)
BOD$g <- gl(2, 3, labels = c("group1", "group2"))
ggplot(BOD, aes( Time, demand ) ) +
geom_point(aes(colour = g)) +
geom_vline(aes(xintercept = xintercept, colour2 = line.data$color), line.data,
size = 1) %>% rename_geom_aes(new_aes = c("colour" = "colour2")) +
scale_colour_manual(aesthetics = "colour", values = c("red", "orange")) +
scale_colour_manual(aesthetics = "colour2", values = line.data$color)
You can definitely make your own custom legend, but it is a bit complicated, so I'll take you through it step-by-step with some fake data.
The fake data contained 100 samples from a normal distribution (monthly_change for your data), 5 groupings (similar to the os variable in your data) and a sequence of dates from a random starting point.
library(tidyverse)
library(lubridate)
y <- rnorm(100)
df <- tibble(y) %>%
mutate(os = factor(rep_len(1:5, 100)),
date = seq(from = ymd('2013-01-01'), by = 1, length.out = 100))
You already use the colour aes for your call to geom_line, so you will need to choose a different aes to map onto the calls to geom_vline. Here, I use linetype and a call to scale_linetype_manual to manually edit the linetype legend to how I want it.
ggplot(df, aes(x = date, y = y, colour = os)) +
geom_line() +
# set `xintercept` to your date and `linetype` to the name of the os which starts
# at that date in your `aes` call; set colour outside of the `aes`
geom_vline(aes(xintercept = min(date),
linetype = 'os 1'), colour = 'red') +
geom_vline(aes(xintercept = median(date),
linetype = 'os 2'), colour = 'blue') +
# in the call to `scale_linetype_manual`, `name` will be the legend title;
# set `values` to 1 for each os to force a solid vertical line;
# use `guide_legend` and `override.aes` to change the colour of the lines in the
# legend to match the colours in the calls to `geom_vline`
scale_linetype_manual(name = 'lines',
values = c('os 1' = 1,
'os 2' = 1),
guide = guide_legend(override.aes = list(colour = c('red',
'blue'))))
And there you go, a nice custom legend. Please do remember next time that if you can provide your data, or a minimally reproducible example, we can better answer your question without having to generate fake data.
I have data that looks like this:
example.df <- as.data.frame(matrix( c("height","fruit",0.2,0.4,0.7,
"height","veggies",0.3,0.6,0.8,
"height","exercise",0.1,0.2,0.5,
"bmi","fruit",0.2,0.4,0.6,
"bmi","veggies",0.1,0.5,0.7,
"bmi","exercise",0.4,0.7,0.8,
"IQ","fruit",0.4,0.5,0.6,
"IQ","veggies",0.3,0.5,0.7,
"IQ","exercise",0.1,0.4,0.6),
nrow=9, ncol=5, byrow = TRUE))
colnames(example.df) <- c("phenotype","predictor","corr1","corr2","corr3")
So basically three different correlations between 3x3 variables. I want to visualize the increase in correlations as follows:
ggplot(example.df, aes(x=phenotype, y=corr1, yend=corr3, colour = predictor)) +
geom_linerange(aes(x = phenotype,
ymin = corr1, ymax = corr3,
colour = predictor),
position = position_dodge(width = 0.5))+
geom_point(size = 3,
aes(x = phenotype, y = corr1, colour = predictor),
position = position_dodge(width = 0.5), shape=4)+
geom_point(size = 3,
aes(x = phenotype, y = corr2, colour = predictor),
position = position_dodge(width = 0.5), shape=18)+
geom_point(size = 3,
aes(x = phenotype, y = corr3, colour = predictor),
position = position_dodge(width = 0.5))+
labs(x=NULL, y=NULL,
title="Stackoverflow Example Plot")+
scale_colour_manual(name="", values=c("#4682B4", "#698B69", "#FF6347"))+
theme_minimal()
This gives me the following plot:
Problems:
Tthere is something wrong with the way the geom_point shapes are dodged with BMI and IQ. They should be all with on the line with the same colour, like with height.
How do I get an extra legend that can show what the circle, cross, and square represent? (i.e., the three different correlations shown on the line: cross = correlation 1, square = correlation 2, circle = correlation 3).
The legend now shows a line, circle, cross through each other, while just a line for the predictors (exercise, fruit, veggies) would suffice..
Sorry for the multiple issues, but adding the extra legend (problem #2) is the most important one, and I would be already very satisfied if that could be solved, the rest is bonus! :)
See if the following works for you? The main idea is to convert the data frame from wide to long format for the geom_point layer, and map correlation as a shape aesthetic:
example.df %>%
ggplot(aes(x = phenotype, color = predictor, group = predictor)) +
geom_linerange(aes(ymin = corr1, ymax = corr3),
position = position_dodge(width = 0.5)) +
geom_point(data = . %>% tidyr::gather(corr, value, -phenotype, -predictor),
aes(y = value, shape = corr),
size = 3,
position = position_dodge(width = 0.5)) +
scale_color_manual(values = c("#4682B4", "#698B69", "#FF6347")) +
scale_shape_manual(values = c(4, 18, 16),
labels = paste("correlation", 1:3)) +
labs(x = NULL, y = NULL, color = "", shape = "") +
theme_minimal()
Note: The colour legend is based on both geom_linerange and geom_point, hence the legend keys include both a line and a point shape. While it's possible to get rid of the second one, it does take some more convoluted code, and I don't think the plot would be much improved as a result...
My question is very similar to this and this and also this question. I have a scatterplot (using geom_point) coloured by a factor, using a particular colour palette. I'm using stat_smooth to draw certain smoothing lines through the points, grouped by another factor. I'd like these lines to use a different colour palette.
Here is a Dropbox link to some example data. Just do a
currDT <- read.table("SO_data", sep = "|", header = TRUE, strip.white = TRUE)
I usually have my data in a data.table, so you might find it helpful change that as well. Oh and here is the colour scheme I'm using at the moment, you can use scale_colour_brewer to generate your own, I'm just including this for completeness.
my_col_scheme <- c("#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#7B8A7B",
"#0B29D6", "#f781bf", "#999999", "black")
Hopefully that is clear enough. Here is some example code:
icorr_elec <- ggplot(currDT,
aes(x = EFP, y = SAPT), na.rm = TRUE) +
geom_point(aes(colour = Anion, shape = Cation), size = 3, alpha = 0.4) +
scale_colour_manual(values = my_col_scheme) +
stat_smooth(method = "lm", formula = y ~ x + 0, aes(linetype = Halide, colour = Halide),
alpha = 0.8, size = 0.5, level = 0) +
scale_linetype_manual(name = "", values = c("dotdash", "F1"),
breaks = c("hal", "non-hal"), labels = c("Halides", "Non-Halides"))
How can this be done in ggplot2? From the other questions I gathered I could specify each line manually, but I'd like to avoid that.
You can get separate color mappings for the lines and the points by using a filled point marker for the points and mapping that to the fill aesthetic, while keeping the lines mapped to the colour aesthetic. Filled point markers are those numbered 21 through 25 (see ?pch). Here's an example, adapting #RichardErickson's code:
ggplot(currDT, aes(x = EFP, y = SAPT), na.rm = TRUE) +
stat_smooth(method = "lm", formula = y ~ x + 0,
aes(linetype = Halide, colour = Halide),
alpha = 0.8, size = 0.5, level = 0) +
scale_linetype_manual(name = "", values = c("dotdash", "F1"),
breaks = c("hal", "non-hal"), labels = c("Halides", "Non-Halides")) +
geom_point(aes(fill = Anion, shape = Cation), size = 3, alpha = 0.4, colour="transparent") +
scale_colour_manual(values = c("blue", "red")) +
scale_fill_manual(values = my_col_scheme) +
scale_shape_manual(values=c(21,24)) +
guides(fill = guide_legend(override.aes = list(colour=my_col_scheme[1:8],
shape=15, size=3)),
shape = guide_legend(override.aes = list(shape=c(21,24), fill="black", size=3)),
colour = guide_legend(override.aes = list(linetype=c("dotdash", "F1"))),
linetype = FALSE)
Here's an explanation of what I've done:
In geom_point, change colour aesthestic to fill. Also, put colour="transparent" outside of aes. That will get rid of the border around the points. If you want a border, set it to whatever border color you prefer.
In scale_colour_manual, I've set the colors to blue and red, but you can, of course, set them to whatever you prefer.
Add scale_fill_manual to set the colors of the points using the fill aesthetic.
Add scale_shape_manual and set the values to 21 and 24 (filled circles and triangles, respectively).
All the stuff inside guides() is to modify the legend. I'm not sure why, but without these overrides, the legends for fill and shape are blank. Note that I've set fill="black" for the shape legend, but it's showing up as gray. I don't know why, but without fill="somecolor" the shape legend is blank. Finally, I overrode the colour legend in order to include the linetype in the colour legend, which allowed me to get rid of the redundant linetype legend. I'm not totally happy with the legend, but it was the best I could come up with without resorting to special-purpose grobs.
NOTE: I changed color=NA to color="transparent", as color=NA (in version 2 of ggplot2) causes the points to disappear completely, even if you use a point with separate border and fill colors. Thanks to #aosmith for pointing this out.
I don't think ggplot2 will let you change color twice and update the legend. I seem to recall reading that you cannot change scale_color_manual twice in a plot. I couldn't find that webpage, but this post touches on the topic. I also tried using the suggestions in this post, but that didn't work. Probably because we're trying to mix geoms (but that's just a guess).
I can get either the regression lines colored:
part1 <-
ggplot(currDT,
aes(x = EFP, y = SAPT), na.rm = TRUE) +
stat_smooth(method = "lm", formula = y ~ x + 0,
aes(linetype = Halide, colour = Halide),
alpha = 0.8, size = 0.5, level = 0) +
scale_linetype_manual(name = "", values = c("dotdash", "F1"),
breaks = c("hal", "non-hal"),
labels = c("Halides", "Non-Halides")) +
scale_color_manual(name = "", values = c("red", 'blue'),
labels = c("Halides", "Non-Halides"))
ggsave('part1.jpeg', part1)
Or the data points to plot:
part2 <-
ggplot(currDT,
aes(x = EFP, y = SAPT), na.rm = TRUE) +
geom_point(aes(color = Anion, shape = Cation), size = 3, alpha = 0.4) +
scale_linetype_manual(name = "", values = c("dotdash", "F1"),
breaks = c("hal", "non-hal"),
labels =c("Halides", "Non-Halides")) +
scale_colour_manual(values = my_col_scheme)
ggsave('part2.jpeg', part2)
But not both:
both <-
ggplot(currDT,
aes(x = EFP, y = SAPT), na.rm = TRUE) +
stat_smooth(method = "lm", formula = y ~ x + 0,
aes(linetype = Halide, colour = Halide),
alpha = 0.8, size = 0.5, level = 0) +
scale_linetype_manual(name = "", values = c("dotdash", "F1"),
breaks = c("hal", "non-hal"), labels = c("Halides", "Non-Halides")) +
geom_point(aes(color = Anion, shape = Cation), size = 3, alpha = 0.4) +
scale_colour_manual(values = my_col_scheme)
ggsave('both.jpeg', both)
I think you're going to need to add each line manual. Hopefully somebody else knows how to answer this, but I don't think #hadley allows both colors to changed. Luckily, you're data.table so it should be easy to do :-)
Comment to anybody else trying to solve this Hopefully my partial answer helps you to answer this question. Also, my third figure shows how ggplot2 isn't getting the legend color correct as the OP wants it. As another tip, you might try playing around with the legend options.
For completeness sake - adding two scales for the same aesthetic is now easily possible with the ggnewscale package