I am doing a plot using ggplot2
and a small dataframe df
. My dataframe has a group variable Letter
and two numeric variables X
and Y
(I include the dput()
version of df
at the end of this post).
My main issue appears when I try to align the labels of the bars on top. Because of my design, one variable needs to be showed to right side and the other to left side. That is why I multiply some values by -1. This is the code and output for my plot:
library(tidyverse)
library(ggplot2)
#Plot
df %>%
pivot_longer(-c(Letter)) %>%
mutate(value=ifelse(name=='X',value*-1,value)) %>%
ggplot(aes(x=Letter,y=value,fill=name))
geom_bar(stat = 'identity',color='black',alpha=0.7)
geom_text(aes(label=format(abs(value),big.mark = '.')),
size=3,fontface='bold')
scale_x_discrete(limits = rev(unique(df$Letter)))
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
breaks = scales::pretty_breaks(10))
coord_flip()
And the output:
As you can see the plot is fine but the problem is with the labels. In both left and right sides, a part of the label is inside the bar and the other is outside. I would like to have the labels in both sides at the top of each bar. I do not know if this is possible because some values are positive and other negatives. I have tried adding hjust
to geom_text()
and this only works for left side:
#Plot 1
df %>%
pivot_longer(-c(Letter)) %>%
mutate(value=ifelse(name=='X',value*-1,value)) %>%
ggplot(aes(x=Letter,y=value,fill=name))
geom_bar(stat = 'identity',color='black',alpha=0.7)
geom_text(aes(label=format(abs(value),big.mark = '.')),
size=3,fontface='bold',
hjust=1)
scale_x_discrete(limits = rev(unique(df$Letter)))
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
breaks = scales::pretty_breaks(10))
coord_flip()
Output:
I would like to find a way to have the labels on both sides aligned to the top of their respective bars.
Many thanks for the help. The dput()
of my data is next:
#Data
df <- structure(list(Letter = c("A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S"),
X = c(3099, 14201, 18901, 17351, 29498, 28928, 32284, 31536,
25334, 20008, 39242, 35856, 43317, 53765, 44652, 32876, 26142,
18567, 8836), Y = c(9482, 22669, 11159, 9506, 12113, 12805,
10570, 10199, 9190, 9217, 10674, 10894, 11986, 15664, 12793,
10552, 8050, 8609, 7717)), row.names = c(NA, -19L), class = c("tbl_df",
"tbl", "data.frame"))
CodePudding user response:
You can actually set the hjust
as an aesthetic variable, instead of giving all labels the same hjust value. Just give it a 1 for negative values and a 0 for positive values by doing hjust = value < 0
inside the aes
call - the resulting logical vector will be implicitly converted to 1s and 0s:
df %>%
pivot_longer(-c(Letter)) %>%
mutate(value = ifelse(name == 'X', value * -1, value)) %>%
ggplot(aes(x = Letter, y = value, fill = name))
geom_bar(stat = 'identity', color = 'black', alpha = 0.7)
geom_text(aes(label = format(abs(value), big.mark = '.'), hjust = value < 0),
size = 3, fontface = 'bold')
scale_x_discrete(limits = rev(unique(df$Letter)))
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
breaks = scales::pretty_breaks(10))
coord_flip()
CodePudding user response:
try to split geom_text by value type (>0,<0). see below:
df %>%
pivot_longer(-c(Letter)) %>%
mutate(value=ifelse(name=='X',value*-1,value)) %>%
ggplot(aes(x=Letter,y=value,fill=name))
geom_bar(stat = 'identity',color='black',alpha=0.7)
geom_text(data = . %>% filter(value>0), aes(label=format(abs(value),big.mark = '.')),
size=3,fontface='bold',hjust=0)
geom_text(data = . %>% filter(value<0), aes(label=format(abs(value),big.mark = '.')),
size=3,fontface='bold',hjust= 1)
scale_x_discrete(limits = rev(unique(df$Letter)))
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
breaks = scales::pretty_breaks(10))
coord_flip()
CodePudding user response:
Here is an alternative approach, with an ifelse
statement:
library(tidyverse)
#Plot
df1 <- df %>%
pivot_longer(-c(Letter)) %>%
mutate(value=ifelse(name=='X',value*-1,value))
ggplot(df1, aes(x=Letter,y=value,fill=name))
geom_bar(stat = 'identity',color='black',alpha=0.7)
geom_text(aes(label = format(value, big.mark = '.'),
vjust = ifelse(value >= 0, 0.5, 0.5),
hjust = ifelse(value>= 0, -0.05, 1.05)),
size=3,fontface='bold')
scale_x_discrete(limits = rev(unique(df$Letter)))
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
breaks = scales::pretty_breaks(10))
coord_flip()