ggplot2 - Turn off legend for one geom with same aesthetic as another geom - r

I'm making a plot with two different geoms, both use fill. I'd like one geom to have a legend, but the other to not. However adding show.legend=F to the required geom doesn't switch off the legend for that geom.
Example:
library(tidyverse)
library(ggalluvial)
x = tibble(qms = c("grass", "cereal", "cereal"),
move1 = "Birth",
move2 = c("Direct", "Market", "Slaughter"),
move3 = c("Slaughter", "Slaughter", NA),
freq = c(10, 5, 7))
x %>%
mutate(id = qms) %>%
to_lodes_form(axis = 2:4, id = id) %>%
na.omit() %>%
ggplot(aes(x = x, stratum = stratum, alluvium = id,
y = freq, label = stratum)) +
scale_x_discrete(expand = c(.1, .1)) +
geom_flow(aes(fill = qms)) +
geom_stratum(aes(fill = stratum), show.legend=F) +
geom_text(stat = "stratum", size = 3) +
theme_void() +
labs(fill="")
Output:
Desired output:
Question:
How do I turn off the fill legend for one geom, but not the other? I can (if I have to) do this in inkscape/gimp, but would prefer a solution I can version control.

Have a look at the final line of code:
scale_fill_discrete(breaks = c("grass", "cereal"))
That defines the breaks for the fills to only include cereal and grass, as required.
library(tidyverse)
library(ggalluvial)
x = tibble(qms = c("grass", "cereal", "cereal"),
move1 = "Birth",
move2 = c("Direct", "Market", "Slaughter"),
move3 = c("Slaughter", "Slaughter", NA),
freq = c(10, 5, 7))
x %>%
mutate(id = qms) %>%
to_lodes_form(axis = 2:4, id = id) %>%
na.omit() %>%
ggplot(aes(x = x, stratum = stratum, alluvium = id,
y = freq, label = stratum)) +
scale_x_discrete(expand = c(.1, .1)) +
geom_flow(aes(fill = qms)) +
geom_stratum(aes(fill = stratum), show.legend=FALSE) +
geom_text(stat = "stratum", size = 3) +
theme_void() +
labs(fill="") +
scale_fill_discrete(breaks = c("grass", "cereal")) #<- This line!
Created on 2019-03-18 by the reprex package (v0.2.1)

Related

Simple one about Alluvial plot in R

I would like to make a simple flow graph.
Here is my code:
## Data
x = tibble(qms = c("FLOW", "FLOW"),
move1 = c("Birth", "Birth"),
move2 = c("Direct", NA),
freq = c(100, 50))
## Graph
x %>%
mutate(id = qms) %>%
to_lodes_form(axis = 2:3, id = id) %>%
na.omit() %>%
ggplot(aes(x = x, stratum = stratum, alluvium = id,
y = freq, label = stratum)) +
scale_x_discrete(expand = c(.1, .1)) +
geom_flow(aes(fill = qms),stat = "alluvium") +
geom_stratum(aes(fill = stratum), show.legend=FALSE) +
geom_text(stat = "stratum", size = 3)
This is the outcome:
My desired outcome is that:
How can I express the decreasing pattern with the missing value?
By slightly reshaping your data you can get what you want. I think the key is to map the alluvium to something fixed like 1 so that it will be a single flow, and mapping stratum to the same variable as x.
library(tidyverse)
library(ggalluvial)
x <- tibble(x = c("Birth", "Direct"),
y = c(100, 50))
x %>%
ggplot(aes(x, y, alluvium = 1, stratum = x)) +
geom_alluvium() +
geom_stratum()
Created on 2022-11-15 with reprex v2.0.2

3 layer donut chart in R

I am trying to recreate this image in R, however I am unable to work out how to have 3 layers to a donut chart - everything I find (for instance, webr::PieDonut) only allows 2. Using ggplot I am also unable to re-create it.
A MRE is:
library(ggplot2)
library(webr)
library(dplyr)
lexicon <- data.frame("Level1" = c(rep("Flavour", 11), rep("Appearance", 4)),
"Level2" = c(rep("Misc", 6), rep("Pungent", 5), rep("Colour", 4)),
"Level3" = c("Fresh", "Refreshing", "Soapy", "Minty", "Nutty", "Milky", "Peppery", "Sharp", "Horseradish", "Mustard hot", "Spicy", "Colourful"," Fresh Green", "Dark Green", "Bright Green")
)
PieDonut(lexicon, aes(Level1, Level2), title = "Salad Lexicon", showRatioDonut =FALSE, showRatioPie = FALSE)
ggplot(lexicon, aes(Level2, Level3, fill = Level1)) +
geom_col() +
scale_fill_viridis_d() +
coord_polar("y")
While the PieDonut works for 2 levels (not shown), it doesn't allow the final level to be included. The ggplot approach also does not work, as seen in the figure below.
How can I get this style of chart in R? Either with ggplot or base plotting.
I think a nice alternative is to use geom_rect here after some data manipulation. Using the fill, color, and alpha scales can help improve the differentiation of categories. I would also use geom_textpath here, though I might go for circumferential labels if there is room to do so:
lexicon %>%
mutate(top_level = Level1) %>%
pivot_longer(1:3) %>%
group_by(name, value) %>%
mutate(width = n()) %>%
unique() %>%
arrange(name) %>%
group_by(name) %>%
mutate(ymid = as.numeric(sub("\\D+", "", name)),
ymax = ymid + 0.5, ymin = ymid - 0.5,
xmin = c(0, head(cumsum(width), -1)),
xmax = cumsum(width),
xmid = (xmax + xmin) / 2) %>%
ggplot(aes(xmid, ymid, fill = top_level)) +
geom_rect(aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax,
alpha = name, color = top_level)) +
geomtextpath::geom_textpath(aes(y = ymid + 0.25, label = value,
group = value)) +
scale_alpha_manual(values = c(1, 0.3, 0.1)) +
scale_fill_manual(values = c("#cd9900", "#00817e")) +
scale_colour_manual(values = c("#cd9900", "#00817e")) +
scale_y_continuous(limits = c(-0.5, 3.6)) +
coord_polar() +
theme_void() +
theme(legend.position = "none")
One option would be to reeshape your data to long and do some manual aggregating before passing to ggplot. Additionally I use geomtextpath::geom_textpath to add the labels:
library(ggplot2)
library(dplyr)
library(geomtextpath)
lexicon <- data.frame("Level1" = c(rep("Flavour", 11), rep("Appearance", 4)),
"Level2" = c(rep("Misc", 6), rep("Pungent", 5), rep("Colour", 4)),
"Level3" = c("Fresh", "Refreshing", "Soapy", "Minty", "Nutty", "Milky", "Peppery", "Sharp", "Horseradish", "Mustard hot", "Spicy", "Colourful"," Fresh Green", "Dark Green", "Bright Green")
)
lexicon_long <- lexicon |>
mutate(fill = Level1) |>
tidyr::pivot_longer(-fill, names_to = "level", values_to = "label") |>
mutate(label = forcats::fct_inorder(label)) |>
count(fill, level, label) |>
group_by(level) |>
mutate(pct = n / sum(n))
ggplot(lexicon_long, aes(level, pct, fill = fill)) +
geom_col(color = "white") +
geom_textpath(aes(label = label, group = label),
position = position_stack(vjust = .5),
upright = TRUE, hjust = .5, size = 3
) +
scale_fill_viridis_d() +
coord_polar("y") +
theme_void() +
guides(fill = "none")

`ggalluvial`: `geom_flow` fill color from destination stratum

I have the following example:
library(tidyverse)
library(ggalluvial)
data <- tibble(id = paste0("S", 1:20),
class_1 = c(rep("A", 10), rep("B", 10)),
class_2 = c(rep("A", 8), rep("B", 8), rep("A", 4)))
data_pvt <- data %>%
pivot_longer(starts_with("class"), names_to = "class_type", values_to = "class_label") %>%
mutate(class_type = factor(class_type),
class_label = factor(class_label))
ggplot(data_pvt, aes(x = fct_rev(class_type), stratum = class_label, alluvium = id,
label = class_label)) +
geom_flow(aes(fill = class_label), stat = "alluvium",
lode.guidance = "frontback") +
geom_stratum(aes(fill = class_label)) +
scale_x_discrete(expand = c(0.1, 0)) +
labs(x = "Class system", y = "n") +
coord_flip() +
theme_minimal()
Created on 2022-02-18 by the reprex package (v2.0.1)
I would like geom_flow to take the fill color from the top stratum (class_1) instead of the bottom stratum (class_2). I could achieve this by not fct_rev(class_type) at the beginning, but then class_1 is at the bottom, while I want it at the top.
Any ideas? I could use other functions from ggalluvium or ggforce, but I'd like to keep the option of having the stratum colored by class_label.
Are you looking for aes.flow = "backward"?
ggplot(data_pvt, aes(x = fct_rev(class_type), stratum = class_label, alluvium = id,
label = class_label)) +
geom_flow(aes(fill = class_label), stat = "alluvium",
lode.guidance = "frontback", aes.flow = "backward") +
geom_stratum(aes(fill = class_label)) +
scale_x_discrete(expand = c(0.1, 0)) +
labs(x = "Class system", y = "n") +
coord_flip() +
theme_minimal()

How to adjust ggrepel label on pie chart?

I am trying to create a pie chart to visualize percent abundance of 9 genera. However, the labels are all clumping together. How do I remedy this? Code included below:
generaabundance2020 <- c(883, 464, 1948, 1177, 2607, 962, 2073, 620, 2670)
genera2020 <- c("Andrena", "Ceratina", "Halictus",
"Hesperapis", "Lasioglossum", "Melissodes",
"Osmia", "Panurginus", "Other")
generabreakdown2020 <- data.frame(group = genera2020, value = generaabundance2020)
gb2020label <- generabreakdown2020 %>%
group_by(value) %>% # Variable to be transformed
count() %>%
ungroup() %>%
mutate(perc = `value` / sum(`value`)) %>%
arrange(perc) %>%
mutate(labels = scales::percent(perc))
generabreakdown2020 %>%
ggplot(aes(x = "", y = value, fill = group)) +
geom_col() +
coord_polar("y", start = 0) +
theme_void() +
geom_label_repel(aes(label = gb2020label$labels), position = position_fill(vjust = 0.5),
size = 5, show.legend = F, max.overlaps = 50) +
guides(fill = guide_legend(title = "Genera")) +
scale_fill_manual(values = c("brown1", "chocolate1",
"darkgoldenrod1", "darkgreen",
"deepskyblue", "darkslateblue",
"darkorchid4", "hotpink1",
"lightpink"))
Which produces the following:
Thanks for adding your data.
There are a few errors in your code. The main one is that you didn't precalculate where to place the labels (done here in the text_y variable). That variable needs to be passed as the y aesthetic for geom_label_repel.
The second is that you no longer need
group_by(value) %>% count() %>% ungroup() because the data you provided is already aggregated.
library(tidyverse)
library(ggrepel)
generaabundance2020 <- c(883, 464, 1948, 1177, 2607, 962, 2073, 620, 2670)
genera2020 <- c("Andrena", "Ceratina", "Halictus", "Hesperapis", "Lasioglossum", "Melissodes", "Osmia", "Panurginus", "Other")
generabreakdown2020 <- data.frame(group = genera2020, value = generaabundance2020)
gb2020label <-
generabreakdown2020 %>%
mutate(perc = value/ sum(value)) %>%
mutate(labels = scales::percent(perc)) %>%
arrange(desc(group)) %>% ## arrange in the order of the legend
mutate(text_y = cumsum(value) - value/2) ### calculate where to place the text labels
gb2020label %>%
ggplot(aes(x = "", y = value, fill = group)) +
geom_col() +
coord_polar(theta = "y") +
geom_label_repel(aes(label = labels, y = text_y),
nudge_x = 0.6, nudge_y = 0.6,
size = 5, show.legend = F) +
guides(fill = guide_legend(title = "Genera")) +
scale_fill_manual(values = c("brown1", "chocolate1",
"darkgoldenrod1", "darkgreen",
"deepskyblue", "darkslateblue",
"darkorchid4", "hotpink1",
"lightpink"))
If you want to arrange in descending order of frequency, you should remember to also set the factor levels of the group variable to the same order.
gb2020label <-
generabreakdown2020 %>%
mutate(perc = value/ sum(value)) %>%
mutate(labels = scales::percent(perc)) %>%
arrange(desc(perc)) %>% ## arrange in descending order of frequency
mutate(group = fct_rev(fct_inorder(group))) %>% ## also arrange the groups in descending order of freq
mutate(text_y = cumsum(value) - value/2) ### calculate where to place the text labels
gb2020label %>%
ggplot(aes(x = "", y = value, fill = group)) +
geom_col() +
coord_polar(theta = "y") +
geom_label_repel(aes(label = labels, y = text_y),
nudge_x = 0.6, nudge_y = 0.6,
size = 5, show.legend = F) +
guides(fill = guide_legend(title = "Genera")) +
scale_fill_manual(values = c("brown1", "chocolate1",
"darkgoldenrod1", "darkgreen",
"deepskyblue", "darkslateblue",
"darkorchid4", "hotpink1",
"lightpink"))
Created on 2021-10-27 by the reprex package (v2.0.1)
You didn't provide us with your data to work with so I'm using ggplot2::mpg here.
library(tidyverse)
library(ggrepel)
mpg_2 <-
mpg %>%
slice_sample(n = 20) %>%
count(manufacturer) %>%
mutate(perc = n / sum(n)) %>%
mutate(labels = scales::percent(perc)) %>%
arrange(desc(manufacturer)) %>%
mutate(text_y = cumsum(n) - n/2)
Chart without polar coordinates
mpg_2 %>%
ggplot(aes(x = "", y = n, fill = manufacturer)) +
geom_col() +
geom_label(aes(label = labels, y = text_y))
Chart with polar coordinates and geom_label_repel
mpg_2 %>%
ggplot(aes(x = "", y = n, fill = manufacturer)) +
geom_col() +
geom_label_repel(aes(label = labels, y = text_y),
force = 0.5,nudge_x = 0.6, nudge_y = 0.6) +
coord_polar(theta = "y")
But maybe your data isn’t dense enough to need repelling?
mpg_2 %>%
ggplot(aes(x = "", y = n, fill = manufacturer)) +
geom_col() +
geom_label(aes(label = labels, y = text_y), nudge_x = 0.6) +
coord_polar(theta = "y")
Created on 2021-10-26 by the reprex package (v2.0.1)

R label with commas but no decimals

My goal is to produce labels with commas, but no decimals. Let's say I have a ggplot with the following section:
geom_text(aes(y = var,
label = scales::comma(round(var))), hjust = 0, nudge_y = 300 )
This is almost what I need. It gives me the commas, but has a decimal. I have seen here (axis labels with comma but no decimals ggplot) that comma_format() could be good, but I think the label in my case needs a data argument, which comma_format() does not take. What can I do?
Update:
As an example of when this problem occurs, see the following, which uses gganimate and has a lot more going on. Code derived from Jon Spring's answer at Animated sorted bar chart with bars overtaking each other
library(gapminder)
library(gganimate)
library(tidyverse)
gap_smoother <- gapminder %>%
filter(continent == "Asia") %>%
group_by(country) %>%
complete(year = full_seq(year, 1)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
group_by(year) %>%
mutate(rank = min_rank(-gdpPercap) * 1) %>%
ungroup() %>%
group_by(country) %>%
complete(year = full_seq(year, .5)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
mutate(rank = approx(x = year, y = rank, xout = year)$y) %>%
ungroup() %>%
arrange(country,year)
gap_smoother2 <- gap_smoother %>% filter(year<=2007 & year>=1999)
gap_smoother3 <- gap_smoother2 %<>% filter(rank<=8)
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap))), hjust = 0, nudge_y = 300 ) +
coord_flip(clip = "off", expand = FALSE) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state %>% as.numeric %>% floor}',
x = "", y = "GFP per capita") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(year, transition_length = 1, state_length = 0) +
enter_grow() +
exit_shrink() +
ease_aes('linear')
animate(p, fps = 2, duration = 5, width = 600, height = 500)
In addition to the solution provided by #drf, you need to add scale_y_continuous(scales::comma) to your ggplot commands. But put it before the coord_flip function.
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap), accuracy=1)),
hjust = 0, nudge_y = 300 ) +
scale_y_continuous(labels = scales::comma) +
... etc.

Resources