I want to use geom_text_repel to have my labels lie as close to the edge of a pie graph as possible unless the percent is under a certain value, in which case the label should be nudged further away and connected with a line. I adapted a solution from Move labels in ggplot2 pie graph but increased the xpos values for groups above the threshold.
library(dplyr)
library(ggplot2)
library(ggrepel)
library(scales)
threshold = 0.05
age <- data.frame(Age = c("20 - 29", "30 - 39", "40 - 49", "50 - 59", "60 - 69"), count = c(27, 29, 26, 16, 2))
age <- age %>% mutate(percent = count/sum(count),
cs = rev(cumsum(rev(percent))),
ypos = percent/2 + lead(cs, 1),
ypos = ifelse(is.na(ypos), percent/2, ypos),
xpos = ifelse(percent > threshold, 1.8, 1.3),
xn = ifelse(percent > threshold, 0, 0.5))
ggplot(age, aes_string(x = 1, y = "percent", fill = "Age")) +
geom_bar(width = 1 , stat = "identity", colour = "black") +
geom_text_repel(aes(label = percent(percent, accuracy = 0.1), x = xpos, y = ypos), size = 7.5, nudge_x = age$xn, segment.size = .5, direction = "x", force = 0.5, hjust = 1) +
coord_polar("y" , start = 0, clip = "off") +
theme_minimal() +
theme(axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
legend.title = element_text(size = 22.5),
legend.text = element_text(size = 19.5),
legend.box.margin=margin(c(0,0,0,30))) +
labs(fill = "Age") +
scale_fill_manual(values = c("#2B83BA", "#FDAE61", "#FFFF99", "#ABDDA4", "#D7191C"))
The below-threshold values act as expected, but the above-threshold values seem to vary in how far they're located from the edge. I believe two things are at play:
How can I account for these two issues? Or if there any any other issues I'd appreciate help in identifying them. I would consider the 29.0% label to be good enough, if others could follow that format.
Text Text geoms are useful for labeling plots. They can be used by themselves as scatterplots or in combination with other geoms, for example, for labeling points or for annotating the height of bars. geom_text () adds only text to the plot. geom_label () draws a rectangle behind the text, making it easier to read.
They can be used by themselves as scatterplots or in combination with other geoms, for example, for labeling points or for annotating the height of bars. geom_text () adds only text to the plot. geom_label () draws a rectangle behind the text, making it easier to read. Set of aesthetic mappings created by aes () or aes_ ().
position_nudge is generally useful for adjusting the position of items on discrete scales by a small amount. Nudging is built in to geom_text() because it's so useful for moving labels a small distance from what they're labelling. position_nudge(x = 0, y = 0) Amount of vertical and horizontal distance to move.
Therefore data should be arranged by the label column before calling geom_text (). Note that this argument is not supported by geom_label (). Note that when you resize a plot, text labels stay the same size, even though the size of the plot area changes. This happens because the "width" and "height" of a text element are 0.
I would offer the following tricks:
To overcome the first issue, use both ofgeom_text_repel()
and geom_text()
for all data, but show the label
in geom_text_repel()
only for values less than threshold
, and show label
in geom_text()
only for values more than threshold
.
To overcome the second issue, use hjust = 'outward'
in geom_text()
, and adjust the value of nudge_x
both in geom_text()
and geom_text_repel()
.
Use geom_segment()
to create lines connecting pie chart areas with the labels.
Here is the full code:
library(dplyr)
library(ggplot2)
library(ggrepel)
library(scales)
threshold = 0.05
age <- data.frame(Age = c("20 - 29", "30 - 39", "40 - 49", "50 - 59", "60 - 69"), count = c(27, 29, 26, 16, 2))
age <- age %>% mutate(percent = count/sum(count),
cs = rev(cumsum(rev(percent))),
ypos = percent/2 + lead(cs, 1),
ypos = ifelse(is.na(ypos), percent/2, ypos),
xpos = ifelse(percent > threshold, 1.4, 1.8))
ggplot(age, aes_string(x = 1, y = "percent", fill = "Age")) +
geom_bar(width = 1 , stat = "identity", colour = "black") +
theme_minimal() +
theme(axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
legend.title = element_text(size = 22.5),
legend.text = element_text(size = 19.5),
legend.box.margin=margin(c(0,0,0,30))) +
labs(fill = "Age") +
scale_fill_manual(values = c("#2B83BA", "#FDAE61", "#FFFF99", "#ABDDA4", "#D7191C")) +
geom_segment(aes(x = ifelse(percent<threshold,1, xpos), xend = xpos, y = ypos, yend = ypos)) +
geom_text(aes(x = xpos, y = ypos, label = ifelse(percent>threshold,percent(percent, accuracy = 0.1),"")), hjust = "outward", nudge_x = 0.2, size = 7.5) +
geom_text_repel(aes(x = xpos, y = ypos, label = ifelse(percent<threshold, percent(percent, accuracy = 0.1), "")), nudge_x = 0.2, size = 7.5)+
coord_polar("y")
I have tried this code for more than one values less than threshold
by adjusting nudge_x
, and it works.
For example:
library(dplyr)
library(ggplot2)
library(ggrepel)
library(scales)
threshold = 0.05
age <- data.frame(Age = c("20 - 29", "30 - 39", "40 - 49", "50 - 59", "60 - 69"), count = c(50, 44, 1, 2, 3))
age <- age %>% mutate(percent = count/sum(count),
cs = rev(cumsum(rev(percent))),
ypos = percent/2 + lead(cs, 1),
ypos = ifelse(is.na(ypos), percent/2, ypos),
xpos = ifelse(percent > threshold, 1.4, 1.8))
ggplot(age, aes_string(x = 1, y = "percent", fill = "Age")) +
geom_bar(width = 1 , stat = "identity", colour = "black") +
theme_minimal() +
theme(axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
legend.title = element_text(size = 22.5),
legend.text = element_text(size = 19.5),
legend.box.margin=margin(c(0,0,0,30))) +
labs(fill = "Age") +
scale_fill_manual(values = c("#2B83BA", "#FDAE61", "#FFFF99", "#ABDDA4", "#D7191C")) +
geom_segment(aes(x = ifelse(percent<threshold,1, xpos), xend = xpos, y = ypos, yend = ypos)) +
geom_text(aes(x = xpos, y = ypos, label = ifelse(percent>threshold,percent(percent, accuracy = 0.1),"")), hjust = "outward", nudge_x = 0.2, size = 7.5) + geom_text_repel(aes(x = xpos, y = ypos, label = ifelse(percent<threshold, percent(percent, accuracy = 0.1), "")), nudge_x = 0.5, size = 7.5)+
coord_polar("y")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With