For the data frame below, how can I display labels outside the circle with arrows pointing at them?
asd <- data.frame(a=c("fs","dfg","gf"), b=c(3,5,6))
ggplot(asd, aes(x="", y= b, fill = factor(a))) +
geom_bar(stat = "identity", width =1) +
coord_polar(theta = "y") + theme_void() +
geom_text(aes(label=paste(a, sep = " ", b, "%"), x= 1.3, angle = 0))
Try with this:
library(ggplot2)
library(dplyr)
asd <- data.frame(a = c("fs","dfg","gf"),
b = c(3,10,5)) # changed to show that order doesnt matter
asd %>%
mutate(prop = b/sum(b)) %>%
arrange(desc(a)) %>%
mutate(lab.ypos = cumsum(prop) - 0.5*prop) %>%
ggplot(aes(x = "", y = prop, fill = a)) +
geom_bar(width = 1, stat = "identity", color = "white") +
coord_polar("y", start = 0, clip = "off")+
geom_segment(aes(x = 1, y = lab.ypos, xend = 2, yend = lab.ypos, colour = a),
size = 1) +
geom_label(aes(y = lab.ypos, label = paste(a, scales::percent(prop))),
x = 2,
size = 5,
color = "white") +
theme_void() +
theme(legend.position = "none")
For the record: don't use pie chart.
There is a reason why doing a pie chart is complicated: you are not supposed to use them.
Bar charts are always better.
Related
I've been trying for a while now and also doing a lot of research, but I just can't get it to add a simple legend for my two lines.
I have two lines in my chart and I just want to add a legend for the two lines.
scale_color_manual did not work. I suspect it is because I am using scale_y_break. In other plots (without scale_y_break) scale_color_manual works without problems.
Here is my code:
day <- c(1:5)
altimeter <- c(8.291, 8.872, 7.212, 8.1, 5.92)
slope_kilometers <- c(30.23, 34.8, 29.34, 32.98, 21.23)
df2 <- data.frame(day, altimeter, slope_kilometers)
library(ggbreak)
altimeter_color <- "steelblue"
slope_kilometers_color <- "darkred"
ggplot(df2, aes(x = day)) +
#Altimeter data
geom_line(aes(y = altimeter),
linetype = 2,
linewidth = 1,
color = altimeter_color) +
geom_point(y = altimeter, size = 3, color = altimeter_color) +
#Slope kilometers data
geom_line(aes(y = slope_kilometers),
linetype = 2,
linewidth = 1,
color = slope_kilometers_color) +
geom_point(y = slope_kilometers, size = 3, color = slope_kilometers_color) +
#Y-Axis
scale_y_break( c(9, 20), scales = 1.5) +
#Label
labs(x = "Tage",
y = "[km]") +
#Legend
scale_color_manual(values = c(altimeter_color, slope_kilometers_color)) +
#Title
ggtitle("Höhenmeter und Pistenkilometer meines 5-tägigen Skiurlaubs")
I tried different versions of scale_color_manual, labs, aes(fill="")
Update: I tweaked the former plot (removed):
One way to achieve what you want is:
First bring data in long format then put color inside aesthetics:
Rule of thumb: What is in aesthetics will have a legend:
library(tidyverse)
library(ggbreak)
df2 %>%
pivot_longer(-day) %>%
ggplot(aes(x = day)) +
#Altimeter data
geom_line(data = . %>% filter(name == "altimeter"), aes(y = altimeter, color = name),
linetype = 2, linewidth = 1 ) +
geom_point(data = . %>% filter(name == "altimeter"), aes(y = altimeter, color = name), size = 3) +
#Slope kilometers data
geom_line(data = . %>% filter(name == "slope_kilometers"), aes(y = slope_kilometers, color = name),
linetype = 2, linewidth = 1) +
geom_point(data = . %>% filter(name == "slope_kilometers"), aes(y = slope_kilometers, color = name), size = 3) +
#Y-Axis
scale_y_break( c(9, 20), scales = 1.5) +
#Label
labs(x = "Tage", y = "[km]", color = "") +
#Legend
scale_color_manual(values = c(altimeter_color, slope_kilometers_color)) +
#Title
ggtitle("Höhenmeter und Pistenkilometer meines 5-tägigen Skiurlaubs") +
theme(legend.position = "bottom")
I have this code:
as_tibble(earlyCiliated[[]]) %>%
ggplot(aes(x="", y=Phase, fill=Phase)) + geom_col() +
coord_polar("y", start=0) +
geom_text(aes(label = paste0(Phase, "%")))
and my output looks like this:
What am I doing wrong that's causing the labels to all be on top of each other?
I can't completely recreate your plot because I do not have your data. That being said, you can try this:
install.packages("ggrepel")
library(ggrepel)
as_tibble(earlyCiliated[[]]) %>%
ggplot(aes(x="", y=Phase, fill=Phase)) + geom_col() +
coord_polar("y", start=0) +
geom_label_repel(data = earlyCiliated[[]],
aes(y = Phase, label = paste0(Phase, "%")),
size = 4.5, nudge_x = 1, show.legend = FALSE)
This is what it will look like (using other data because none was provided)
library(ggplot2)
library(ggrepel)
library(tidyverse)
df <- data.frame(value = c(15, 25, 32, 28),
group = paste0("G", 1:4))
# Get the positions
df2 <- df %>%
mutate(csum = rev(cumsum(rev(value))),
pos = value/2 + lead(csum, 1),
pos = if_else(is.na(pos), value/2, pos))
ggplot(df, aes(x = "" , y = value, fill = fct_inorder(group))) +
geom_col(width = 1, color = 1) +
coord_polar(theta = "y") +
scale_fill_brewer(palette = "Pastel1") +
geom_label_repel(data = df2,
aes(y = pos, label = paste0(value, "%")),
size = 4.5, nudge_x = 1, show.legend = FALSE) +
guides(fill = guide_legend(title = "Group")) +
theme_void()
If I have the following plot, how would I place the color (error) legend item below the fill (yr) one?
library(dplyr)
library(ggplot2)
dat <- data.frame(yr = c(2017:2019) %>% as.factor,
val = 1:3)
dat %>%
ggplot(aes(x = yr, y = val, fill = yr)) +
geom_bar(stat = "identity") +
geom_errorbar(aes(ymin = 7, ymax = 7,
color = "Error")) +
theme(legend.position = "bottom")
There are two parts, you need to change the order with guides and then add legend.box = "vertical" to theme:
dat %>%
ggplot(aes(x = yr, y = val, fill = yr)) +
geom_bar(stat = "identity") +
geom_errorbar(aes(ymin = 7, ymax = 7,
color = "Error")) +
guides(fill = guide_legend(order = 1),
color = guide_legend(order = 2)) +
theme(legend.box="vertical", legend.position = "bottom")
I am working on a donut chart using ggplot2, but I need the center of the plot to contain text.
Here's sample data (found from this site: https://www.datanovia.com/en/blog/how-to-create-a-pie-chart-in-r-using-ggplot2/):
library(dplyr)
count.data <- data.frame(
class = c("1st", "2nd", "3rd", "Crew"),
n = c(325, 285, 706, 885),
prop = c(14.8, 12.9, 32.1, 40.2)
)
count.data <- count.data %>%
arrange(desc(class)) %>%
mutate(lab.ypos = cumsum(prop) - 0.5*prop)
count.data
I then modified their code to get this donut chart:
library(ggplot2)
library(dplyr)
mycols <- c("#0073C2FF", "#EFC000FF", "#868686FF", "#CD534CFF")
ggplot(count.data, aes(x = 2, y = prop, fill = class)) +
geom_bar(stat = "identity", color = "white") +
coord_polar(theta = "y", start = 0)+
geom_text(aes(y = lab.ypos, label = paste0("n = ", n, ", \n", prop, "%")), color = "white")+
scale_fill_manual(values = mycols) +
theme_void() +
xlim(.5, 2.5)
The plot looks like this:
It is exactly what I want except I need the center of the donut to have the proportion from a variable. In this case, I want the center to say 40.2% (the prop of crew, in this example).
How do I do this?
Edit
Used annotate as suggested by #aosmith and made it a direct call to crew.
like this?
ggplot(count.data, aes(x = 2, y = prop, fill = class)) +
geom_bar(stat = "identity", color = "white") +
coord_polar(theta = "y", start = 0)+
geom_text(aes(y = lab.ypos, label = paste0("n = ", n, ", \n", prop, "%")), color = "white")+
scale_fill_manual(values = mycols) +
theme_void() +
xlim(.5, 2.5) +
annotate(geom = 'text', x = 0.5, y = 0, label = paste0(count.data$prop[count.data$class == 'Crew'], "%"))
I'm plotting a stacked bar graph and use geom_text to insert the value and name of each stack. The problem is some stacks are very small/narrow, so that the text of two stacks overlap each other and hence is not very readable. How can I modify the code to solve this issue.
Type<-c("ddddddddddd","ddddddddddd","bbbbbbbbbbbbb","ddddddddddd","eeeeeeeeeeeeee","bbbbbbbbbbbbb","ddddddddddd","bbbbbbbbbbbbb","ddddddddddd",
"eeeeeeeeeeeeee","mmmmmmmmmmmmmmmmmmm","bbbbbbbbbbbbb","ddddddddddd","bbbbbbbbbbbbb","eeeeeeeeeeeeee")
Category<-c("mmmmm","mmmmm","gggggggggggggggggg","ffffffffffff","ffffffffffff","ffffffffffff","sanddddddddd","sanddddddddd","yyyyyyyyyyy",
"yyyyyyyyyyy","yyyyyyyyyyy","sssssssssssssss","sssssssssssssss","sssssssssssssss","ttttttttttttt")
Frequency<-c(4,1,30,7,127,11,1,1,6,9,1,200,3,4,5)
Data <- data.frame(Type, Category, Frequency)
p <- ggplot(Data, aes(x = Type, y = Frequency)) +
geom_bar(aes(fill = Category), stat="identity", show.legend = FALSE) +
geom_text(aes(label = Frequency), size = 3) +
geom_text(aes(label = Category), size = 3)
Considering your data, a facetted plot might be a better approach:
# summarise your data
library(dplyr)
d1 <- Data %>%
mutate_each(funs(substr(.,1,2)),Type,Category) %>%
group_by(Type,Category) %>%
summarise(Freq = sum(Frequency)) %>%
mutate(lbl = paste(Category,Freq)) # create a label by pasting the 'Category' and the 'Freq' variables together
# plot
ggplot(d1, aes(x = Category, y = Freq, fill = Category)) +
geom_bar(stat="identity", width = 0.7, position = position_dodge(0.8)) +
geom_text(aes(label = lbl), angle = 90, size = 5, hjust = -0.1, position = position_dodge(0.8)) +
scale_y_continuous(limits = c(0,240)) +
guides(fill = FALSE) +
facet_grid(.~Type, scales = "free", space = "free") +
theme_bw(base_size = 14)
which gives:
In the above plot I shortened the labels on purpose. If you don't want to do that, you could consider this:
d2 <- Data %>%
group_by(Type,Category) %>%
summarise(Freq = sum(Frequency)) %>%
mutate(lbl = paste(Category,Freq))
ggplot(d2, aes(x = Category, y = Freq, fill = Category)) +
geom_bar(stat="identity", width = 0.7, position = position_dodge(0.8)) +
geom_text(aes(y = 5, label = lbl), alpha = 0.6, angle = 90, size = 5, hjust = 0, position = position_dodge(0.8)) +
scale_y_continuous(limits = c(0,240)) +
guides(fill = FALSE) +
facet_grid(.~Type, scales = "free", space = "free") +
theme_bw(base_size = 14) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank())
which gives: