Ggplot bar chart: how to show precentage on the y-axis

Viewed 92

I'm very new to R and trying to create some meaningful bar charts with my data. Here is some example code for a simple bar chart using ggplot2:

library(ggplot2)

#a variable with 4 different levels
category <- as.factor(c(1, 2, 3, 3, 2, 2, 1, 2, 4, 4, 1, 3, 2, 2, 2, 1))
#a variable with either 0 ("false") or 1 ("true")
quality <- as.factor(c(0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1))
mydata <- data.frame(category, quality)

plot1 <- ggplot(mydata, aes(x= category, fill=quality)) +
  geom_bar(width=0.25) +
  ggtitle("example") +
  xlab("category") +
  ylab("count") +
  labs("true")
plot1

So far I only know how to create a bar chart with counts (n) on the y-axis, with the bar filled based on the number of instances that are true. However, I need the precentage of instances that are true in each category. For example: there are 4 instances belonging to category 1, 3 of which are labeled as true. I need the y-axis to show the precentage which in the case of category 1 would be 75,0%. For category 2 that would be 2/7 * 100 = 28,6%.

Hope this explanation makes sense and someone can suggest a solution! Thank you in advance.

Edit

I have refined my code a bit but now I'm facing a new problem. It feels like the answer should be obvious but I can't figure this out:

category <- as.factor(c(1, 2, 3, 3, 2, 2, 1, 2, 4, 4, 1, 3, 2, 2, 2, 1))
quality <- as.factor(c(0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1))
mydata <- data.frame(category, quality)

mydata<- mydata %>% group_by(category,quality) %>% mutate(count_q = n()) %>% ungroup() %>%
  group_by(category) %>% mutate(tot_q=n(),pc=count_q*100/tot_q)  %>% unique() %>% arrange(category)

plot1 <- ggplot(mydata, aes(x= category, y = pc)) +
  geom_bar(position = 'dodge', stat='identity', fill="red") +
  geom_text(aes(label=round(tot_q)), position=position_dodge(0.9), vjust=-0.5) +
  ggtitle("example") +
  xlab("category") +
  ylab("count")
plot1

For some reason this gives me the tot_q values twice on top of the bar chart. So how to remove the extra values?

enter image description here

2 Answers

Here is one approach using dplyr

setup library and generate data

library(ggplot2)
library(dplyr)
#a variable with 4 different levels
category <- as.factor(c(1, 2, 3, 3, 2, 2, 1, 2, 4, 4, 1, 3, 2, 2, 2, 1))
#a variable with either 0 ("false") or 1 ("true")
quality <- as.factor(c(0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1))
mydata <- data.frame(category, quality)
#

Calculate pc percent variable using dplyr

mydata<- mydata %>% group_by(category,quality) %>% mutate(count_q = n()) %>% ungroup() %>%
  group_by(category) %>% mutate(tot_q=n(),pc=count_q*100/tot_q)  %>% unique() %>% arrange(category)

Generate plot, one correction change y = quality to y = count_q

plot1 <- ggplot(mydata, aes(x= category, y = count_q, fill=quality)) +
  geom_bar(position = 'dodge', stat='identity') +
  geom_text(aes(label=round(pc,digits=1)), position=position_dodge(width=0.9), vjust=-0.25) +
  ggtitle("example") +
  xlab("category") +
  ylab("count") +
  labs("true")
plot1

Output plot

@sachin2014 showed a great example of pre-processing the data. But we can also use the special variables in ggplot2 to do the same thing.

We can use tapply to get the sum of the ..count..s of each ..x... ..x.. is each category since we defined it in the main aes().

library(ggplot2)
ggplot(mydata, aes(x= category, fill=quality)) +
  geom_bar(aes(y = ..count..),width=0.75, position = "dodge") +
  geom_text(stat="count", aes(label = round((..count../tapply(..count.., ..x.. ,sum)[..x..])*100,1)),
            position=position_dodge(width=0.9), vjust=-0.25) +
  ggtitle("example") +   xlab("category") +  ylab("count") + labs("true")

enter image description here

Related