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.
Related
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)
I'm trying to consistently plot histograms for zonal statistics from a thematic map. The data within a single zone often looks something like this:
dat <- data.frame("CLASS" = sample(LETTERS[1:6], 250, replace = TRUE,
prob = c(.15, .06, .35, .4, .02, 0)))
dat$CLASS <- factor(dat$CLASS, levels = LETTERS[1:6], ordered = T)
wherein not all possible classes may have been present in the zone.
I can pre-compute the data summary and use geom_bar and a manual colour scale to get consistent bar colours regardless of missing data:
library(dplyr)
library(ggplot2)
library(viridis)
dat_summ <- dat %>%
group_by(CLASS, .drop = FALSE) %>%
summarise(percentage = n() / nrow(.) * 100)
mancols <- viridis_pal()(6)
names(mancols) <- LETTERS[1:6]
ggplot(dat_summ) +
geom_bar(aes(x = CLASS, y = percentage, fill = CLASS),
stat = 'identity', show.legend = FALSE) +
scale_x_discrete(drop = FALSE) +
scale_fill_manual(values = mancols, drop = FALSE) +
labs(x = 'Class', y = 'Percent') +
theme_minimal() +
theme(panel.grid.minor = element_blank())
But I can't keep the colours consistent across plots when I try to use geom_histogram:
ggplot(dat) +
geom_histogram(aes(x = CLASS,
y = (..count../sum(..count..)) * 100,
fill = ..x..), stat = 'count', show.legend = FALSE) +
scale_x_discrete(drop = FALSE) +
scale_fill_viridis_c() +
labs(x = 'Class', y = 'Percent') +
theme_minimal() +
theme(panel.grid.minor = element_blank())
If any of the outside-edge columns (A, F) are count = 0, the colours rescale to where data is present. This doesn't happen if there's a gap in one of the middle classes. Using scale_fill_viridis_b() doesn't solve the problem - it always rescales the palette against the number of non-0 columns.
Is it possible to prevent this behaviour and output consistent colours no matter which columns are count = 0, or am I stuck with my geom_bar approach?
Maybe scale_fill_discrete/scale_fill_viridis_d(drop = F) is what you want (with fill = CLASS).
ggplot(dat) +
geom_histogram(aes(x = CLASS,
y = (..count../sum(..count..)) * 100,
fill = CLASS), stat = 'count', show.legend = FALSE) +
scale_x_discrete(drop = FALSE) +
scale_fill_viridis_d(drop = FALSE) +
labs(x = 'Class', y = 'Percent') +
theme_minimal() +
theme(panel.grid.minor = element_blank())
I think that the problem is that you pass the calculated variable ..x.. to fill in the aesthetics. It appears the length of this variable changes with your data set. You could replace it with scale_fill_manual and you will get the same plot colours regardless of how many levels there are in your CLASS variable:
ggplot(dat) +
geom_histogram(aes(x = CLASS, y = stat(count/sum(count) * 100), fill = CLASS), stat = 'count', show.legend = FALSE) +
scale_x_discrete(drop = FALSE) +
scale_fill_manual(values = c("#FF0000FF", "#CCFF00FF", "#00FF66FF", "#0066FFFF", "#CC00FFFF", "#FF99FFFF"))
labs(x = 'Class', y = 'Percent') +
theme_minimal() +
theme(panel.grid.minor = element_blank())
I have a geom_jitter plot showing Variables between 2 samples, I would like to include the Group-variable parameters on the left of the plot, setting a separation by lines like in the figure below. Thus, Variables are organised by Group.
Here is a reproducible example:
data<- tibble::tibble(
Variable = c("A","B","C","D","E", "F"),
Group = c("Asia","Asia","Europe","Europe","Africa","America"),
sample1 = c(0.38,0.22,0.18,0.12,0.1,0),
sample2 = c(0.23,0.2,0,0.12,0.11,0.15))
library(reshape2)
data2<- melt(data,
id.vars=c("Variable", "Group"),
measure.vars=c("sample1", "sample2"),
variable.name="Sample",
value.name="value")
data22[is.na(data22)] <- 0
library(ggplot2)
ggplot(data2, aes(x = Sample, y = Variable, label=NA)) +
geom_point(aes(size = value, colour = value)) +
geom_text(hjust = 1, size = 2) +
# scale_size(range = c(1,3)) +
theme_bw()+
scale_color_gradient(low = "lightblue", high = "darkblue")
Here is the current output I have:
And this is the format I would like:
To get a polished version of the plot most similar to your ideal plot, you can use facet_grid() plus some theme() customization.
ggplot(data2, aes(x = Sample, y = Variable, label=NA)) +
geom_point(aes(size = value, colour = value)) +
geom_text(hjust = 1, size = 2) +
# scale_size(range = c(1,3)) +
theme_bw()+
scale_color_gradient(low = "lightblue", high = "darkblue") +
facet_grid(Group~., scales = "free", switch = "y") +
theme(strip.placement = "outside",
strip.text.y = element_text(angle = 180),
panel.spacing = unit(0, "cm"))
I have two very similar plots, which have two y-axis - a bar plot and a line plot:
code:
sec_plot <- ggplot(data, aes_string (x = year, group = 1)) +
geom_col(aes_string(y = frequency), fill = "orange", alpha = 0.5) +
geom_line(aes(y = severity))
However, there are no labels. I want to get a label for the barplot as well as a label for the line plot, something like:
How can I add the labels to the plot, if there is only pone single group? is there a way to specify this manually? Until know I have only found option where the labels can be added by specifying them in the aes
EXTENSION (added a posterior):
getSecPlot <- function(data, xvar, yvar, yvarsec, groupvar){
if ("agegroup" %in% xvar) xvar <- get("agegroup")
# data <- data[, startYear:= as.numeric(startYear)]
data <- data[!claims == 0][, ':=' (scaled = get(yvarsec) * max(get(yvar))/max(get(yvarsec)),
param = max(get(yvar))/max(get(yvarsec)))]
param <- data[1, param] # important, otherwise not found in ggplot
sec_plot <- ggplot(data, aes_string (x = xvar, group = groupvar)) +
geom_col(aes_string(y = yvar, fill = groupvar, alpha = 0.5), position = "dodge") +
geom_line(aes(y = scaled, color = gender)) +
scale_y_continuous(sec.axis = sec_axis(~./(param), name = paste0("average ", yvarsec),labels = function(x) format(x, big.mark = " ", scientific = FALSE))) +
labs(y = paste0("total ", yvar)) +
scale_alpha(guide = 'none') +
theme_pubclean() +
theme(legend.title=element_blank(), legend.background = element_rect(fill = "white"))
}
plot.ExposureYearly <- getSecPlot(freqSevDataAge, xvar = "agegroup", yvar = "exposure", yvarsec = "frequency", groupvar = "gender")
plot.ExposureYearly
How can the same be done on a plot where both the line plot as well as the bar plot are separated by gender?
Here is a possible solution. The method I used was to move the color and fill inside the aes and then use scale_*_identity to create and format the legends.
Also, I needed to add a scaling factor for severity axis since ggplot does not handle the secondary axis well.
data<-data.frame(year= 2000:2005, frequency=3:8, severity=as.integer(runif(6, 4000, 8000)))
library(ggplot2)
library(scales)
sec_plot <- ggplot(data, aes(x = year)) +
geom_col(aes(y = frequency, fill = "orange"), alpha = 0.6) +
geom_line(aes(y = severity/1000, color = "black")) +
scale_fill_identity(guide = "legend", label="Claim frequency (Number of paid claims per 100 Insured exposure)", name=NULL) +
scale_color_identity(guide = "legend", label="Claim Severity (Average insurance payment per claim)", name=NULL) +
theme(legend.position = "bottom") +
scale_y_continuous(sec.axis =sec_axis( ~ . *1, labels = label_dollar(scale=1000), name="Severity") ) + #formats the 2nd axis
guides(fill = guide_legend(order = 1), color = guide_legend(order = 2)) #control which scale plots first
sec_plot
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)