Different colour palettes for two different colour aesthetic mappings in ggplot2 - r

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

Related

scale_fill_discrete with breaks and custom color scale?

I have an issue choosing the custom colouring of my plot, I am trying to add my own values to the plot but I can's seem to understand where should I put the custom color vector:
col <- c("#004d8d", "#cc2701", "#e5b400")
This is the plot code right now:
p1 <- ggplot(data = densdf1, mapping = aes(x = x, y = y)) +
geom_area(data = densdf1[densdf1$CI,],
aes(fill = Electrode, color = Electrode),
outline.type = "full", alpha = 0.3, size = 1) +
geom_line(aes(color = Electrode), size = 1) +
scale_fill_discrete(breaks=c("Fz","Cz","Pz")) +
guides(colour = "none") +
geom_vline(xintercept = 0) +
lims(x = c(-3, 2), y = c(0, 2.25)) +
labs(title="INTERVAL 225-275ms", x="VALUES", y="DENSITY") +
theme_bw() +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=12),
plot.title=element_text(size=14))
This is the plot with default colours and it looks perfectly fine, but I'd like to customise the colours.
As you can see there is a contour line and the area from the density that need to be coloured.
I tried changing the aesthetics but I think I am not understanding the logic. I tried also using scale_manual_fill(values = col) removing scale_fill_discrete(breaks=c("Fz","Cz","Pz")) but it only works with the area inside.
Can any ggplot2 expert give me a hint? Thanks!
in the aes(...) section you define scales, and the color must be adjusted for each scale. In your case it would be the "fill" and the "color" scale, hence the following two lines must be added to adjust both scales:
scale_color_manual(values = col, breaks = c("Fz", "Cz", "Pz")) +
scale_fill_manual(values = col, breaks = c("Fz", "Cz", "Pz")) +
If you want to make sure, that a certain category gets a certain color, you have to specify the breaks, and the order the vector corresponds to the order of the color vector.

Adding legend for combo bar and line graph -- ggplot ignoring commands

I am trying to make a bar chart with line plots as well. The graph has created fine but the legend does not want to add the line plots to the legend.
I have tried so many different ways of adding these to the legend including:
ggplot Legend Bar and Line in Same Graph
None of which have worked. show.legend also seems to have been ignored in the geom_line aes.
My code to create the graph is as follows:
ggplot(first_q, aes(fill = Segments)) +
geom_bar(aes(x= Segments, y= number_of_new_customers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
ylab('Number of Customers') + xlab('Segments') +
ggtitle('Number Customers in Q1 by Segments') +theme(plot.title =
element_text(hjust = 0.5)) +
geom_line(aes(x= Segments, y=count) ,stat="identity",
group = 1, size = 1.5, colour = "darkred", alpha = 0.9, show.legend =
TRUE) +
geom_line(aes(x= Segments, y=bond_count)
,stat="identity", group = 1, size = 1.5, colour = "blue", alpha =
0.9) +
geom_line(aes(x= Segments, y=variable_count)
,stat="identity", group = 1, size = 1.5, colour = "darkgreen",
alpha = 0.9) +
geom_line(aes(x= Segments, y=children_count)
,stat="identity", group = 1, size = 1.5, colour = "orange", alpha
= 0.9) +
guides(fill=guide_legend(title="Segments")) +
scale_color_discrete(name = "Prod", labels = c("count", "bond_count", "variable_count", "children_count)))
I am fairly new to R so if any further information is required or if this question could be better represented then please let me know.
Any help is greatly appreciated.
Alright, you need to remove a little bit of your stuff. I used the mtcars dataset, since you did not provide yours. I tried to keep your variable names and reduced the plot to necessary parts. The code is as follows:
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers, fill = "Bar"), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="Line"))+
geom_line(aes(x=Segments,y=disp, linetype="next line"))
The answer you linked already gave the answer, but i try to explain. You want to plot the legend by using different properties of your data. So if you want to use different lines, you can declare this in your aes. This is what get's shown in your legend. So i used two different geom_lines here. Since the aes is both linetype, both get shown at the legend linetype.
the plot:
You can adapt this easily to your use. Make sure you using known keywords for the aesthetic if you want to solve it this way. Also you can change the title names afterwards by using:
labs(fill = "costum name")
If you want to add colours and the same line types, you can do customizing by using scale_linetype_manual like follows (i did not use fill for the bars this time):
library(ggplot2)
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
cols = c("red", "green")
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="solid"), color = "red", alpha = 0.4)+
geom_line(aes(x=Segments,y=disp, linetype="second"), color ="green", alpha = 0.5)+
scale_linetype_manual(values = c("solid","solid"),
guide = guide_legend(override.aes = list(colour = cols)))

How do I add a legend to identify vertical lines in ggplot?

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.

How can I add specific, labeled points to a ggtern plot?

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)

ggplot outline jitter datapoints

I'm trying to create a scatterplot where the points are jittered (geom_jitter), but I also want to create a black outline around each point. Currently I'm doing it by adding 2 geom_jitters, one for the fill and one for the outline:
beta <- paste("beta == ", "0.15")
ggplot(aes(x=xVar, y = yVar), data = data) +
geom_jitter(size=3, alpha=0.6, colour=my.cols[2]) +
theme_bw() +
geom_abline(intercept = 0.0, slope = 0.145950, size=1) +
geom_vline(xintercept = 0, linetype = "dashed") +
annotate("text", x = 2.5, y = 0.2, label=beta, parse=TRUE, size=5)+
xlim(-1.5,4) +
ylim(-2,2)+
geom_jitter(shape = 1,size = 3,colour = "black")
However, that results in something like this:
Because jitter randomly offsets the data, the 2 geom_jitters are not in line with each other. How do I ensure the outlines are in the same place as the fill points?
I've see threads about this (e.g. Is it possible to jitter two ggplot geoms in the same way?), but they're pretty old and not sure if anything new has been added to ggplot that would solve this issue
The code above works if, instead of using geom_jitter, I use the regular geom_point, but I have too many overlapping points for that to be useful
EDIT:
The solution in the posted answer works. However, it doesn't quite cooperate for some of my other graphs where I'm binning by some other variable and using that to plot different colours:
ggplot(aes(x=xVar, y = yVar, color=group), data = data) +
geom_jitter(size=3, alpha=0.6, shape=21, fill="skyblue") +
theme_bw() +
geom_vline(xintercept = 0, linetype = "dashed") +
scale_colour_brewer(name = "Title", direction = -1, palette = "Set1") +
xlim(-1.5,4) +
ylim(-2,2)
My group variable has 3 levels, and I want to colour each group level by a different colour in the brewer Set1 palette. The current solution just colours everything skyblue. What should I fill by to ensure I'm using the correct colour palette?
You don't actually have to use two layers; you can just use the fill aesthetic of a plotting character with a hole in it:
# some random data
set.seed(47)
df <- data.frame(x = rnorm(100), y = runif(100))
ggplot(aes(x = x, y = y), data = df) + geom_jitter(shape = 21, fill = 'skyblue')
The colour, size, and stroke aesthetics let you customize the exact look.
Edit:
For grouped data, set the fill aesthetic to the grouping variable, and use scale_fill_* functions to set color scales:
# more random data
set.seed(47)
df <- data.frame(x = runif(100), y = rnorm(100), group = sample(letters[1:3], 100, replace = TRUE))
ggplot(aes(x=x, y = y, fill=group), data = df) +
geom_jitter(size=3, alpha=0.6, shape=21) +
theme_bw() +
geom_vline(xintercept = 0, linetype = "dashed") +
scale_fill_brewer(name = "Title", direction = -1, palette = "Set1")

Resources