Show alpha/opacity in ggplot legend - r

I have two dodge bar chart which I have put on top of each other to create this plot.
Groups <- c(1, 2,1,2,1,2)
variable <- c("Yes", "Yes", "Maybe", "Maybe", "No", "No")
value <- c(50,60,70,80,90,100)
df <- data.frame(Groups, variable, value)
Groups <- c(1, 2,1,2,1,2)
variable <- c("Yes*", "Yes*", "Maybe*", "Maybe*", "No*", "No*")
value <- c(5,6,7,8,9,10)
df2 <- data.frame(Groups, variable, value)
ggplot() +
geom_bar(data=df, aes(x=Groups, y=value, fill=variable),
stat="identity", position=position_dodge(), alpha=0.2)+
geom_bar(data=df2, aes(x=Groups, y=value, fill=variable),
stat="identity", position=position_dodge())
I would like for the opacity/alpha from the plot behind to show in the legend.
I have tried +guides(colour = guide_legend(override.aes = list(alpha = 0.2))) but this does not work.
Thanks.

You can use a little trick with after_scale to achieve this. Map fill to the variable in one geom layer, and map color to variable in the second layer, but set the second layer's fill to after_scale(color). This will give you two legends, one for the first data set with its transparency, and the other for the second set, fully opaque. You can call these whatever you like using labs
ggplot(df, aes(Groups, value)) +
geom_col(aes(fill = variable), position = 'dodge', alpha = 0.2) +
geom_col(data = df2, aes(color = variable, fill = after_scale(color)),
position = 'dodge') +
labs(color = 'df2', fill = 'df1')
If you want everything in a single legend, you can do this in a couple of ways, for example, using a manual fill value with 6 colors, 3 of which have alpha set, or just merging the two legends in the method above using theme tweaks:
ggplot(df, aes(Groups, value)) +
geom_col(aes(fill = variable), position = 'dodge', alpha = 0.2) +
geom_col(data = df2, position = 'dodge', size = 0,
aes(color = variable, fill = after_scale(color))) +
labs(color = 'key', fill = NULL) +
guides(fill = guide_legend(order = 2),
color = guide_legend(order = 1,
override.aes = list(color = 'white', size = 0.3))) +
theme(legend.spacing.y = unit(-2, 'mm'),
legend.title = element_text(vjust = 5))

Related

Mixed Variable Stacked Barplot

I would like to produce a GGPlot stacked barplot with variables on the horizontal axis, yet some variables have different responses.
Some variables are 'Y/N' responses. Some variables are 'Old/Young'. And some of the variables are a Likert scale of 0-5.
Therefore, I would like to plot these in a stacked barplot form, with each variable type encoded with a different colour palette, and with a legend reflecting the different palettes/variable types.
I wondered if someone could help with this, please? Would be greatly appreciated.
X1<-c("N","N","N","N","Y","N","Y","N","N","N","N","N","Y","N","N","Y","N","N","N","Y","N","Y","Y","N","N","Y","Y","Y","N","N","N","N","N","N","N","N","Y","N","Y","N","N","N","N","Y","N","N","Y","N","Y","Y","N","Y","N","N")
X2 <-c("N","N","N","N","Y","N","Y","N","N","N","N","N","Y","N","N","Y","N","N","N","Y","N","Y","Y","N","N","Y","Y","Y","N","N","N","N","N","N","N","N","Y","N","Y","N","N","N","N","Y","N","N","Y","N","Y","Y","N","Y","N","N")
X3<-c(1,1,0,1,2,0,0,0,0,0,1,1,1,2,0,1,2,1,1,0,0,0,4,1,0,0,0,0,1,0,2,0,0,2,1,1,0,0,0,1,1,0,1,0,1,0,1,1,0,1,0,1,1,1)
X4 <-c("YouNg","Old","Old","YouNg","Old","Old","Old","YouNg","YouNg","YouNg","Old","Old","Old",
"Old","Old","Old","Old","YouNg","Old","Old","Old","YouNg","YouNg","Old","Old","Old",
"Old","Old","Old","Old","Old","Old","Old","YouNg","Old","YouNg","Old","YouNg","Old",
"Old","YouNg","Old","YouNg","YouNg","Old","Old","Old","YouNg","Old","Old","Old","YouNg", "Old", "Old")
Y <- data.frame(X1, X2, X3, X4)
One option to achieve your desired result would to use the ggnewscale package which allows for multiple scales and legends for the same aesthetic:
library(ggplot2)
library(ggnewscale)
library(tidyr)
library(dplyr)
dat <- Y |>
mutate(across(everything(), as.character)) |>
pivot_longer(everything(), names_to = "var")
ggplot(dat, aes(y = var)) +
geom_bar(data = ~subset(.x, var %in% c("X1", "X2")), aes(fill = value), position = "fill") +
scale_fill_brewer(palette = "Accent", guide = guide_legend(order = 3))+
new_scale_fill() +
geom_bar(data = ~subset(.x, var %in% c("X3")), aes(fill = value), position = "fill") +
scale_fill_brewer(palette = "Dark2", guide = guide_legend(order = 2)) +
new_scale_fill() +
geom_bar(data = ~subset(.x, var %in% c("X4")), aes(fill = value), position = "fill") +
scale_fill_brewer(palette = "Paired", guide = guide_legend(order = 1))
EDIT Depending on what you want to achieve there are three arguments to consider:
Use position = position_fill(reverse = TRUE) in geom_bar to reverse the order of the stack
To reverse the order of the fill colors use direction=-1 in scale_fill_xxx
Finally, if you want to reverse the order in the legend use reverse=TRUE in guide_legend.
The example code below uses all three, i.e.
the bars run from 0 on the left to 4 on the right.
the fill colors are reversed so that 0 is now assigned "pink" and 4 the "green" color
and finally the order in the legend is reversed.
ggplot(dat, aes(y = var)) +
geom_bar(data = ~subset(.x, var %in% c("X1", "X2")), aes(fill = value), position = "fill") +
scale_fill_brewer(palette = "Accent", guide = guide_legend(order = 3))+
new_scale_fill() +
geom_bar(data = ~subset(.x, var %in% c("X3")), aes(fill = value), position = position_fill(reverse = TRUE)) +
scale_fill_brewer(palette = "Dark2", guide = guide_legend(order = 2, reverse = TRUE), direction = -1) +
new_scale_fill() +
geom_bar(data = ~subset(.x, var %in% c("X4")), aes(fill = value), position = "fill") +
scale_fill_brewer(palette = "Paired", guide = guide_legend(order = 1))

How can I change the color based on my grouping value instead of fill value in ggplot in R?

I want to change the color of my boxplots based on their grouping value or individually instead of the fill value in ggplot. How can I do this? I need to define the fill variable to be able to get the groups subdivided into types, just as I intended, but I cannot seem to control the color anymore.
Here is some example data and how I define the plot:
library(ggplot2)
data <- data.frame( id = rep(1:120),
group = as.factor(rep(1:3, times = 40)),
type = as.factor(rep(1:2, times = 60)),
value = rnorm(120)+1)
ggplot(data, aes(x = group, y = value, fill = type)) +
geom_boxplot(aes(fill = type)) +
geom_point(aes(fill = type), position = position_jitterdodge(dodge.width = 0.65, jitter.width = 0.1, jitter.height = 0.1))
Which results in this image:
Now the colours are based on the "type" grouping. But I would like to control the 6 individual boxplots separately. For example, I want to colour them like this:
colors <- c("#b76e79", "#80a3dd",
"#a5bf9f", "#e3f0cd",
"#8a9a5b", "#ffdead")
Adding scale_fill_manual does not seem to work the way I want.
ggplot(data, aes(x = group, y = value, fill = type)) +
geom_boxplot(aes(fill = type)) +
geom_point(aes(fill = type), position = position_jitterdodge(dodge.width = 0.65, jitter.width = 0.1, jitter.height = 0.1)) +
scale_fill_manual(values = colors)
Any suggestions?
We create a new column by the interaction of 'group', 'type' and use that in scale_fill_manual
library(dplyr)
library(ggplot2)
data <- data %>%
mutate(grptype = interaction(group, type))
gg <- ggplot(data, aes(x = group, y = value, fill = grptype)) +
geom_boxplot(aes(fill = grptype)) +
geom_point(aes(fill = grptype), position = position_jitterdodge(dodge.width = 0.65, jitter.width = 0.1, jitter.height = 0.1))
colors <- c("#b76e79", "#80a3dd",
"#a5bf9f", "#e3f0cd",
"#8a9a5b", "#ffdead")
gg +
scale_fill_manual(name="grptype",
labels = levels(data$grptype),
values = setNames(colors, levels(data$grptype))) +
theme(legend.title = element_text(size=12, color = "black", face="bold"),
legend.justification=c(0,1),
legend.position=c(0.05, 0.95),
legend.background = element_blank(),
legend.key = element_blank())
-output

How to add a legend manually for line chart

i need the plan legend
How to add a legend manually for geom_line
ggplot(data = impact_end_Current_yr_m_actual, aes(x = month, y = gender_value)) +
geom_col(aes(fill = gender))+theme_classic()+
geom_line(data = impact_end_Current_yr_m_plan, aes(x=month, y= gender_value, group=1),color="#288D55",size=1.2)+
geom_point(data = impact_end_Current_yr_m_plan, aes(x=month, y=gender_value))+
theme(axis.line.y = element_blank(),axis.ticks = element_blank(),legend.position = "bottom", axis.text.x = element_text(face = "bold", color = "black", size = 10, angle = 0, hjust = 1))+
labs(x="", y="End Beneficiaries (in Num)", fill="")+
scale_fill_manual(values=c("#284a8d", "#00B5CE","#0590eb","#2746c2"))+
scale_y_continuous(labels = function(x) format(x, scientific = FALSE)
The neatest way to do it I think is to add colour = "[label]" into the aes() section of geom_line() then put the manual assigning of a colour into scale_colour_manual() here's an example from mtcars (apologies that it uses stat_summary instead of geom_line but does the same trick):
library(tidyverse)
mtcars %>%
ggplot(aes(gear, mpg, fill = factor(cyl))) +
stat_summary(geom = "bar", fun = mean, position = "dodge") +
stat_summary(geom = "line",
fun = mean,
size = 3,
aes(colour = "Overall mean", group = 1)) +
scale_fill_discrete("") +
scale_colour_manual("", values = "black")
Created on 2020-12-08 by the reprex package (v0.3.0)
The limitation here is that the colour and fill legends are necessarily separate. Removing labels (blank titles in both scale_ calls) doesn't them split them up by legend title.
In your code you would probably want then:
...
ggplot(data = impact_end_Current_yr_m_actual, aes(x = month, y = gender_value)) +
geom_col(aes(fill = gender))+
geom_line(data = impact_end_Current_yr_m_plan,
aes(x=month, y= gender_value, group=1, color="Plan"),
size=1.2)+
scale_color_manual(values = "#288D55") +
...
(but I cant test on your data so not sure if it works)

Duplicate items in ggplot2 legend

I have produced a plot with stacked columns and two lines in ggplot2. However, the legend items of the lines also show in the legend of the columns. Any one knows how to remove them from the column legend?
Code below:
##Remove Objects
rm(list=ls(all=TRUE))
##Load packages
library(ggplot2)
library(dplyr)
library(reshape)
##Data
set.seed(12345)
d.fig6.1 <- data.frame(mm=c("Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"),a.1=(rnorm(12)*5)^2)
d.fig6.1$a.2 <- (rnorm(12)*5)^2
d.fig6.1$a.3 <- (rnorm(12)*5)^2
d.fig6.1$a.4 <- (rnorm(12)*5)^2
d.fig6.1$a.5 <- (rnorm(12)*5)^2
d.fig6.1$a.6 <- (rnorm(12)*5)^2
d.fig6.1$a.7 <- (rnorm(12)*5)^2
d.fig6.2 <- data.frame(mm=c("Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"),a.8=(rnorm(12)*5)^2)
d.fig6.2$a.9 <- (rnorm(12)*5)^2
d.fig6.1 <- melt(d.fig6.1,id="mm")
d.fig6.2 <- melt(d.fig6.2,id="mm")
d.fig6.1
d.fig6.2
##Plot
theme_set(theme_bw(7)) #25
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2",
"#D55E00", "#CC79A7","red")
sp.6 <- ggplot(d.fig6.1, aes(x=mm, y=value, fill=variable)) + geom_col()
+ labs(x="") + labs(y="[Units]")
+ theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())
+ scale_fill_manual(values=cbPalette,name="")
+ geom_text(data=d.fig6.1, aes(label = round(value,digits=2)), position = position_stack(vjust=0.5), size=2)
+ theme(legend.title = element_blank())
+ geom_line(data=d.fig6.2, aes(x=as.numeric(mm), y=value, color=variable),size=1,inherit.aes = FALSE)
+ geom_text(data=d.fig6.2, aes(label=round(value,digits=2)),hjust=0, vjust=0, size=2.5)
sp.6
Move fill = variable out of the top level ggplot(aes(...)) mapping. Keep only the aesthetic mappings common to all geoms there. This way you don't really need inherit.aes = FALSE, either:
ggplot(d.fig6.1,
aes(x = mm, y = value, label = round(value, digits = 2))) +
geom_col(aes(fill = variable)) +
geom_text(position = position_stack(vjust = 0.5), size = 2) +
geom_line(data = d.fig6.2,
aes(color = variable, group = variable),
size = 1) +
geom_text(data = d.fig6.2,
hjust = 0, vjust = 0, size = 2.5) +
labs(x = "", y = "[Units]") +
scale_fill_manual(values = cbPalette, name="") +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.title = element_blank())
Explanation: By including fill = variable in the top level aesthetic mapping, every geom that doesn't have inherit.aes = FALSE set explicitly will inherit this as part of the aesthetic mapping for its level.
In this case, geom_line & the second geom_text both use a different data source (d.fig6.2 instead of d.fig6.1), but geom_text still inherits fill = variable from the top level mapping. So the variable values from d.fig6.2 ("a.8" & "a.9") are added to the fill palette, even though they aren't used anywhere.
I stripped down your code for a more minimal example that reproduces the problem below:
# this will result in a.8 & a.9 in the column legend
# because geom_text inherits aes(fill = variable) from ggplot()
ggplot(d.fig6.1,
aes(x = mm, y = value, fill = variable,
label = round(value, digits = 2))) +
geom_col() +
geom_text(data = d.fig6.2)
# this will not
# because aes(fill = variable) is moved from ggplot() to geom_col()
ggplot(d.fig6.1,
aes(x = mm, y = value,
label = round(value, digits = 2))) +
geom_col(aes(fill = variable)) +
geom_text(data = d.fig6.2)
# this will not, either
# because geom_text() includes all the aes mappings it requires, & inherit.aes = FALSE
ggplot(d.fig6.1,
aes(x = mm, y = value, fill = variable,
label = round(value, digits = 2))) +
geom_col() +
geom_text(data = d.fig6.2,
aes(x = mm, y = value,
label = round(value, digits = 2)),
inherit.aes = FALSE)
Try adding + guides(color = FALSE) at the end.

Aesthetics must be either length 1 or the same as the data (1): x, y, label

I'm working on some data on party polarization (something like this) and used geom_dumbbell from ggalt and ggplot2. I keep getting the same aes error and other solutions in the forum did not address this as effectively. This is my sample data.
df <- data_frame(policy=c("Not enough restrictions on gun ownership", "Climate change is an immediate threat", "Abortion should be illegal"),
Democrats=c(0.54, 0.82, 0.30),
Republicans=c(0.23, 0.38, 0.40),
diff=sprintf("+%d", as.integer((Democrats-Republicans)*100)))
I wanted to keep order of the plot, so converted policy to factor and wanted % to be shown only on the first line.
df <- arrange(df, desc(diff))
df$policy <- factor(df$policy, levels=rev(df$policy))
percent_first <- function(x) {
x <- sprintf("%d%%", round(x*100))
x[2:length(x)] <- sub("%$", "", x[2:length(x)])
x
}
Then I used ggplot that rendered something close to what I wanted.
gg2 <- ggplot()
gg2 <- gg + geom_segment(data = df, aes(y=country, yend=country, x=0, xend=1), color = "#b2b2b2", size = 0.15)
# making the dumbbell
gg2 <- gg + geom_dumbbell(data=df, aes(y=country, x=Democrats, xend=Republicans),
size=1.5, color = "#B2B2B2", point.size.l=3, point.size.r=3,
point.color.l = "#9FB059", point.color.r = "#EDAE52")
I then wanted the dumbbell to read Democrat and Republican on top to label the two points (like this). This is where I get the error.
gg2 <- gg + geom_text(data=filter(df, country=="Government will not control gun violence"),
aes(x=Democrats, y=country, label="Democrats"),
color="#9fb059", size=3, vjust=-2, fontface="bold", family="Calibri")
gg2 <- gg + geom_text(data=filter(df, country=="Government will not control gun violence"),
aes(x=Republicans, y=country, label="Republicans"),
color="#edae52", size=3, vjust=-2, fontface="bold", family="Calibri")
Any thoughts on what I might be doing wrong?
I think it would be easier to build your own "dumbbells" with geom_segment() and geom_point(). Working with your df and changing the variable refences "country" to "policy":
library(tidyverse)
# gather data into long form to make ggplot happy
df2 <- gather(df,"party", "value", Democrats:Republicans)
ggplot(data = df2, aes(y = policy, x = value, color = party)) +
# our dumbell
geom_path(aes(group = policy), color = "#b2b2b2", size = 2) +
geom_point(size = 7, show.legend = FALSE) +
# the text labels
geom_text(aes(label = party), vjust = -1.5) + # use vjust to shift text up to no overlap
scale_color_manual(values = c("Democrats" = "blue", "Republicans" = "red")) + # named vector to map colors to values in df2
scale_x_continuous(limits = c(0,1), labels = scales::percent) # use library(scales) nice math instead of pasting
Produces this plot:
Which has some overlapping labels. I think you could avoid that if you use just the first letter of party like this:
ggplot(data = df2, aes(y = policy, x = value, color = party)) +
geom_path(aes(group = policy), color = "#b2b2b2", size = 2) +
geom_point(size = 7, show.legend = FALSE) +
geom_text(aes(label = gsub("^(\\D).*", "\\1", party)), vjust = -1.5) + # just the first letter instead
scale_color_manual(values = c("Democrats" = "blue", "Republicans" = "red"),
guide = "none") +
scale_x_continuous(limits = c(0,1), labels = scales::percent)
Only label the top issue with names:
ggplot(data = df2, aes(y = policy, x = value, color = party)) +
geom_path(aes(group = policy), color = "#b2b2b2", size = 2) +
geom_point(size = 7, show.legend = FALSE) +
geom_text(data = filter(df2, policy == "Not enough restrictions on gun ownership"),
aes(label = party), vjust = -1.5) +
scale_color_manual(values = c("Democrats" = "blue", "Republicans" = "red")) +
scale_x_continuous(limits = c(0,1), labels = scales::percent)

Resources