I tried the following code but instead of showing the label in middle, I want to point it to leftmost (you can see my picture under). Thanks for the help!
library(tidyverse)
library(ggrepel)
mtcars %>%
group_by(am, cyl) %>%
slice(1) %>%
ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl, label = mpg)) +
geom_bar(position = "dodge", stat = "identity") +
geom_label_repel(data = mtcars %>% filter(am == 0,
cyl == 4) %>%
slice(1),
nudge_x = 0.2,
nudge_y = 0.3,
aes(fill = NULL))
Created on 2019-01-22 by the reprex package (v0.2.1)
This solution will work generally, for any combination of labels.
First, it seems as if the am variable is a binary factor, so I code it as a factor so the x axis is a little cleaner. In your case, you are looking for position_dodge(width = 1) for your labels, which automatically lines the labels up on top of the corresponding bars. The way I thought was best for subsetting to only the one label you want us ti define a column mpg_lab which is NA for all the labels you don't want. If you change the conditions in the last mutate row you can isolate different labels, or delete that row altogether for all the labels.
df <- mtcars %>%
mutate(am = factor(am)) %>%
group_by(am, cyl) %>%
slice(1) %>%
mutate(mpg_lab = ifelse(am == 0 & cyl == 4, mpg, NA))
df %>%
ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl)) +
geom_bar(position = "dodge", stat = "identity") +
geom_label_repel(data = df,
aes(label = mpg_lab, fill = NULL), position = position_dodge(width = 1), point.padding = NA, ylim = max(df$mpg_lab, na.rm = T) * 1.02)
A couple of optional things I added was turning off point.padding in geom_label_repel to keep the labels from randomly moving around side-to-side. I also bumped the label up so you can see the arrow by using the ylim argument in there. You can play around with those options if you want something different.
On way is to use geom_text instead. You don't get the white background around the label, but you can position it exactly:
mtcars %>%
group_by(am, cyl) %>%
slice(1) %>%
ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl, label = mpg)) +
geom_bar(position = "dodge", stat = "identity") +
geom_text(data = mtcars %>% filter(am == 0,cyl == 4) %>% slice(1),
nudge_x = -0.3,
nudge_y = 0.7,
aes(fill = NULL))
Edit:
As pointed out, geom_label will make the white background, which is a much nicer option :P
Related
I have been attempting to add a label on top of each bar to represent the proportion that each ethnic group makes up in referrals.
For some reason I cannot get the labels to be placed at the top of each bar. How do I fix this?
My code below
freq <- df %>%
group_by(ethnicity) %>%
summarise(n = n()) %>%
mutate(f = round((n/sum(n)*100, 1))
df %>%
group_by(pathway) %>%
count(ethnicity) %>%
ggplot(aes(x = ethnicity, y = n , fill = pathway)) +
geom_bar(stat = "identity", position = "stack") +
geom_text(data = freq,
aes(x= ethnicity, y = f, label = f),
inherit.aes = FALSE) +
theme(legend.position = "bottom") +
scale_fill_manual(name = "",
values = c("light blue", "deepskyblue4"),
labels = "a", "b") +
xlab("") +
ylab("Number of Referrals") +
scale_y_continuous(breaks = seq(0, 2250, 250), expand = c(0,0)
Here is what it currently looks like
Since you are using the count as your y-axis position in geom_bar, you need to use the same thing in your geom_text to get the labels in the right place. Below is an example using mtcars dataset. Using vjust = -1 I put a little bit of space between the label and the bars to make it more legible and aesthetically pleasing.
library(tidyverse)
mtcars %>%
group_by(carb) %>%
summarise(n = n()) %>%
mutate(f = round(proportions(n) * 100, 1)) -> frq
mtcars %>%
group_by(gear) %>%
count(carb) -> df
df %>%
ggplot(aes(x = carb, y = n, fill = gear)) +
geom_bar(stat = "identity", position = "stack") +
geom_text(data = frq,
vjust = -1,
aes(x= carb, y = n, label = f),
inherit.aes = FALSE)
Created on 2022-10-31 by the reprex package (v2.0.1)
I tried this code without faceting, it works.
I want to add counts on each bar and use facets in my plot, it brokes. I managed to make it close to what I want, like this:
mtcars %>% group_by(gear, am, vs) %>% summarize(hp_sum = sum(hp), hp = hp) %>%
ggplot(aes(gear, hp_sum, fill = factor(am))) + facet_grid(.~vs) +
geom_bar(stat = 'identity', position = 'dodge', alpha = 0.5, size = 0.25) +
geom_text(aes(label=..count.., y = ..count..), stat='count', position = position_dodge(width = 0.95), size=4)
But I want the number on top of each bar. If I use y = hp_sum, I got error:
Error: stat_count() can only have an x or y aesthetic.
Run `rlang::last_error()` to see where the error occurred.
I might have format the dataset in the wrong way. Any ideas? Thanks!
I learned from this post that geom_text does not do counts by groups.
A solution is to do the summary beforehand:
mtcars %>% group_by(gear, am, vs) %>%
summarize(hp_sum = sum(hp), count = length(hp)) %>%
ggplot(aes(gear, hp_sum, fill = factor(am))) + facet_grid(.~vs) +
geom_bar(stat = 'identity', position = 'dodge', alpha = 0.5, size = 0.25) +
geom_text(aes(gear, hp_sum, label = count),
position = position_dodge(width = 0.95), size=4)
Be sure to group data the same way in the plot. Here x=gear, facet_grid(.~vs), fill = factor(am) are three factors putting y=hp into groups. So you should group this way: group_by(gear, am, vs). Hope this helps anyone who is struggling with this issue.
plot example
I'm tryng to add label to a grouped bar plot in r.
However I'm using percentege in the y axis, and I want the label to be count.
I've tried to use the geom_text() function, but I don't how exacly the parameters i need to use.
newdf3 %>%
dplyr::count(key, value) %>%
dplyr::group_by(key) %>%
dplyr::mutate(p = n / sum(n)) %>%
ggplot() +
geom_bar(
mapping = aes(x = key, y = p, fill = value),
stat = "identity",
position = position_dodge()
) +
scale_y_continuous(labels = scales::percent_format(),limits=c(0,1))+
labs(x = "", y = "%",title="")+
scale_fill_manual(values = c('Before' = "deepskyblue", 'During' = "indianred1", 'After' = "green2", '?'= "mediumorchid3"),
drop = FALSE, name="")
Here is an exemple of how I need it:
here's a sample of data I'm using:
key value
A Before
A After
A During
B Before
B Before
C After
D During
...
I also wanted to keep the bars with no value (label = 0).
Can someone help me with this?
Here is MWE of how to add count labels to a simple bar chart. See below for the case when these are grouped.
library(datasets)
library(tidyverse)
data <- chickwts %>%
group_by(feed) %>%
count %>%
ungroup %>%
mutate(p = n / sum(n))
ggplot(data, aes(x = feed, y = p, fill = feed)) +
geom_bar(stat = "identity") +
geom_text(stat = "identity",
aes(label = n), vjust = -1)
You should be able to do the same thing on your data.
EDIT: StupidWolf points out in the comments that the original example has grouped data. Adding position = position_dodge(0.9) in geom_text deals with this.
Again, no access to the original data, but here's a different MWE using mtcars showing this:
library(datasets)
library(tidyverse)
data <- mtcars %>%
as_tibble %>%
transmute(gear = as_factor(gear),
carb = as_factor(carb),
cyl = cyl) %>%
group_by(gear, carb) %>%
count
ggplot(data, aes(x = gear, y = n, fill = carb)) +
geom_bar(stat = "identity",
position = "dodge") +
geom_text(aes(label = n),
stat = "identity",
vjust = -1,
position = position_dodge(0.9))
I thought I understood aligning text to plots but this one has me stumped. I want to use position_fill() to show the percentage shares of groups, but include the group counts with geom_text to indicate that different groups have different numbers of observations. Because some groups have very small shares, I decided to just have the labels be fixed at either end of the plot.
Here is my first attempt, using an example group variable long_sepal from iris that is just whether or not the Sepal.Length is bigger than 5.5.
I make a custom y_label variable that is either 0 or 1 to map to the y aesthetic of geom_text, so that the labels are always at the extremes of the plot.
library(tidyverse)
iris %>%
mutate(long_sepal = Sepal.Length > 5.5) %>%
count(Species, long_sepal) %>%
mutate(y_label = if_else(long_sepal, 0, 1)) %>%
ggplot(aes(x = Species)) +
geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
geom_text(
mapping = aes(label = n, y = y_label, group = long_sepal),
hjust = 0,
position = position_fill()
) +
coord_flip()
All well and good, but the FALSE labels are hanging off the edge of the plot. No problem, I'll just change the values of y_label:
iris %>%
mutate(long_sepal = Sepal.Length > 5.5) %>%
count(Species, long_sepal) %>%
mutate(y_label = if_else(long_sepal, 0, 0.5)) %>% # This has changed
ggplot(aes(x = Species)) +
geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
geom_text(
mapping = aes(label = n, y = y_label, group = long_sepal),
hjust = 0,
position = position_fill()
) +
coord_flip()
And nothing has changed. Interestingly, it seems to work, sort of, on changing the first value in if_else but not the second, as below. The left labels will move but don't seem to align with 0.25 as I would expect. Any ideas why? Is my mental model of how geoms work breaking here? I suspect it is something to do with position_fill but I am not sure.
iris %>%
mutate(long_sepal = Sepal.Length > 5.5) %>%
count(Species, long_sepal) %>%
mutate(y_label = if_else(long_sepal, 0.25, 1)) %>% # Now the text moves, but not where I expect
ggplot(aes(x = Species)) +
geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
geom_text(
mapping = aes(label = n, y = y_label, group = long_sepal),
hjust = 0,
position = position_fill()
) +
coord_flip()
Created on 2019-03-15 by the reprex package (v0.2.1)
Using the comments, especially from Axeman, I realised that I can use position_identity() to get the desired result:
library(tidyverse)
iris %>%
mutate(long_sepal = Sepal.Length > 5.5) %>%
count(Species, long_sepal) %>%
ungroup() %>%
mutate(y_label = if_else(long_sepal, 0.01, 0.99)) %>%
ggplot(aes(x = Species)) +
geom_col(aes(y = n, fill = long_sepal), position = position_fill()) +
geom_text(
mapping = aes(label = n, y = y_label, group = long_sepal),
hjust = "inward",
position = position_identity()
) +
coord_flip()
Created on 2019-03-15 by the reprex package (v0.2.1)
I need to generate a plot with bar graph for two variables and a line with the third variable.
I can create a column graph for one variable like below
df <- head(mtcars)
df$car <- row.names(df)
ggplot(df) + geom_col(aes(x=car, y=disp))
Ref this answer - I can plot two variables - disp and hp as below
library(tidyr)
df$car = row.names(df)
df_long = gather(df, key = var, value = value, disp, hp)
ggplot(df_long, aes(x = car, y = value, fill = var)) +
geom_bar(stat = 'identity', position = 'dodge')
I need to have a third variable qsec plotted as a line like as in the below chart - how to go about this ?
You can try:
library(tidyverse)
# some data
data <- mtcars %>%
mutate(car = rownames(mtcars)) %>%
slice(1:6) %>%
select(car, disp, hp)
data %>%
gather(key, value, -car) %>%
group_by(car) %>%
mutate(qsec_value = median(value)) %>%
mutate(qsec = factor("qsec")) %>%
ggplot() +
geom_col(aes(x=car, y=value, fill = key), position = "dodge") +
geom_point(aes(x=car, y=qsec_value,color = qsec)) +
geom_line(aes(x=car, y=qsec_value, color = qsec, group =1)) +
scale_colour_manual(name= "", values = 1) +
theme(legend.position = "top",
legend.title = element_blank())
Less code, same result:
data %>%
pivot_longer(-1) %>%
ggplot(aes(x = car)) +
geom_col(aes(y=value, fill = name), position = "dodge") +
stat_summary(aes(y=value, group=1, color="qseq"), fun = "median", geom = "point")+
stat_summary(aes(y=value, group=1, color="qseq"), fun = "median", geom = "line")+
scale_colour_manual(name= "", values = 1)
You need another layer and because geom_line is for continuous data, you need to do as if your x-values are for the line-layer. By doing so, order of data becomes crucial, hence you have also to sort it:
gather(df, key = var, value = value, disp, hp, qsec) %>%
arrange(car) %>%
{
print(
ggplot() +
geom_bar(stat = 'identity', position = 'dodge', data = filter(., var != "qsec"), mapping = aes(x = car, y = value, fill = var)) +
geom_line(mapping = aes(x = 1:length(car), y = value), data = filter(., var == "qsec"))
)
}
Edit:
btw, you can check the correct order of qsec to the respective x-value by calling plotly::ggplotly(), then you can read the values better and compare them to the df, because they will show up if you point on the element...