I am creating a plot that has 2 legends using both ggplot2 and ggnewscale. What I'm trying to achieve is to fix the order of the separate legends so they always appear in the same order. The images and code below show a basic example of the issue I'm having.
First, create some data and set the colour palettes:
df <- data.frame(x = c(1:5), y = runif(10))
df_1 <- data.frame(x = c(1:5), y = runif(10))
pal_1 = rev(colorspace::sequential_hcl(palette = "Blues 3", n = 10))
pal_2 = rev(colorspace::sequential_hcl(palette = "Reds 3", n = 10))
Now create a plot (note the limits I set are between 0 and 1 for the legends):
library(ggplot2)
library(ggnewscale)
ggplot(df, mapping = aes(x, y)) +
geom_point(size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_1, limits = c(0,1), name = "val1") +
new_scale_fill() +
geom_point(data = df_1, size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_2, limits = c(0,1), name = "val2")
This will result in the following image (note that the legend for val1 is on the top):
Now, if we change the limits for val2 to something larger, like so:
ggplot(df, mapping = aes(x, y)) +
geom_point(size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_1, limits = c(0,1), name = "val1") +
new_scale_fill() +
geom_point(data = df_1, size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_2, limits = c(0,10), name = "val2")
We will get this (note that val2 legend is now on top:
It seems that ggplot will put the legend with the larger limit range on the top. I was wonder if there was a way to fix the legend order so that, say val1 is always on the top... no matter the range of the limits?
You could specify the order of the legends via the order argument of guide_xxx:
df <- data.frame(x = c(1:5), y = runif(10))
df_1 <- data.frame(x = c(1:5), y = runif(10))
pal_1 = rev(colorspace::sequential_hcl(palette = "Blues 3", n = 10))
pal_2 = rev(colorspace::sequential_hcl(palette = "Reds 3", n = 10))
library(ggplot2)
library(ggnewscale)
ggplot(df, mapping = aes(x, y)) +
geom_point(size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_1, limits = c(0,1), name = "val1",
guide = guide_colorbar(order = 1)) +
new_scale_fill() +
geom_point(data = df_1, size = 3, aes(fill = y)) +
scale_fill_gradientn(colors = pal_2, limits = c(0,10), name = "val2",
guide = guide_colorbar(order = 2))
Related
I saw a picture of a chart below and would like to recreate something similar. My code is below, how would I got about creating a multi-layer donut plot that shows me the percentage of each group?
data <- data.frame(Specialty = c("Neuro-oncologist", "Neurosurgeon", "Radiation-oncologist"),Percentage = c(44.2, 69.6, 17.2))
Surely there is a better way to do it, but if you separate your data in three independent dataframes, show the three dataset in stacked bars and finally change coordinates to polar can get this:
Code:
data1 <- data.frame(Specialty = c("Neuro-oncologist", ""),Percentage = c(44.2, 100-44.2))
data2 <- data.frame(Specialty = c("Neurosurgeon", ""),Percentage = c(69.6, 100-69.6))
data3 <- data.frame(Specialty = c("Radiation-oncologist", ""),Percentage = c(17.2, 100-17.2))
plt <- ggplot() + geom_col(aes(x = 2, y = Percentage, fill = Specialty),
data = data1, color = "white") +
geom_text(aes(label = Percentage, x= 2, y = Percentage), data = data1, size = 3) +
geom_col(aes(x = 3, y = Percentage, fill = Specialty),
data = data2, color = "white") +
geom_text(aes(label = Percentage, x= 3, y = Percentage), data = data2, size = 3) +
geom_col(aes(x = 4, y = Percentage, fill = Specialty),
data = data3, color = "white") +
geom_text(aes(label = Percentage, x= 4, y = Percentage), data = data3, size = 3) +
xlim(0, 4.5) + labs(x = NULL, y = NULL) +
theme(axis.ticks=element_blank(),
axis.text=element_blank(),
axis.title=element_blank())
plt + coord_polar(theta = "y")
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 have a plot like this:
library("ggplot2")
library("ggtext")
df = data.frame(x = rnorm(n=12), y = rnorm(n=12),
groupshape = rep(c("a","b","c"), 4),
groupcol = rep(c("e","d","f"), 4))
p = ggplot(df, aes(x = x,y = y,
shape = groupshape,
color = groupcol)) +
geom_point() + theme_classic() + labs(shape = "shape", color = "color")
Producing a plot like this:
But I want to know how to get something like this with the legend:
How would I do that? I can get rid of the shape itself through guides by turning them all while, but then the text just hovers menacingly without alignment to the legend below.
This could be achieved via the label.position and the label.theme arguments of guide_legend:
library("ggplot2")
library("ggtext")
df = data.frame(x = rnorm(n=12), y = rnorm(n=12),
groupshape = rep(c("a","b","c"), 4),
groupcol = rep(c("e","d","f"), 4))
ggplot(df, aes(x = x,y = y,
shape = groupshape,
color = groupcol)) +
geom_point() + theme_classic() + labs(shape = "shape", color = "color") +
guides(color = guide_legend(label.position = "left", label.theme = element_text(face = "bold")),
shape = guide_legend(label.position = "left"))
This question already has answers here:
How to make gap between x and y axis and protruded ticks in ggplot2
(4 answers)
Closed 2 years ago.
I am doing some plots for a project where they need to have a specific look. They will need to have some space between the axes lines and the plot panel.
library(ggplot2)
plot_data <- data.frame(X = 1:10, Y = 1:10)
ggplot() + geom_point(data = plot_data, aes(x = X, y = Y)) +
theme(axis.line = element_line(colour = "black", linetype = "solid"))
What I have
I want to add some distance between the X and Y axis of my plot, but without my axes lines also expanding, like they if I use the expand command.
plot_data <- data.frame(X = 1:10, Y = 1:10)
ggplot() + geom_point(data = plot_data, aes(x = X, y = Y)) +
theme(axis.line = element_line(colour = "black", linetype = "solid"))+
scale_x_continuous(name = "X", limits = c(1, 10), expand = c(0.1,0)) +
scale_y_continuous(name = "Y", limits = c(1, 10), expand = c(0.1,0))
What I can do
Is there a fast and reliable way to do this in R?
What i want
Thank you all in advance !
You could achieve this "capping" of the lines with the lemon package. You may use the following code to achieve this:
library(ggplot2)
library(lemon)
### your code
plot_data <- data.frame(X = 1:10, Y = 1:10)
p <- ggplot() + geom_point(data = plot_data, aes(x = X, y = Y)) +
theme(axis.line = element_line(colour = "black", linetype = "solid"))+
scale_x_continuous(name = "X", limits = c(1, 10), expand = c(0.1,0)) +
scale_y_continuous(name = "Y", limits = c(1, 10), expand = c(0.1,0))
### using the lemon package
p + coord_capped_cart(bottom='right', left='none', gap = 0.15)
### mimic the view of your plot
p2 <- p + coord_capped_cart(bottom='right', left='none', gap = 0.15)
p2 + theme(panel.background = element_rect(fill = "white"))
which yields the following picture:
An other option : Deleting the axis lines and making new ones with geom_segment.
plot_data <- data.frame(X = 1:10, Y = 1:10)
library(ggplot2)
ggplot() + geom_point(data = plot_data, aes(x = X, y = Y)) +
theme(axis.line = element_blank(),panel.background =element_blank())+
geom_segment(aes(x=0,y=1,xend=0,yend=10))+
geom_segment(aes(x=1,y=0,xend=10,yend=0))+
scale_x_discrete(limits=c(1,2,3,4,5, 8,10))+
scale_y_discrete(limits=c(1:10))
Steeling the example of this question (Link), I want to ask if it is possible to add the additional blue point to the legend?
dat <- data.frame(cond = rep(c("A", "B"), each=10),
xvar = 1:20 + rnorm(20,sd=3),
yvar = 1:20 + rnorm(20,sd=3))
g1 <- dat[15,]
ggplot(dat, aes(x = xvar, y = yvar, shape = cond,
colour = cond), size = 2.5) +
geom_point(alpha = 1) +
geom_point(data = g1, colour = "blue", size = 4, show_guide = FALSE)
You can put the aesthetics for the additional points layer inside aes instead of outside to get it added to the legend. You can use any string value; that string will be the name in the legend.
Then you can control the color and shape of that point via scale_*_manual layers.
I additionally changed the size of that point in the legend using override.aes, which is optional.
ggplot(dat, aes(x = xvar, y = yvar, shape = cond,
colour = cond), size = 2.5) +
geom_point(alpha = 1) +
geom_point(data = g1, aes(colour = "Point 15", shape = "Point 15"), size = 4) +
scale_shape_manual(values = c(16, 17, 17) ) +
scale_color_manual(values = c("pink", "turquoise", "blue") ) +
guides(color = guide_legend( override.aes = list(size = c(1.5, 1.5, 4) ) ) )
You probably have to change the condition of that point in the data as in your example or add it to the date, if it is not already part of it.
dat <- data.frame(cond = rep(c("A", "B"), each=10),
xvar = 1:20 + rnorm(20,sd=3),
yvar = 1:20 + rnorm(20,sd=3))
dat$size = 2.5
dat[15,]$cond = "C"
dat$cond = as.character(dat$cond)
dat[15,]$size = 4
ggplot(dat, aes(x = xvar, y = yvar, shape = cond,
colour = cond, size=size)) +
geom_point(alpha = 1) +
scale_colour_manual(values=c("red", "turquoise", "blue")) +
scale_size_continuous(guide = FALSE)