How to align geom_text labels above dodged geom_col - r

I can't seem to find a way to get the text labels on this (dodged) geom_col to line up according to their respective columns.
I have tried numerous suggestions solutions on SO and other sites, and this is the closest I could get:
How do I fix this?
Code:
ggplot(leads[leads$key_as_string <= max(leads$key_as_string) - 1, ], aes(fill = type)) +
geom_col(aes(x = key_as_string, y = doc_count),
colour = "black",
position = position_dodge(1)) +
scale_y_continuous(limits = c(0, max(leads$doc_count))) +
geom_text(aes(x = key_as_string, y = doc_count, label = doc_count, group = key_as_string),
hjust = 0.5,
vjust = -0.5,
size = 3,
colour = "black",
position = position_dodge(1)) +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
axis.text = element_text(colour = "black"))

As per my comment, group = key_as_string is the culprit here. The code is essentially telling ggplot to keep both labels with the same key_as_string value in the same group, negating the dodge command.
Illustration with the diamonds dataset below. We can see that removing the group aesthetic mapping changes the labels' positions:
p <- ggplot(diamonds %>%
filter(cut %in% c("Fair", "Good")) %>%
group_by(cut, clarity) %>%
summarise(carat = mean(carat)),
aes(clarity, carat, fill = cut, label = round(carat, 2))) +
geom_col(position = position_dodge(1))
gridExtra::grid.arrange(
p + geom_text(position = position_dodge(1), aes(group = clarity)),
p + geom_text(position = position_dodge(1)),
ncol = 1
)

Related

Replace x axis labels with colored bars to get a gradient effect in R

I'm building a barplot with RNA reads % in ggplot, I did this:
ggplot(tipos_exo,aes(x = reorder(sample, -value),y = value,fill = variable)) +
geom_bar( stat = "identity")
I need to replace the x axis labels with colored bars, each sample belongs to a specific batch and I looking for this effect:
Any thoughts?
One option to achieve your desired result would be to create your axis colorbar as a second plot and glue it to the main plot via the patchwork package.
For the colorbar I use geom_tile and remove all non-data ink using theme_void. As a first step I reorder your sample column by value and get rid of the duplicated sample categories using dplyr::distinct.
Using some fake random example data:
set.seed(123)
tipos_exo <- data.frame(
sample = rep(letters, each = 2),
variable = c("tablaq_readsPerc", "tablaq_shortReadsPerc"),
value = runif(52, 0, 100),
batch = rep(LETTERS, each = 2)
)
library(ggplot2)
library(patchwork)
library(dplyr, warn = FALSE)
p1 <- ggplot(tipos_exo,aes(x = reorder(sample, -value),y = value,fill = variable)) +
geom_bar( stat = "identity") +
scale_y_continuous(expand = c(0, 0)) +
labs(x = NULL) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.ticks.length.x = unit(0, "pt"))
tipos_exo1 <- tipos_exo |>
mutate(sample = reorder(sample, -value)) |>
distinct(sample, batch)
p_axis <- ggplot(tipos_exo1, aes(x = sample, y = factor(1), fill = batch)) +
geom_tile(width = .9) +
geom_text(aes(label = sample)) +
theme_void() +
theme(axis.title.x = element_text()) +
labs(x = "Batch Annotation") +
guides(fill = "none")
p1 / p_axis + plot_layout(heights = c(8, 1))
UPDATE Adapting my answer on this post Reorder Bars of a Stacked Barchart in R you could reorder your sample column by a helper value "column", e.g. if you want to reorder by "tablaq_readsPerc" you set the values for the other categories to zero and use FUN=sum. Note that I also reversed the order of the stack so that the "tablaq_readsPerc" bars are placed at the bottom.
tipos_exo <- tipos_exo |>
mutate(sample1 = reorder(sample, -ifelse(!variable %in% "tablaq_readsPerc", 0, value), FUN = sum))
p1 <- ggplot(tipos_exo,aes(x = sample1, y = value, fill = variable)) +
geom_bar( stat = "identity", position = position_stack(reverse = TRUE)) +
scale_y_continuous(expand = c(0, 0)) +
labs(x = NULL) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.ticks.length.x = unit(0, "pt"))
tipos_exo1 <- tipos_exo |>
distinct(sample, sample1, batch)
p_axis <- ggplot(tipos_exo1, aes(x = sample1, y = factor(1), fill = batch)) +
geom_tile(width = .9) +
geom_text(aes(label = sample)) +
theme_void() +
theme(axis.title.x = element_text()) +
labs(x = "Batch Annotation") +
guides(fill = "none")
p1 / p_axis + plot_layout(heights = c(8, 1))

Add an additional legend according to the colors of x axis labels

I have modified the colors of my x axis labels according to their group.
For that, I have used the following pseudocode:
library(ggsci)
library(ggplot2)
x_cols = pal_jco()(length(unique(melted_df$Group)))
names(x_cols) = unique(melted_df$Group)
ggplot(melted_df, ... + theme(axis.text.x = element_text(colour = x_cols))
I would like to add a legend to the plot (if possible, outside the plot), that explains the colouring of the x axis labels.
melted_df dataframe looks like this:
Here the full code:
#Generate color mapping
x_cols = pal_jco()(length(unique(melted_df$Group)))
names(x_cols) = unique(melted_df$Group)
melted_df$mycolors = sapply(as.character(melted_df$Group), function(x) x_cols[x])
#Plot
ggplot(melted_df, aes(fill=variable, y=value, x=fct_inorder(id))) +
geom_bar(position="stack", stat = "identity") + ggtitle("Barplot") +
theme_bw() +
xlab("samples") + ylab("Counts") +
theme(axis.title.y=element_text(size=10), axis.title.x=element_text(size=10),
plot.title = element_text(face = "bold", size = (15), hjust = 0.5),
axis.text.x = element_text(distinct(samples_melt[c("id", "mycolors")])$mycolors)) +
guides(fill=guide_legend(title="Columns"))
In the absence of a reproducible example, here is how you might do it with the built-in iris data set:
library(ggplot2)
ggplot(iris, aes(Species, Sepal.Length)) +
stat_summary(fun = mean, geom = "col", aes(fill = Species)) +
geom_point(aes(color = Species), alpha = 0, key_glyph = draw_key_text) +
theme_bw(base_size = 20) +
labs(color = "") +
guides(color = guide_legend(override.aes = list(alpha = 1, size = 8))) +
theme(axis.text.x = element_text(color = scales::hue_pal()(3), face = 2))
I addressed the issue using Legend() constructor, provided by ComplexHeatmap library.
I first used the code provided above under the EDIT section, and then I added the following code in order to draw an additional legend explaining the x-axis colouring.
lgd = Legend(labels = names(x_cols), title = "Group", labels_gp = gpar(fontsize = 8), nrow = 1, legend_gp = gpar(fill = x_cols))
draw(lgd, x = unit(1.8, "cm"), y = unit(0.3, "cm"), just = c("left", "bottom"))

Overlapping text on top of geom_bar in ggplot2

I have made a barplot similar to the one below using ggplot2.
I cannot get the percentages on top of the bars to be centered and not overlapping of other bars and numbers. Sample code is below.
library(tidyverse)
cat1=c("cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1","cat1",
"cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2","cat2",
"cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3","cat3",
"cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4","cat4")
cat2=c("c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
"c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
"c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12",
"c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12")
count1=round(rnorm(48,10))
fakeperc=rnorm(48,9)
df1=cbind(count1,fakeperc)
df2=cbind(cat1,cat2)
finaldf=as.data.frame(cbind(df1,df2))
finaldf$cat1=as.factor(finaldf$cat1)
finaldf$fakeperc=as.numeric(finaldf$fakeperc)
#finaldf$cat1=factor(finaldf$cat1,levels = c("cat1","cat2","cat3","cat4"))
finaldf$cat2 = factor(finaldf$cat2,
levels = c("c1","c2","c3","c4","c5","c6","c7","c8","c9","c10","c11","c12"))
a=ggplot(data=finaldf,aes(x=cat1, y=count1,
fill=cat2,group=cat2)) +
geom_bar(stat='identity',color='black',width=.65,position=position_dodge(width=.9))+
scale_y_discrete(limits=0:50,breaks=c(0,10,20,30,40,50))+
scale_fill_brewer(palette="Set3") +
theme_classic() +
geom_text(data = finaldf,
aes(x=cat1,y=count1,group=cat2,
label=format(paste(round(fakeperc),"%",sep = ""))),inherit.aes = F,
color='black',position=position_dodge(.9),vjust=-.5,size=3)
a
When trying to add either nudge_y or nudge_x to the geom_text call, nothing happens. I suspect this is because there is already a position_dodge call. I am open any and all solutions to make these percentages non-overlapping and legible.
What do you think of this?
# I think you meant count1 to be numeric
finaldf$count1 <- as.numeric(finaldf$count1)
ggplot(data = finaldf,
aes(x = cat1,
y = count1,
fill = cat2,
group = cat2)) +
geom_col(color = 'black',
width = 0.65,
position = position_dodge(width = 0.9)) +
geom_text(data = finaldf,
aes(x = cat1,
y = count1,
group = cat2,
label = scales::percent(fakeperc/100, accuracy = 0.01)),
inherit.aes = FALSE,
color = 'black',
position = position_dodge(0.9),
hjust = -0.1,
size = 3) +
scale_y_continuous(limits = c(0,50), breaks = c(0,10,20,30,40,50)) +
scale_fill_brewer(palette = "Set3") +
theme_classic() +
coord_flip()
I cleaned up a bit the code (according to my taste)
I changed scale_y_numeric to scale_y_continuous (since count1 should be numeric)
I used coord_flip() to make it more readable
I used scales::percent to write percentage numbers
(don't know why you set up limits from 0 to 50 but I left them as I suppposed they were intended)
If you don't want to use coor_flip:
finaldf$count1 <- as.numeric(finaldf$count1)
ggplot(data = finaldf,
aes(x = cat1,
y = count1,
fill = cat2,
group = cat2)) +
geom_col(color = 'black',
width = 0.65,
position = position_dodge(width = 0.9)) +
geom_text(data = finaldf,
aes(x = cat1,
y = count1,
group = cat2,
label = scales::percent(fakeperc/100, accuracy = 0.01)),
inherit.aes = FALSE,
color = 'black',
position = position_dodge(0.9),
hjust = -0.1,
angle = 90,
size = 3) +
scale_y_continuous(limits = c(0,50), breaks = c(0,10,20,30,40,50)) +
scale_fill_brewer(palette = "Set3") +
theme_classic()
Is this what you are looking for:
library(ggplot2)
#Code
ggplot(data=finaldf,aes(x=cat2, y=count1,
fill=cat2,group=cat2)) +
geom_bar(stat='identity',color='black',
position=position_dodge(width=1))+
scale_fill_brewer(palette="Set3") +
theme_bw() +
geom_text(aes(x=cat2,y=count1,group=cat2,
label=format(paste(round(fakeperc),"%",sep = ""))),inherit.aes = F,
color='black',position=position_dodge(1),
size=3,vjust=-0.5)+
facet_wrap(.~cat1,scales = 'free_x',nrow = 1,strip.position = 'bottom')+
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
legend.position = 'top',
strip.background = element_blank(),
panel.spacing = unit(2, "lines"),
panel.grid = element_blank())+
guides(fill = guide_legend(nrow = 1))
Output:

How to arrange y axis in bar chart using ggplot2

I have a following dataframe and I'am trying to plot bar chart.
country <- c('AUD','USD','GBP','ROW','EUR')
count <- c(58, 28, 8, 4, 2)
data <- data.frame(country, count)
ggplot(data = data , aes(x = 'COUNTRY', y = reorder(count, -count), fill = country))+
geom_bar(stat = "identity")+
xlab("COUNTRY")+
ylab("TOTAL")+
theme_minimal()+
geom_text(aes(label = country), vjust = -0.5, size = 3)+
scale_fill_brewer(palette="Paired")+
theme(legend.position = "bottom",
legend.title = element_blank())
Plot generated by this code does not have axis and point labels in order.
It generates below plot.
I need a help to re-arrange this axis and count labels.
It's not quite clear to me what you want the output to look like. Would something like this be ok?
ggplot(data = data , aes(x = 'COUNTRY', y = count,
fill = reorder(country, count)))+
geom_bar(stat = "identity")+
xlab("COUNTRY")+
ylab("TOTAL")+
theme_minimal()+
geom_text(aes(label = sprintf("%s (%d)", country, count),
y = cumsum(count) - 0.5*count), size = 3)+
scale_fill_brewer(palette="Paired")+
theme(legend.position = "bottom",
legend.title = element_blank())

Define different label size by category in geom_bar

I am trying to plot a stacked barchart using geom_bar and label each category with its value within the barchart. Because some categories have small values, the height of the corresponding segment of the barchart is sometimes small. So, I am trying to adjust the size of the labels using geom_text.
I have tried to define a vector, called size, that varies according to the value of the variable I am trying to plot but,although the size of the labels does vary between categories, it does not seem to be related to the values. Also, I am not sure why I am getting a legend for my label size on the right-hand side of the graph.
Here is a stripped version of the code I am using:
library(ggplot2)
library(plyr)
library(scales)
Var1 = as.factor(rep(c("A", "B", "C", "D", "E", "F"),2))
Var2 = as.factor(rep(c("Y1","Y2"),each=6))
Freq = c(0.4, 0.1, 0.3, 0.1, 0.05, 0.05,0.2,0.2,0.3,0.2,0.05,0.05)
Data = data.frame(Var1, Var2, Freq)
Data <- ddply(Data, .(Var2), mutate, y = cumsum(Freq)-Freq/2)
size = ifelse(Data$Freq > 0.05, 10, 3)
label = paste(round(Data$Freq*100,0),"%", sep = "")
p = ggplot(data = Data, aes(x = factor(''), y = Freq, fill = Var1)) +
geom_bar(stat = "identity",position = "fill", width = 1) +
scale_fill_brewer(palette = 3) +
facet_grid(facets = . ~ Var2) +
geom_text(aes(y = y, label = label,
position ="identity", face = "bold"), size = size, hjust=0.5, vjust=0.5) +
xlab('') + ylab('') + labs(fill = '') + ggtitle('Example') +
theme(axis.text.y = element_text(size=14,face="bold"),
panel.background = element_blank(),
plot.title = element_text(size = 20, colour = "black", face = "bold"))
p
As far as I can see, the issue is caused by the facets since this slightly simplified version (i.e. without the facets) works fine:
library(ggplot2)
library(plyr)
library(scales)
Var1 = as.factor(c("A", "B", "C", "D", "E", "F"))
Freq = c(0.4, 0.1, 0.3, 0.1, 0.05, 0.05)
y = cumsum(Freq)-Freq/2
Data = data.frame(Var1, Freq, y)
size = ifelse(Data$Freq > 0.05, 10, 3)
label = paste(round(Data$Freq*100,0),"%", sep = "")
p = ggplot(data = Data, aes(x = factor(''), y = Freq, fill = Var1)) +
geom_bar(stat = "identity",position = "fill", width = 1) +
scale_fill_brewer(palette = 3) +
geom_text(aes(y = y, label = label,
position ="identity", face = "bold"), size = size, hjust=0.5, vjust=0.5) +
xlab('') + ylab('') + labs(fill = '') + ggtitle('Example') +
theme(axis.text.y = element_text(size=14,face="bold"),
panel.background = element_blank(),
plot.title = element_text(size = 20, colour = "black", face = "bold"))
p
By adding a size to your data frame and moving those parameters within your geom_text aes()` this seems to plot ok for me. I think..
Data$size <- size
p <- ggplot(data = Data, aes(x = factor(''), y = Freq, fill = Var1)) +
geom_bar(stat = "identity",position = "fill", width = 1) +
scale_fill_brewer(palette = 3) +
geom_text(aes(y = y, label = label,
position ="identity", face = "bold", size = size), hjust=0.5,
vjust=0.5) +
xlab('') + ylab('') + labs(fill = '') + ggtitle('Example') +
theme(axis.text.y = element_text(size=14,face="bold"),
panel.background = element_blank(),
plot.title = element_text(size = 20, colour = "black", face = "bold")) +
facet_grid(facets = . ~ Var2) +
guides(size=FALSE)
Also if you add + guides(size=FALSE) to the end as I have done this will remove your size legend.
My explanation for this, might be wrong, is that once you facet you were still providing a full length size and not allowing facet to chop the size data according to Var2 .
I think your problem with the sizing is that size you only have two sizes you are getting big differences (there must be some relative scaling maybe), perhaps add + scale_size(range=c(6,10)) and play with this to get something more appropriate? The 6,10 size range looks much better to me.

Resources