I am trying to create a pie chart with ggplot. I want to show how many hours I use for diffrent tasks at work everyday.
# Libraries
library(ggplot2)
library(tidyverse) # function "%>%"
# 1. Read data (semicolon separated)
res = read.csv2(text = "Activity;No_of_Hours
Work;3
Lunch;1
Meetings;2
Talking;1")
# 2. Print table
df <- as.data.frame(res)
df
# 3. Plot Pie chart
res %>% ggplot(aes(x="", # we leave x blank with ""
y= Activity,
fill=No_of_Hours)) +
geom_bar(stat="identity") +
coord_polar("y", start=0) +
theme_classic()
You can calculate the y position of labels with coord_polar and plot them with geom_text
df <- df %>%
arrange(desc(Activity)) %>%
mutate(prop = No_of_Hours / sum(df$No_of_Hours) *100) %>%
mutate(ypos = cumsum(prop)- 0.5*prop )
# 3. Plot Pie chart
ggplot(df, aes(x="", y=prop, fill=Activity)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
geom_text(aes(y = ypos, label = No_of_Hours), color = "white", size=6)
which give you:
Or more similar to your example (but IMHO less informative):
ggplot(df, aes(x="", y=prop, fill=No_of_Hours)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
geom_text(aes(y = ypos, label =Activity ), color = "white", size=6)
which give you:
Related
I have a pie chart below, and I would like to create the wheel much more readable. many thanks in advance.
library(ggplot2)
library(dplyr)
# Create Data
data <- data.frame(
group=c("IMAGINE HERE I HAVE A LONG SURVEY QUESTIONS",
"AND THE TEXT IS IN DIFFERENT LENGTH",
"WANNA PLOT THESE IN ORDER",
"WITH 45 DEGREE ANGLE",
"PIE CAN BE PROPORTIONAL WITH THE TEXT LENGTH" ),
value=c(13,7,9,21,2)
)
# Compute the position of labels
data <- data %>%
arrange(desc(group)) %>%
mutate(prop = value / sum(data$value) *100) %>%
mutate(ypos = cumsum(prop)- 0.5*prop )
# Basic piechart
ggplot(data, aes(x="", y=prop, fill=group)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_text(aes(y = ypos, label = group), color = "white", size=6) +
scale_fill_brewer(palette="Set1")
You have a better chance of fitting in the labels if you wrap them using str_wrap and curve them using geomtextpath:
# Compute the position of labels
data <- data %>%
arrange(desc(group)) %>%
mutate(prop = value / sum(data$value) *100) %>%
mutate(ypos = cumsum(prop)- 0.5*prop,
group = stringr::str_wrap(group, 20))
library(geomtextpath)
# Basic piechart
ggplot(data, aes(x=1, y=prop, fill=group)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_textpath(aes(x = 1.3, y = ypos, label = group,
vjust = group), color = "black", size = 6,
angle = 90) +
scale_fill_brewer(palette="Set1") +
scale_x_continuous(limits = c(0.5, 2)) +
scale_vjust_manual(values = c(2, 1.8, 2.2, 2, 4))
Maybe to wrap the group text and have it plotted in many lines of text is a way to solve the problem.
First strwrap breaks each value in group, then paste inserts newline characters at the break points.
And the graphics device dimensions are made larger, though it doesn't show with reprex.
suppressPackageStartupMessages({
library(ggplot2)
library(dplyr)
})
# Create Data
data <- data.frame(
group=c("IMAGINE HERE I HAVE A LONG SURVEY QUESTIONS",
"AND THE TEXT IS IN DIFFERENT LENGTH",
"WANNA PLOT THESE IN ORDER",
"WITH 45 DEGREE ANGLE",
"PIE CAN BE PROPORTIONAL WITH THE TEXT LENGTH" ),
value=c(13,7,9,21,2)
)
# Compute the position of labels
data <- data %>%
arrange(desc(group)) %>%
mutate(prop = value / sum(data$value) *100) %>%
mutate(ypos = cumsum(prop) - 0.5*prop )
data$group2 <- sapply(data$group, \(x) {
s <- strwrap(x, width = 15)
paste(s, collapse = "\n")
})
old_par <- par()[c("fin", "mar")]
par(fin = old_par$fin + 5, mar = rep(0, 4))
# Basic piechart
ggplot(data, aes(x="", y=prop, fill=group)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_text(aes(y = ypos, label = group2), color = "white", size=3) +
scale_fill_brewer(palette="Set1")
par(old_par)
Created on 2022-05-29 by the reprex package (v2.0.1)
It is not the best option, but you could use geom_label_repel from ggrepel like this:
library(ggplot2)
library(dplyr)
library(ggrepel)
data <- data %>%
arrange(desc(group)) %>%
mutate(prop = value / sum(data$value) *100) %>%
mutate(ypos = cumsum(prop)- 0.5*prop )
ggplot(data, aes(x="", y=prop, fill=group)) +
geom_bar(stat="identity", width=1, color="white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_label_repel(data = data,
aes(y = ypos, label = group),
size = 4.5, nudge_x = 3, show.legend = FALSE) +
scale_fill_brewer(palette="Set1")
Output:
I have a pie chart below, and I would like to leave extra white space between each pie and paste the value of each letter in each pie (A=25). how to get around this? many thanks in advance.
library(ggplot2)
library(dplyr)
# Create Data
data <- data.frame(
group=LETTERS[1:5],
value=c(13,7,9,21,2)
)
# Compute the position of labels
data <- data %>%
arrange(desc(group)) %>%
mutate(prop = value / sum(data$value) *100) %>%
mutate(ypos = cumsum(prop)- 0.5*prop )
# Basic piechart
ggplot(data, aes(x="", y=prop, fill=group)) +
geom_bar(stat="identity", width=10, color="white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_text(aes(y = ypos, label = round(prop,2) ), color = "white", size=4) +
scale_fill_brewer(palette="Set1")
You could do:
ggplot(data, aes(x="", y=prop, fill=group)) +
geom_bar(stat="identity", width=10, size = 3, color = "white") +
coord_polar("y", start=0) +
theme_void() +
theme(legend.position="none") +
geom_text(aes(y = ypos, label = paste(group, round(prop,2), sep = "\n")),
color = "white", size=4, nudge_x = 3) +
scale_fill_brewer(palette="Set1")
Perhaps 3D pie chart in base R can work with explode argument set to true, e.g.
pie3D(num_data, labels = num_data, explode = 0.25)
I have this graph that I want to show the count over the bar, however my code shows the number 1 inside the bars..
What I have:
What I am trying to make:
# Library
library(ggplot2)
# 1. Read data (comma separated)
df = read.csv2(text = "Id;Date
1;2021-06-09
2;2021-06-08
3;2021-06-08
4;2021-06-09
5;2021-06-09")
# 2. Print table
df_date <- df[, "Date"]
df_date <- as.data.frame(table(df_date))
colnames(df_date)[which(names(df_date) == "df_date")] <- "Date" # Set column name to Date
df_date
# 3. Plot bar chart
ggplot(df_date, aes(x = Date, y = Freq)) +
geom_bar(stat = "identity") +
theme_classic() +
ggtitle("Date") +
xlab("Date") +
ylab("Frequency") +
geom_text(stat= "count", aes(label = ..count.., y= ..prop..), vjust = -1)
Since you have already calculated the frequency use geom_col.
library(ggplot2)
ggplot(df_date, aes(x = Date, y = Freq)) +
geom_col() +
theme_classic() +
ggtitle("Date") +
xlab("Date") +
ylab("Frequency") +
geom_text(aes(label = Freq), vjust = -1)
If you use df you can use geom_bar as -
ggplot(df, aes(x = Date)) +
geom_bar() +
theme_classic() +
ggtitle("Date") +
xlab("Date") +
ylab("Frequency") +
geom_text(stat= "count",aes(label = ..count..), vjust = -1)
Sample data
data <- data.frame(Country = c("Mexico","USA","Canada","Chile"), Per = c(15.5,75.3,5.2,4.0))
I tried set position of labels.
ggplot(data =data) +
geom_bar(aes(x = "", y = Per, fill = Country), stat = "identity", width = 1) +
coord_polar("y", start = 0) +
theme_void()+
geom_text(aes(x = 1.2, y = cumsum(Per), label = Per))
But pie chart actually look like:
You have to sort the data before calculating the cumulative sum. Then, you can optimize label position, e.g. by subtracting half of Per:
library(tidyverse)
data %>%
arrange(-Per) %>%
mutate(Per_cumsum=cumsum(Per)) %>%
ggplot(aes(x=1, y=Per, fill=Country)) +
geom_col() +
geom_text(aes(x=1,y = Per_cumsum-Per/2, label=Per)) +
coord_polar("y", start=0) +
theme_void()
PS: geom_col uses stat_identity by default: it leaves the data as is.
Or simply use position_stack
data %>%
ggplot(aes(x=1, y=Per, fill=Country)) +
geom_col() +
geom_text(aes(label = Per), position = position_stack(vjust = 0.5))+
coord_polar(theta = "y") +
theme_void()
From the help:
# To place text in the middle of each bar in a stacked barplot, you
# need to set the vjust parameter of position_stack()
While creating a slopegraph with ggplot2, as below, I find that many of my labels overlap when their data points are close together. How can I change the labelling to automatically stagger my labels if there is overlap?
library(ggplot2)
library(scales)
install.packages("Lock5Data", repos = "http://cran.us.r-project.org") # you might need this
library(Lock5Data)
data("NBAStandings1e")
data("NBAStandings2016")
colnames(NBAStandings1e)[4] <- "year1" # 2010-2011
colnames(NBAStandings2016)[4] <- "year2" # 2015-2016
nba_df <- merge(NBAStandings1e[,c('Team','year1')], NBAStandings2016[,c('Team','year2')])
scale <- dim(nba_df)[1]
a<-nba_df
p<-ggplot(nba_df) + geom_segment(aes(x=0,xend=scale,y=year1,yend=year2),size=.75)
# clear junk
p<-p + theme(panel.background = element_blank())
p<-p + theme(panel.grid=element_blank())
p<-p + theme(axis.ticks=element_blank())
# p<-p + theme(axis.text=element_blank())
p<-p + theme(panel.border=element_blank())
# p<-p + theme(panel.grid.major = element_line(linetype = "dashed", fill = NA))
p<-p + theme(panel.grid.major = element_line(linetype = "dashed",color = "grey80"))
p<-p + theme(panel.grid.major.x = element_blank())
p<-p + theme(axis.text.x = element_blank())
# annotate
p<-p + xlab("") + ylab("Percentage Wins")
p<-p + xlim((-5),(scale+12))
p<-p + geom_text(label="2010-2011 Season", x=0, y=(1.1*(max(a$year2,a$year1))),hjust= 1.2,size=3)
p<-p + geom_text(label="2015-2016 Season", x=months,y=(1.1*(max(a$year2,a$year1))),hjust=-0.1,size=3)
p<-p + geom_text(label=nba_df$Team, y=nba_df$year2, x=rep.int(scale,dim(a)[1]),hjust=-0.2,size=2)
p<-p + geom_text(label=nba_df$Team, y=nba_df$year1, x=rep.int( 0,dim(a)[1]),hjust=1.2,size=2)
p
Since the teams that overlap have the same winning percentage, you can deal with overlap more simply by combining the labels for teams with the same winning percentage. I've also made a few other changes to your code intended to streamline the process.
library(Lock5Data)
library(tidyverse)
library(scales)
data("NBAStandings1e")
data("NBAStandings2016")
colnames(NBAStandings1e)[4] <- "2010-11" # 2010-2011
colnames(NBAStandings2016)[4] <- "2015-16" # 2015-2016
nba_df <- merge(NBAStandings1e[,c('Team','2010-11')], NBAStandings2016[,c('Team','2015-16')])
# Convert data to long format
dat = gather(nba_df, Season, value, -Team)
# Combine labels for teams with same winning percentage (see footnote * below)
dat_lab = dat %>% group_by(Season, value) %>%
summarise(Team = paste(Team, collapse="\U2014")) # \U2014 is the emdash character
ggplot(dat, aes(Season, value, group=Team)) +
geom_line() +
theme_minimal() + theme(panel.grid.minor=element_blank()) +
labs(y="Winning Percentage") +
scale_y_continuous(limits=c(0,1), labels=percent) +
geom_text(data=subset(dat_lab, Season=="2010-11"), aes(label=Team, x=0.98), hjust=1, size=2) +
geom_text(data=subset(dat_lab, Season=="2015-16"), aes(label=Team, x=2.02), hjust=0, size=2)
Here's a closeup of what the labels look like:
* If there are teams that overlap due to having very close, but unequal, winning percentages, you can still group them by rounding. For example, if you wanted to group teams with winning percentages that are the same when rounded to the nearest 2 percent, you could do:
dat_lab = dat %>% group_by(Season, group=round(value/0.02)*0.02) %>%
summarise(Team = paste(Team, collapse="\U2014"),
value = mean(value))
This would result in the labels being placed at the mean value for their group.