Make a line separated by group in bar chart - r

I am trying to overlay two sets of data that with be used in bar charts. The first is the main set of data and I want that to be the main focus. For the second dataset I want just a line marking where on the chart it would be. I can get close to what I want by doing this:
Tbl = data.frame(Factor = c("a","b","c","d"),
Percent = c(43,77,37,55))
Tbl2 = data.frame(Factor = c("a","b","c","d"),
Percent = c(58,68,47,63))
ggplot(aes(x = Factor, y = Percent), data = Tbl) +
geom_bar(position = "stack", stat = "identity", fill = "blue") +
ylim(0,100) +
geom_bar(aes(x = Factor, y = Percent), data = Tbl2,
position = "stack", stat = "identity", fill = NA, color = "black") +
theme_bw()
What I have so far
I believe I can accomplish what I want by using geom_vline if there is a way to separate it by groups. Another option I came up with is if it is possible to change the colors of the "sides" of the bars in the overlay to white while keeping the "top" of each bar chart as black.
An idea of what I want (Edited in paint)

You could use geom_errorbar where the ymin and ymax are the same values like this:
library(ggplot2)
ggplot(aes(x = Factor, y = Percent), data = Tbl) +
geom_bar(position = "stack", stat = "identity", fill = "blue") +
ylim(0,100) +
geom_errorbar(aes(x = Factor, ymin = Percent, ymax = Percent), data = Tbl2,
stat = "identity", color = "black") +
theme_bw()
Created on 2022-12-28 with reprex v2.0.2

Another option is geom_point with shape = 95 (line) and the size adjusted to suit:
library(tidyverse)
Tbl = data.frame(Factor = c("a","b","c","d"),
Percent = c(43,77,37,55))
Tbl2 = data.frame(Factor = c("a","b","c","d"),
Percent = c(58,68,47,63))
ggplot(aes(x = Factor, y = Percent), data = Tbl) +
geom_bar(position = "stack", stat = "identity", fill = "blue") +
ylim(0,100) +
geom_point(aes(x = Factor, y = Percent), data = Tbl2,
position = "stack", stat = "identity", color = "black", shape = 95, size = 30) +
theme_bw()
Created on 2022-12-28 with reprex v2.0.2

Here is one more using geom_segment(). Some would say to fancy, but anyway:
For this we have to extend Tbl2:
library(ggplot2)
library(dplyr)
ggplot(aes(x = Factor, y = Percent), data = Tbl) +
geom_bar(position = "stack", stat = "identity", fill = "blue") +
ylim(0,100) +
geom_segment(data = Tbl2 %>%
mutate(x = c(0.55, 1.55, 2.55, 3.55),
xend = x+0.9), aes(x=x,xend=xend, y = Percent, yend=Percent), size=2)+
theme_bw()

Related

how to have the legend inside a grouped bar graph in R ggplot?

So my legend here is village which has (Chirodzo, God, Ruaca). How to remove the legend and display it inside the bars; for instance inside the bar for chirodzo, I want chirodzo written inside?
ggplot(data = interviews_plotting, aes(x = respondent_wall_type, fill = village)) +
geom_bar(position = "fill")
Source is here https://mq-software-carpentry.github.io/r-ggplot-extension/02-categorical-data/index.html
ggplot(data = interviews_plotting, aes(x = respondent_wall_type, fill = village)) +
geom_bar(position = "fill")
To label your bars with the fill category and getting rid of the legend you could use geom_text like so:
Using mtcars as example data:
library(ggplot2)
ggplot(data = mtcars, aes(x = am, fill = factor(cyl))) +
geom_bar(position = "fill") +
geom_text(aes(label = cyl), stat = "count", position = position_fill(vjust = 0.5)) +
guides(fill = "none")
From your comments, it sounds like you are looking for something like this:
library(ggplot2)
ggplot(interviews_plotting, aes(x = respondent_wall_type, fill = village)) +
geom_bar(position = position_dodge()) +
geom_text(stat = 'count',
aes(y = stat(count)/2, label = village, group = village),
position = position_dodge(width = 1), angle = 90) +
guides(fill = guide_none())
Or, if you want to get a bit more sophisticated with your label placement and theme choices:
library(ggplot2)
ggplot(interviews_plotting, aes(x = respondent_wall_type, fill = village)) +
geom_bar(position = position_dodge(width = 0.9), width = 0.8) +
geom_text(stat = 'count', size = 6,
aes(y = ifelse(stat(count) > 2, stat(count)/2, stat(count)),
label = village, group = village,
hjust = ifelse(stat(count) > 2, 0.5, -0.2)),
position = position_dodge(width = 0.9), angle = 90) +
labs(x = 'Wall type', y = 'Count') +
theme_minimal(base_size = 16) +
scale_fill_brewer(palette = 'Set2', guide = 'none')
Data used
interviews_plotting <- read.csv(paste0("https://raw.githubusercontent.com/",
"humburg/r-ggplot-project/master/",
"data_output/interviews_plotting.csv"))

Percentages for geom_text in stacked barplot with counts

I want to have a stacked barplot with percentages in it based on counts.
I have almost reached what I want but every value in the text is 100% instead of the real percentage ...
I think there is one small mistake in my code but I can not find it.
ggplot(
mtcars,
aes(fill = factor(gear),
x = factor(carb))
) +
geom_bar(stat = "count",
position = "fill",
color = "black",
width = 0.5) +
geom_text(aes(label = scales::percent(..prop..)),
position = position_fill(vjust = 0.5),
stat = "count") +
coord_flip()
Building on this answer
You can use this:
ggplot(
mtcars,
aes(fill = factor(gear),
x = factor(carb))
) +
geom_bar(stat = "count",
position = "fill",
color = "black",
width = 0.5) +
geom_text(aes(label = scales::percent(..count../tapply(..count.., ..x.. ,sum)[..x..])),
position = position_fill(vjust = 0.5),
stat = "count") +
coord_flip()

How to apply position_dodge to geom_point and geom_text in the same plot?

I woul like to be able to make the geom_text inside the geom_point to follow the re-positioning when applying position_dodge. That is, I would like to go from the code below:
Q <- as_tibble(data.frame(series = rep(c("diax","diay"),3),
value = c(3.25,3.30,3.31,3.36,3.38,3.42),
year = c(2018,2018,2019,2019,2020,2020))) %>%
select(year, series, value)
ggplot(data = Q, mapping = aes(x = year, y = value, color = series, label = sprintf("%.2f",value))) +
geom_point(size = 13) +
geom_text(vjust = 0.4,color = "white", size = 4, fontface = "bold", show.legend = FALSE)
which produces the following chart:
to the following change:
ggplot(data = Q, mapping = aes(x = year, y = value, color = series, label = sprintf("%.2f",value))) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(position = position_dodge(width = 1), vjust = 0.4,
color = "white", size = 4, fontface = "bold",
show.legend = FALSE)
which produces the following chart:
The curious thing about this is the fact that excatly the same change works just fine if I change from geom_point to geom_bar:
ggplot(Q, aes(year, value, fill = factor(series), label = sprintf("%.2f",value))) +
geom_bar(stat = "identity", position = position_dodge(width = 1)) +
geom_text(color = "black", size = 4,fontface= "bold",
position = position_dodge(width = 1), vjust = 0.4, show.legend = FALSE)
This happens because the the dodging is based on the group aesthetic, automatically set in this case to series because of the mapping to color. The issue is that the text layer has it's own color ("white") and so the grouping is dropped. Manually set the grouping, and all is good:
ggplot(Q, aes(x = year, y = value, color = series, label = sprintf("%.2f",value), group = series)) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(position = position_dodge(width = 1), vjust = 0.4, color = "white", size = 4,
fontface = "bold", show.legend = FALSE)
One patch work would be the following. Since you cannot add labels on top of the data point using geom_text() right away, you may want to go round a bit. I first created a temporary graphic with geom_point(). Then, I accessed to the data frame which is used for drawing the graphic. You can find the values of x and y axis. Using them, I created a new data frame called temp which include the axis information and the label information. Once I had this data frame, I could draw the expected outcome using temp. Make sure that you use inherit.aes = FALSE in geom_text() since you are using another data frame.
library(dplyr)
library(ggplot2)
g <- ggplot(data = Q, aes(x = year, y = value, color = series)) +
geom_point(size = 13, position = position_dodge(width = 1))
temp <- as.data.frame(ggplot_build(g)$data) %>%
select(x, y) %>%
arrange(x) %>%
mutate(label = sprintf("%.2f",Q$value))
ggplot(data = Q, aes(x = year, y = value, color = series)) +
geom_point(size = 13, position = position_dodge(width = 1)) +
geom_text(data = temp, aes(x = x, y = y, label = label),
color = "white", inherit.aes = FALSE)

Change the shape of legend key for geom_bar in ggplot2

I'm trying to change the shape of the legend key from a geom_bar graph. I've looked at multiple answers online but found they didn't work in this case. Let me explain the problem:
df1 = data.frame(person = c("person1", "person2", "person3"),
variable = "variable1",
value = c(0.5, 0.3, 0.2))
df2 = data.frame(person = c("person1", "person2", "person3"),
variable = "variable2",
value = c(-0.3, -0.1, -0.4))
I'm trying to make a stacked barplot where one side is negative. Using ggplot2 I get:
library(ggplot2)
ggplot() + geom_bar(data = df1, aes(x = person, y = value, fill = variable), stat = "identity") +
geom_bar(data = df2, aes(x = person, y = value, fill = variable), stat = "identity") +
scale_fill_manual(values = c("steelblue", "tomato"), breaks = c("variable1","variable2"),
labels = c("Variable 1", "Variable 2"))
It then looks like this:
Now on the right the legend shows squares by default. Is there a way to change this into a circle for instance?
Online I've found the way this usually works is by using
guides(fill = guide_legend(override.aes = list(shape = 1)))
Or similar variations. However this doesn't seem to work. If anybody can help that would be great, I've been stuck for quite a while now.
You could add a layer of geom_point with no data (just to create a legend) and hide the unwanted rectangular legend from the bars using show.legend = FALSE:
df3 = data.frame(person = as.numeric(c(NA, NA)),
variable = c("variable1", "variable2"),
value = as.numeric(c(NA, NA)))
ggplot() +
geom_bar(data = df1, aes(x = person, y = value, fill = variable), stat = "identity", show.legend = FALSE) +
geom_bar(data = df2, aes(x = person, y = value, fill = variable), stat = "identity", show.legend = FALSE) +
geom_point(data = df3, aes(x = person, y = value, color = variable), size=8) +
scale_fill_manual(values = c("steelblue", "tomato"), breaks = c("variable1","variable2")) +
scale_color_manual(values = c("steelblue", "tomato")) +
theme(legend.key = element_blank())

stacked geom_bar issue with stacked bar and labels misplaced

I have this bar chart:
group = c("A","A","B","B")
value = c(25,-75,-40,-76)
day = c(1,2,1,2)
dat = data.frame(group = group , value = value, day = day)
ggplot(data = dat, aes(x = group, y = value, fill = factor(day))) +
geom_bar(stat = "identity", position = "identity")+
geom_text(aes(label = round(value,0)), color = "black", position = "stack")
and I'd like the bars stacked and the values to show up. When I run the code above the -76 is not in the correct location (and neither is the 75 it seems).
Any idea how to get the numbers to appear in the correct location?
ggplot(data=dat, aes(x=group, y=value, fill=factor(day))) +
geom_bar(stat="identity", position="identity")+
geom_text(label =round(value,0),color = "black")+
scale_y_continuous(breaks=c(-80,-40,0))
Stacking a mix of negative and positive values is difficult for ggplot2. The easiest thing to do is to split the dataset into two, one for positives and one for negatives, and then add bar layers separately. A classic example is here.
You can do the same thing with the text, adding one text layer for the positive y values and one for the negatives.
dat1 = subset(dat, value >= 0)
dat2 = subset(dat, value < 0)
ggplot(mapping = aes(x = group, y = value, fill = factor(day))) +
geom_bar(data = dat1, stat = "identity", position = "stack")+
geom_bar(data = dat2, stat = "identity", position = "stack") +
geom_text(data = dat1, aes(label = round(value,0)), color = "black", position = "stack") +
geom_text(data = dat2, aes(label = round(value,0)), color = "black", position = "stack")
If using the currently development version of ggplot2 (2.1.0.9000), the stacking doesn't seem to be working correctly in geom_text for negative values. You can always calculate the text positions "by hand" if you need to.
library(dplyr)
dat2 = dat2 %>%
group_by(group) %>%
mutate(pos = cumsum(value))
ggplot(mapping = aes(x = group, y = value, fill = factor(day))) +
geom_bar(data = dat1, stat = "identity", position = "stack")+
geom_bar(data = dat2, stat = "identity", position = "stack") +
geom_text(data = dat1, aes(label = round(value,0)), color = "black") +
geom_text(data = dat2, aes(label = round(value,0), y = pos), color = "black")

Resources