Plot a wheel (pie) chart in R - r

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:

Related

fancy pie chart in R using ggplot2

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)

Pie chart with ggplot2 using data from read.csv2

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:

using subscript in legend

I created a pie chart like so:
library(dplyr)
library(ggplot2)
#Data
data <- data.frame(my_val = c(10, 12, 4),
my_var = c("A1B", "H2C3+", "LO4"),
stringsAsFactors = F)
#Create variable
data <- data %>%
mutate(per=my_val/sum(my_val)) %>%
arrange(desc(my_var))
data$label <- scales::percent(data$per)
#Plot
ggplot(data=data)+
geom_bar(aes(x="", y=per, fill=my_var), stat="identity", width = 1)+
coord_polar("y", start=0)+
theme_void()+
geom_text(aes(x=1, y = cumsum(per) - per/2, label=label))
I would like to add the legend as a subscript like so:
my_lab <- c(expression(H['2'*'C'*phantom("+")]),
expression(A['1']*B),
expression(LO['4']))
How can I add it in my pie chart code above?
Put my_lab above (before) your plot. Index it for your labels.
my_lab <- c(expression(H['2'*'C'*phantom("+")]),
expression(A['1']*B),
expression(LO['4']))
ggplot(data=data) +
geom_bar(aes(x="", y=per, fill=my_var),
stat="identity", width = 1) +
coord_polar("y", start=0) +
theme_void() +
geom_text(aes(x=1, y = cumsum(per) - per/2,
label=label)) +
scale_fill_discrete(name="Variables",
labels=c(my_lab[1],
my_lab[2],
my_lab[3]))
Making use of a named vector you could do:
Note: For a nice (left) alignment of the legend labels I made use of guide_legend(label.hjust = 0).
library(ggplot2)
ggplot(data=data)+
geom_bar(aes(x="", y=per, fill=my_var), stat="identity", width = 1)+
coord_polar("y", start=0)+
theme_void()+
geom_text(aes(x=1, y = cumsum(per) - per/2, label=label)) +
scale_fill_discrete(labels = c(`H2C3+` = expression(H['2'*'C'*phantom("+")]),
A1B = expression(A['1']*B),
LO4 = expression(LO['4']))) +
guides(fill = guide_legend(label.hjust = 0))

How To Create Pyramid Bar Chart in R with y-axis labels between the bars

Below is some R code that generates a bar plot using ggplot, where the bars go off to the left and right, centered at x = 0. I would like to take the text on the y axis (the stage names), and place them in-between the left and the right bars. Here is the R code creating the graph:
library(dplyr)
libary(ggplot2)
# Read data
email_campaign_funnel <- read.csv("https://raw.githubusercontent.com/selva86/datasets/master/email_campaign_funnel.csv")
# X Axis Breaks and Labels
brks <- seq(-15000000, 15000000, 5000000)
lbls = paste0(as.character(c(seq(15, 0, -5), seq(5, 15, 5))), "m")
# Shorten Names
email_campaign_funnel <- email_campaign_funnel %>%
dplyr::mutate(Stage = gsub('Stage ', '', Stage)) %>%
dplyr::mutate(Stage = gsub(' Page', '', Stage)) %>%
dplyr::mutate(Stage = gsub('Campaign-', '', Stage))
# Plot
ggplot(email_campaign_funnel, aes(x = Stage, y = Users, fill = Gender)) + # Fill column
geom_bar(stat = "identity", width = .6) + # draw the bars
scale_y_continuous(breaks = brks, # Breaks
labels = lbls) + # Labels
coord_flip() + # Flip axes
labs(title="Email Campaign Funnel") +
theme(plot.title = element_text(hjust = .5),
axis.ticks = element_blank()) + # Centre plot title
scale_fill_brewer(palette = "Dark2") # Color palette
Below is a screenshot of a different graph that highlights sort of how I'd like the text to be split in between the bars (I prefer the vertical style of the ggplot() graph more so than the horizontal nature of the imaged graph below).
Any ideas on how to do this in R would be greatly appreciated, thanks!
How about something like this using ggarrange from the ggpubr package:
gg1 <- email_campaign_funnel %>%
mutate(Users = if_else(Gender == "Male", Users, 0)) %>%
ggplot(aes(Stage, Users, fill = Gender)) +
geom_col(width = 0.6) +
scale_y_continuous(breaks = brks, labels = lbls) +
coord_flip() +
labs(title="Email Campaign Funnel") +
theme_minimal() +
scale_fill_manual(values = c("Male" = "Red", "Female" = "Blue")) +
theme(
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
gg2 <- email_campaign_funnel %>%
filter(Gender == "Male") %>%
ggplot(aes(Stage, 0, label = Stage)) +
geom_text() +
coord_flip() +
theme_void()
gg3 <- email_campaign_funnel %>%
mutate(Users = if_else(Gender == "Female", Users, 0)) %>%
ggplot(aes(Stage, Users, fill = Gender)) +
geom_col(width = 0.6) +
scale_y_continuous(breaks = brks, labels = lbls) +
coord_flip() +
labs(title="Email Campaign Funnel") +
theme_minimal() +
scale_fill_manual(values = c("Male" = "Red", "Female" = "Blue")) +
theme(
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
library(ggpubr)
ggarrange(gg1, gg2, gg3, ncol = 3, common.legend = TRUE, align = "h")
Explanation: The idea is to build the plot separately from the left and right pyramid bar charts and the labels in the middle. We then use ggpubr::ggarrange to arrange all three ggplot2 plot objects in a single row and ensure that axes are properly aligned.
Split horizontal bar chart with labels in the middle
I was interested in seeing how close we can get to the horizontal pyramid bar chart plot you link to. Here is my attempt:
# Sample data
df <- read.table(text =
"Category Group Value
REB Red 39
REB Blue 35
OREB Red 8
OREB Blue 4
DREB Red 31
DREB Blue 31
AST Red 25
AST Blue 21
STL Red 5
STL Blue 5
BLK Red 1
BLK Blue 0
TOV Red 9
TOV Blue 11", header = T)
# Set factor order
df <- df %>% mutate(Category = factor(Category, unique(Category)))
# Build ggplot2 plot objects
library(tidyverse)
gg1 <- df %>%
filter(Group == "Red") %>%
ggplot(aes(Category, Value, fill = Group, label = Value)) +
geom_col() +
geom_text(colour = "red3", fontface = "bold", nudge_y = 10) +
theme_void() +
scale_fill_manual(values = c("Red" = "red3", "Blue" = "navyblue"), drop = FALSE) +
ylim(c(0, round(1.5 * max(df$Value))))
gg2 <- df %>%
filter(Group == "Red") %>%
ggplot(aes(Category, 0, label = Category)) +
geom_text(fontface = "bold") +
theme_void()
gg3 <- df %>%
filter(Group == "Blue") %>%
ggplot(aes(Category, -Value, fill = Group, label = Value)) +
geom_col() +
geom_text(colour = "navyblue", fontface = "bold", nudge_y = -10) +
theme_void() +
scale_fill_manual(values = c("Red" = "red3", "Blue" = "navyblue"), drop = FALSE) +
ylim(c(round(-1.5 * max(df$Value)), 0))
# Arrange plot objects in 1 column with horizontal scales aligned
library(ggpubr)
ggarrange(gg1, gg2, gg3, nrow = 3, common.legend = TRUE, align = "h", heights = c(1, 0.5, 1))

ggplot2 pie chart bad position of labels

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()

Resources