legend does not follow ordered bars in R - r

My plot has two problems:
(1) the group bars are not ordered as I want them to be - I will like them to appear in the order entered and (2) for the legend, the order appears as V, E, B whereas in the groups, it appears as B, E, V. I can reverse the legend, however, what I will really like to get is change the order of the subplots to V, E, B.
library(ggplot2)
df2 <- data.frame(supp = rep(c("V","E","B"), each=5),
s = rep(c("C3","C1", "C2","C5","C6"), 3),
len = c(1,2,3,4,5,6,8,4,4,3,9,7,6,8,5))
p <- ggplot(data = df2, aes(x = s, y = len, fill = supp)) +
geom_bar(stat = "identity", color = "black", position = position_dodge())
p + scale_fill_brewer(palette = "Blues", guide = guide_legend(reverse = TRUE)) +
scale_x_discrete(limits = rev(levels(df2$s)))

You need to change df2$supp from character to factor and specify the levels as you want them to appear.
See modified code below. Also, check out this link for even more detail about how to control the colour of your variables so they are consistent.
library(ggplot2)
df2 <- data.frame(supp = rep(c("V","E","B"), each=5),
s = rep(c("C3","C1", "C2","C5","C6"), 3),
len = c(1,2,3,4,5,6,8,4,4,3,9,7,6,8,5))
df2$supp <- factor(df2$supp,
levels = c("V", "E", "B"))
p <- ggplot(data=df2, aes(x=(df2$s), y=len, fill=supp)) +
geom_bar(stat="identity", color="black", position=position_dodge())
p + scale_fill_brewer(palette="Blues", guide = guide_legend(reverse=TRUE)) +
scale_x_discrete(limits = rev(levels(df2$s)))

Data
df2 <- data.frame(supp = rep(c("V", "E", "B"), each = 5),
s = rep(c("C3", "C1", "C2", "C5", "C6"), 3),
len = c(1, 2, 3, 4, 5, 6, 8, 4, 4, 3, 9, 7, 6, 8, 5))
Adjustment
Because you use data.frame() to create data, R will set strings as factors by default. So you need to revise the types of variables to what you want.
df2$s <- as.character(df2$s)
df2$supp <- factor(df2$supp, levels = c("V", "E", "B"))
Plot
ggplot(data = df2, aes(x = s, y = len, fill = supp)) +
geom_bar(stat = "identity", color = "black", position = position_dodge()) +
scale_fill_brewer(palette = "Blues", direction = -1)
Here you don't need to use additional guide_legend() and scale_x_discrete() to change order. It will be more concise.

Related

Why geom_line legend if show.legend = FALSE and why different colours

After executing the code here below, I was wondering:
1- Why "A.line" and "B.line" variables appear in the geom_point() legend.
2- why there are four colors in the legend.
I guess both answers are related, but I can not tell what is going on.
I would like to have the legend just with "A.points" and "B.points".
I would also like the same colors in both lines and points (I guess this I can do manually).
Thanks in advance for your help.
Best,
David
data.frame(x = rep(1:2,2),
names.points = rep(c("A.point","B.point"), 2),
y.point = c(2, 4, 7, 9),
names.lines = rep(c("A.line","B.line"), each = 2),
y.line = c(3, 3, 8, 8)) %>%
ggplot() +
geom_point(aes(x = x, y = y.point, group = names.points, colour = names.points), size = 5) +
geom_line(aes(x = x, y = y.line, group = names.lines, colour = names.lines), show.legend = FALSE)
Legends are not related to geoms but to the scales and display the categories (or the range of the values) mapped on an aesthetic. Hence, you get four colors because you have four categories mapped on the color aesthetic. The geoms used are only displayed in the legend key via the so called key glyph which is a point for geom_point and a line for geom_line. And show.legend=FALSE only means to not display the key glyph for geom_line in the legend key, i.e. the legend keys shows only a point but no line.
To remove the categories related to the lines from your legend use e.g. the breaks argument of scale_color_discrete instead.
library(ggplot2)
library(dplyr)
data.frame(
x = rep(1:2, 2),
names.points = rep(c("A.point", "B.point"), 2),
y.point = c(2, 4, 7, 9),
names.lines = rep(c("A.line", "B.line"), each = 2),
y.line = c(3, 3, 8, 8)
) %>%
ggplot() +
geom_point(aes(x = x, y = y.point, group = names.points, colour = names.points), size = 5) +
geom_line(aes(x = x, y = y.line, group = names.lines, colour = names.lines), show.legend = FALSE) +
scale_color_discrete(breaks = c("A.point", "B.point"))
UPDATE To fix your issue with the colors you could use a named color vector:
pal_col <- rep(c("darkblue","darkred"), 2)
names(pal_col) <- c("A.point", "B.point", "A.line", "B.line")
data.frame(
x = rep(1:2, 2),
names.points = rep(c("A.point", "B.point"), 2),
y.point = c(2, 4, 7, 9),
names.lines = rep(c("A.line", "B.line"), each = 2),
y.line = c(3, 3, 8, 8)
) %>%
ggplot() +
geom_point(aes(x = x, y = y.point, group = names.points, colour = names.points), size = 5) +
geom_line(aes(x = x, y = y.line, group = names.lines, colour = names.lines), show.legend = FALSE) +
scale_color_manual(breaks = c("A.point", "B.point"),
values = pal_col)

Insert data of two columns in a graph legend - ggplot2

I have the following dataframe:
df <- data.frame(product = c("a", "b", "c", "d"),
count = c(3, 4, 6, 7),
name = c("ball", "pen", "notebook", "pencil"))
ggplot(df, aes(x = "", y = count, fill = product)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0)
Is it possible to make the legend with information of two columns? I mean, I would like to make it like: a - ball; b - pen; c - notebook; d - pencil.
I know I can merge the columns using stringr::str_c and make a new legend like this, but I would like to use a more elegant solution.
What you could do is combine the columns in one line using paste0 and assign these labels in scale_fill_discrete like this:
df <- data.frame(product = c("a", "b", "c", "d"),
count = c(3, 4, 6, 7),
name = c("ball", "pen", "notebook", "pencil"))
library(ggplot2)
ggplot(df, aes(x = "", y = count, fill = product)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0) +
scale_fill_discrete(labels = with(df, paste0(product, " - ", name)))
Created on 2022-07-19 by the reprex package (v2.0.1)

ggplot custom legend instead of default

I've searched and tried a bunch of suggestions to be able to display a custom legend instead of the default one in a grouped scatter ggplot. I've tried this and this and following this among others.
For instance, let's say I have a df like this one:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
and I want to display the values of each id as round dots, but also put the reference values and minimum values for each id as a differently shaped dot, as follows:
p = ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id)), shape = 19, size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id)), shape = 0, size = 8) +
geom_point(aes(x = id, y = min, color = factor(id)), shape = 2, size = 8) +
xlab("") +
ylab("Value")
#print(p)
Now all is fine, but my legend doesn't add anything to the interpretation of the plot, as the X axis and colors are enough to understand it. I know I can remove the legend via theme(legend.position = "none").
Instead, I would like to have a legend of what the actual shapes of each dot represent (e.g., filled round dot = value, triangle = min, square = ref).
Among trying to manually set the scale values via scale_fill_manual and something along those lines
override.shape = shapes$shape
override.linetype = shapes$pch
guides(colour = guide_legend(override.aes = list(shape = override.shape, linetype = override.linetype)))...
....
I've also tried making a secondary plot, but not display it, using something suggested in one of the links pasted above:
shapes = data.frame(shape = c("value", "reference", "minimum"), pch = c(19,0,2), col = c("gray", "gray", "gray"))
p2 = ggplot(shapes, aes(shape, pch)) + geom_point()
#print(p2)
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]]
return(legend)
}
legend <- g_legend(p2)
library(gridExtra)
pp <- arrangeGrob(p1 ,legend,
widths=c(5/4, 1/4),
ncol = 2)
but then I get the error:
> legend <- g_legend(p2)
Error in tmp$grobs[[leg]] :
attempt to select less than one element in get1index
for which I did not find a working solution.. so yeah.. any suggestion on how I could only show a legend related to the different dot shapes would be welcome.
Thank you
You can manually build a shape legend using scale_shape_manual:
library(ggplot2)
ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id), shape = 'value'), size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id), shape = 'ref'), size = 8) +
geom_point(aes(x = id, y = min, color = factor(id), shape = 'min'), size = 8) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
But a better way to do this would be to reshape the df to a long format, and map each aes to a variable:
library(dplyr)
library(tidyr)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
To remove the legend for the color use guide_none():
library(tidyr)
library(ggplot2)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
guides(color = guide_none()) +
xlab("") +
ylab("Value")
Created on 2020-04-16 by the reprex package (v0.3.0)
Data:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
You can tidy your data first using tidyr, and then map the aes shape to the new variable
library(tidyr)
df2 <- pivot_longer(df, -id)
ggplot(data = df2) +
geom_point(aes(x = id, y = value, shape = name), size = 6) +
xlab("") +
ylab("Value")

Add count as label to points in geom_count

I used geom_count to visualise overlaying points as sized groups, but I also want to add the actual count as a label to the plotted points, like this:
However, to achieve this, I had to create a new data frame containing the counts and use these data in geom_text as shown here:
#Creating two data frames
data <- data.frame(x = c(2, 2, 2, 2, 3, 3, 3, 3, 3, 4),
y = c(1, 2, 2, 2, 2, 2, 3, 3, 3, 3),
id = c("a", "b", "b", "b", "c",
"c", "d", "d", "d", "e"))
data2 <- data %>%
group_by(id) %>%
summarise(x = mean(x), y = mean(y), count = n())
# Creating the plot
ggplot(data = data, aes(x = x, y = y)) +
geom_count() +
scale_size_continuous(range = c(10, 15)) +
geom_text(data = data2,
aes(x = x, y = y, label = count),
color = "#ffffff")
Is there any way to achieve this in a more elegant way (i.e. without the need for the second data frame)? I know that you can access the count in geom_count using ..n.., yet if I try to access this in geom_text, this is not working.
Are you expecting this:
ggplot(data %>%
group_by(id) %>%
summarise(x = mean(x), y = mean(y), count = n()),
aes(x = x, y = y)) + geom_point(aes(size = count)) +
scale_size_continuous(range = c(10, 15)) +
geom_text(aes(label = count),
color = "#ffffff")
update:
If the usage of geom_count is must, then the expected output can be achieved using:
p <- ggplot(data = data, aes(x = x, y = y)) +
geom_count() + scale_size_continuous(range = c(10, 15))
p + geom_text(data = ggplot_build(p)$data[[1]],
aes(x, y, label = n), color = "#ffffff")
here would be a solution for a code with discrete values
f<-ggplot(data = STest, aes(x = x, y = y)) + geom_count()+scale_x_discrete(labels = c("strong decrease","decrease","no change","increase","strong increase","no opinion"))+scale_y_discrete(labels = c("strong decrease","decrease","no change","increase","strong increase","no opinion"))
f + geom_text(data = ggplot_build(p)$data[[1]],aes(x, y, label = n,vjust= -2))
Thank you so much!
A much easier way to change this is to use the labs() function so in this case it would be ...labs(size = "Count") + ....
That should be all you need.

Line graph customization (add circles, colors)

With data
value <- c(9, 4, 10, 7, 10,
10, 10, 4, 10,
4, 10, 2, 5, 5, 4)
names <- c("a","b",
"c","d","e",
"f", "g","h",
"i","j","k","l",
"m","n","p")
df <- data.frame(value, names)
df$names <- as.character(df$names)
p <- ggplot(data = df, aes(y = value,x= names,group=1))+
geom_point(color = I("red"),shape=23, lwd=3,fill="red")+
geom_line(group = I(1),color = I("red"))+
theme_bw()+
coord_flip()
p + xlab("") +ylab("")
I produce this
But now I would like to create plot similar picture below, where "a", "b", "c" and "D" would be x aes labels and belong to PART 1 and names "p", "n", "m", "i", "k" would belong in PART 2 (and so on). the key part here is how to add circles inside plot.
I've also looked here
How can I add freehand red circles to a ggplot2 graph?
but no luck.
If this in upper pocture is not possible, than I would like my output to be like below picture
In order to achieve the facetting by part as in your last plot you can create a new column that groups your values, e.g.:
df$part <- rep(c("part3", "part2", "part1"), each = 5)
In order to plot the open circles you can add another geom_point() layer. I created a new data frame that consists of all combinations of names and value for each part:
library(dplyr)
library(tidyr)
df2 <- df %>%
group_by(part, names) %>%
expand(value = min(df$value):max(df$value))
Then you plot a facetted plot with circles:
ggplot() +
geom_point(data = df2, aes(x = value, y = names),
shape = 1) +
geom_point(data = df, aes(y = names, x = value, group = 1), colour = I("red"), shape = 23, lwd = 3, fill = "red") +
geom_line(data = df, aes(y = names, x = value, group = 1), group = I(1),color = I("red")) +
theme_bw() +
facet_wrap(~part, ncol = 1, scales = "free_y")
Note that I swapped x and y values as coord_flip() cannot be used with scales = "free_y" which is however necessary if you want only those names which have values in the respective facet.

Resources