Object not found in "geom_signif" function - r

I want to add significance stars for mean difference comparisons to a plot. Without the lines for the stars, the plot works:
da<-data.frame(group=c("condition1_high","condition1_low","condition2_high","condition2_low"),numb=c(30,25,26,20))
da %>% separate(group, c("A", "B"), remove = F) %>%
ggplot(aes(x=A, y=numb, fill = B)) +
geom_bar(position=position_dodge(), stat="identity") +
scale_fill_manual(values=rep(c("grey20","grey80"), ceiling(length(da$group)/2))[1:length(da$group)]) +
geom_text(aes(label=numb),
position = position_dodge(width = 0.9), vjust = -0.25) +
geom_signif(stat="identity",
data=data.frame(x=c(0.5,1.5), xend=c(1,2),
y=c(30, 30), annotation=c("**", "*","***","+")),
aes(x=x,xend=xend, y=y, yend=y, annotation=annotation))
Now I add a bit of code for the stars I found here on this platform:
da %>% separate(group, c("A", "B"), remove = F) %>%
ggplot(aes(x=A, y=numb, fill = B)) +
geom_bar(position=position_dodge(), stat="identity") +
scale_fill_manual(values=rep(c("grey20","grey80"), ceiling(length(da$group)/2))[1:length(da$group)]) +
geom_text(aes(label=numb),
position = position_dodge(width = 0.9), vjust = -0.25) +
geom_signif(stat="identity",
data=data.frame(x=c(0.5,1.5), xend=c(1,2),
y=c(30, 30), annotation=c("**", "*")),
aes(x=x,xend=xend, y=y, yend=y, annotation=annotation))
Now it says that object B is missing. What can I do?

You need to add inherit.aes = FALSE to the geom_signif call, otherwise it will try to find a column called B in the new data frame you defined. This is because you put an aes call inside your initial call to ggplot. When you do this, by default all subsequent geoms will inherit the aesthetics and data from this call. If you pass new data to a geom, it needs to include a value for all those aesthetics or override the aesthetics or you need to switch off inheritance with inherit.aes = FALSE
da %>%
separate(group, c("A", "B"), remove = FALSE) %>%
ggplot(aes(x = A, y = numb, fill = B)) +
geom_bar(position=position_dodge(), stat = "identity") +
scale_fill_manual(values = rep(c("grey20", "grey80"),
ceiling(length(da$group)/2))[1:length(da$group)]) +
geom_text(aes(label=numb),
position = position_dodge(width = 0.9), vjust = -0.25) +
geom_signif(stat="identity", inherit.aes = FALSE,
data=data.frame(x = c(0.5, 1.5), xend=c(1, 2),
y = c(30, 30), annotation = c("**", "*")),
aes(x = x, xend = xend, y = y, yend = y, annotation = annotation))

Related

gghighlight (R): Labeling bar charts

Alright, after a long silent read along, here's my first question. I am trying to add corresponding labels of unhighlighted items for a grouped barplot. When I insert gghighlight in front of the geom_text I get the following plot:
library(tidyverse)
library(gghighlight)
df <- data.frame (group = c("A", "A", "B", "B", "C", "C"),
value = c("value_1", "value_2","value_1", "value_2","value_1", "value_2"),
mean = c(1.331, 1.931, 3.231, 3.331, 4.631, 3.331)
)
ggplot(data = df, aes(x = group, y = mean, fill = value)) +
geom_bar(stat = "identity", position = "dodge") +
gghighlight(group != "B",
label_key = group
) +
geom_text(aes(label = round(mean, digits = 2)),
stat= "identity",
vjust = -.5,
position = position_dodge(width = .9)
)
If I move gghightlight behind the geom_text I get the following plot:
ggplot(data = df, aes(x = group, y = mean, fill = value)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = round(mean, digits = 2)),
stat= "identity",
vjust = -.5,
position = position_dodge(width = .9)
) +
gghighlight(group != "B",
label_key = group)
Is there a way to label the unhighligthed bars like the highlighted ones?
Thanks in advance.
############## EDIT ###########
Besides graying out certain columns (see #TarJae's answer), there is also the possibility to make them transparent (essential parts are from this post: ggplot transparency on individual bar):
subset_df <- df %>%
mutate(alpha.adj = as.factor(ifelse(group != "B", 1, 0.6)))
ggplot(data = subset_df, aes(x = group, y = mean, fill = value, alpha=factor(alpha.adj))) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = round(mean, digits = 2)),
stat= "identity",
vjust = -.5,
position = position_dodge(width = .9)
) +
scale_alpha_manual(values = c("0.6"=0.6, "1"=1), guide='none')
[]
Are you looking for this?
This is a solution without using gghighlight package:
library(tidyverse)
subset_df <- df %>%
mutate(highlight = if_else(group != "B", mean, NA_real_))
ggplot(data = subset_df, aes(x = group, y = mean, group=value)) +
geom_col(fill = 'grey', alpha = 0.6, position = 'dodge') +
geom_col(aes(y = highlight, fill = value), position = 'dodge') +
geom_text(aes(group, label = round(mean, digits = 2)),
position = position_dodge(width = 1))
This is a solution with the gghighlight package and some limited hacky code.
When reading the vignette, I noticed that the author of the package "filters out" the data that are not highlighted. You can see that if you save your highlighted plot in p_h and then look at p_h$data, the values for group B have disappeared.
library(tidyverse)
library(gghighlight)
p_h <- ggplot(data = df, aes(x = group, y = mean, fill = value)) +
geom_bar(stat = "identity", position = "dodge") +
gghighlight(group != "B",
label_key = group) +
geom_text(aes(label = round(mean, digits = 2)),
stat= "identity",
vjust = -.5,
position = position_dodge(width = .9))
> p_h$data
group value mean
1 A value_1 1.331
2 A value_2 1.931
5 C value_1 4.631
6 C value_2 3.331
If we re-insert the data (after the call to gghighlight() has removed them), then geom_text() will be able to find the means for group B again.
One can "recover" the data and re-insert them with the following code:
### create a ggplot object with the original complete data
### you could check that with p_to_copy_data$data
p_to_copy_data <- ggplot(data = df)
### copy the complete data to your highlighted plot data section
p_h$data <- p_to_copy_data$data
p_h
This yields the following graph:

ggplot: Order stacked barplots by variable proportion

I am creating a plot with 3 variables as below. Is there a way to arrange the plot in a descending order such that the bar with the highest proportion of variable "c" comes first in the plot. Using this example last bar should come in first then middle one and then the first bar in the last.
long<- data.frame(
Name = c("abc","abc","abc","gif","gif","gif","xyz","xyz","xyz"),
variable = c("a","b","c","a","b","c","c","b","a"),
value = c(4,6,NA,2,8,1,6,NA,NA))
long_totals <- long %>%
group_by(Name) %>%
summarise(Total = sum(value, na.rm = T))
p <- ggplot()+
geom_bar(data = long,
aes(x = Name,
y = value,
fill=variable),
stat="summary",
position = "fill") +
geom_text(data = long_totals,
aes(y = 100,
x = Name,
label = Total),
size = 7,
position = position_fill(vjust = 1.02)) +
scale_y_continuous(labels = scales::percent_format()) +
ylab("Total_num") +
ggtitle("Totalnum") +
theme(plot.title = element_text(size = 20, hjust = 0.5)) +
theme(axis.text.x = element_text(angle = 75, vjust = 0.95, hjust=1))
The following code does arrange the bars by count of "c" but not by proportion. How can I arrange by proportion?
p<-long %>%
mutate(variable = fct_relevel(variable,
c("c", "b", "a"))) %>%
arrange(variable) %>%
mutate(Name = fct_inorder(Name))
p %>%
ggplot() +
aes(x = Name,
y = value,
fill = variable) +
geom_bar(position = "fill",
stat = "summary") +
We could use fct_rev from forcats package, it is in tidyverse:
p <- ggplot()+
geom_bar(data = long,
aes(x = fct_rev(Name),
y = value,
fill=variable),
stat="summary",
position = "fill") +
geom_text(data = long_totals,
aes(y = 100,
x = Name,
label = Total),
size = 7,
position = position_fill(vjust = 1.02)) +
scale_y_continuous(labels = scales::percent_format()) +
ylab("Total_num") +
ggtitle("Totalnum") +
theme(plot.title = element_text(size = 20, hjust = 0.5)) +
theme(axis.text.x = element_text(angle = 75, vjust = 0.95, hjust=1))

ggplot inside renderPlot cannot recognise input$myvariable

I have a ggplot inside of a renderPlot function. This code block works as expected:
output$revenue_channel <- renderPlot({
ggplot(untrended_data(), aes(x = reorder(Channel, Revenue), y = Revenue), label = Revenue) +
geom_bar(stat="identity", fill = "#008080", alpha = 0.6) +
coord_flip() +
geom_text(aes(label = scales::dollar(Revenue)), hjust= 1.2, color = "white") +
scale_y_continuous(label = scales::label_dollar(scale = 0.001, suffix = "K")) +
xlab("") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
}, height = 300)
This code block as is runs and produces this chart:
But, in my I actually have a breakdown selector where the user ca enter one of Channel, Device or UserType.
So, this line:
ggplot(untrended_data(), aes(x = reorder(Channel, Revenue), y = Revenue), label = Revenue)
Would become either this:
ggplot(untrended_data(), aes(x = reorder(Device, Revenue), y = Revenue), label = Revenue)
Or this:
ggplot(untrended_data(), aes(x = reorder(UserType, Revenue), y = Revenue), label = Revenue)
I tried just switching in input$myinput like so
ggplot(untrended_data(), aes(x = reorder(input$breakdown, Revenue), y = Revenue), label = Revenue)
But this gives an error:
Error: arguments must have same length
I then tried aes_ for aes_string():
ggplot(untrended_data(), aes_(x = reorder(input$breakdown, "Revenue"), y = "Revenue"), label = Revenue)
Discrete value supplied to continuous scale
I then tried t make sense of this page on quasi-quotation and tried:
ggplot(untrended_data(), aes(x = reorder(!! input$breakdown, Revenue), y = Revenue), label = Revenue)
Which resulted in:
Error: arguments must have same length
How can I pass input$breakdown into ggplot within renderPlot({})?
The input$breakdown stores a string as value, and if we convert it to a symbol (using rlang::sym) and then evaluate (!!) it would work, e.g.
library(ggplot2)
v1 <- "mpg";
ggplot(mtcars, aes(x = reorder(!! rlang::sym(v1), cyl), y = cyl, label = cyl))+
geom_bar(stat="identity", fill = "#008080", alpha = 0.6) +
coord_flip()
In the OP's code block, we need to use reorder(!! rlang::sym(input$breakdown), Revenue)
output$revenue_channel <- renderPlot({
ggplot(untrended_data(), aes(x = reorder(!! rlang::sym(input$breakdown), Revenue),
y = Revenue), label = Revenue) +
geom_bar(stat="identity", fill = "#008080", alpha = 0.6) +
coord_flip() +
geom_text(aes(label = scales::dollar(Revenue)), hjust= 1.2, color = "white") +
scale_y_continuous(label = scales::label_dollar(scale = 0.001, suffix = "K")) +
xlab("") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
}, height = 300)

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)

stacked bar *bringing labels to the graph *

I'm plotting a stacked bar graph and use geom_text to insert the value and name of each stack. The problem is some stacks are very small/narrow, so that the text of two stacks overlap each other and hence is not very readable. How can I modify the code to solve this issue.
Type<-c("ddddddddddd","ddddddddddd","bbbbbbbbbbbbb","ddddddddddd","eeeeeeeeeeeeee","bbbbbbbbbbbbb","ddddddddddd","bbbbbbbbbbbbb","ddddddddddd",
"eeeeeeeeeeeeee","mmmmmmmmmmmmmmmmmmm","bbbbbbbbbbbbb","ddddddddddd","bbbbbbbbbbbbb","eeeeeeeeeeeeee")
Category<-c("mmmmm","mmmmm","gggggggggggggggggg","ffffffffffff","ffffffffffff","ffffffffffff","sanddddddddd","sanddddddddd","yyyyyyyyyyy",
"yyyyyyyyyyy","yyyyyyyyyyy","sssssssssssssss","sssssssssssssss","sssssssssssssss","ttttttttttttt")
Frequency<-c(4,1,30,7,127,11,1,1,6,9,1,200,3,4,5)
Data <- data.frame(Type, Category, Frequency)
p <- ggplot(Data, aes(x = Type, y = Frequency)) +
geom_bar(aes(fill = Category), stat="identity", show.legend = FALSE) +
geom_text(aes(label = Frequency), size = 3) +
geom_text(aes(label = Category), size = 3)
Considering your data, a facetted plot might be a better approach:
# summarise your data
library(dplyr)
d1 <- Data %>%
mutate_each(funs(substr(.,1,2)),Type,Category) %>%
group_by(Type,Category) %>%
summarise(Freq = sum(Frequency)) %>%
mutate(lbl = paste(Category,Freq)) # create a label by pasting the 'Category' and the 'Freq' variables together
# plot
ggplot(d1, aes(x = Category, y = Freq, fill = Category)) +
geom_bar(stat="identity", width = 0.7, position = position_dodge(0.8)) +
geom_text(aes(label = lbl), angle = 90, size = 5, hjust = -0.1, position = position_dodge(0.8)) +
scale_y_continuous(limits = c(0,240)) +
guides(fill = FALSE) +
facet_grid(.~Type, scales = "free", space = "free") +
theme_bw(base_size = 14)
which gives:
In the above plot I shortened the labels on purpose. If you don't want to do that, you could consider this:
d2 <- Data %>%
group_by(Type,Category) %>%
summarise(Freq = sum(Frequency)) %>%
mutate(lbl = paste(Category,Freq))
ggplot(d2, aes(x = Category, y = Freq, fill = Category)) +
geom_bar(stat="identity", width = 0.7, position = position_dodge(0.8)) +
geom_text(aes(y = 5, label = lbl), alpha = 0.6, angle = 90, size = 5, hjust = 0, position = position_dodge(0.8)) +
scale_y_continuous(limits = c(0,240)) +
guides(fill = FALSE) +
facet_grid(.~Type, scales = "free", space = "free") +
theme_bw(base_size = 14) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
which gives:

Resources