Reputation: 23938
The following code works fine and produces the required graph as given below:
library(tidyverse)
library(ggiraphExtra)
library(moonBook)
ggPieDonut(data = acs, mapping = aes(pies = Dx, donuts = smoking), interactive = TRUE)
Wondering how to construct Pie Donut chart with facet functionality. My attempt is below:
ggPieDonut(data = acs, mapping = aes(pies = Dx, donuts = smoking), interactive = TRUE) +
facet_wrap(facets = vars(sex))
NULL
Upvotes: 2
Views: 2187
Reputation: 3222
The code in your attempt doesn't work because when interactive = TRUE
, ggPieDonut()
doesn't return a ggplot, but a htmlwidget:
ggPieDonut(
data = acs,
mapping = aes(pies = Dx, donuts = smoking),
interactive = TRUE
) %>% class()
#> [1] "girafe" "htmlwidget"
And facet_wrap()
only works with ggplots.
If you change to interactive = FALSE
you get another problem:
ggPieDonut(
data = acs,
mapping = aes(pies = Dx, donuts = smoking),
interactive = FALSE
) +
facet_wrap(~sex)
#> Error in `combine_vars()`:
#> ! At least one layer must contain all faceting variables: `sex`.
The geoms doesn't contain both values of sex
, so facet_wrap()
doesn't know how to facet on it.
A solution is to create two plots on different subsets of the data, and use patchwork
to combine the two plots:
library(patchwork)
p1 <-
acs %>%
filter(sex == "Male") %>%
ggPieDonut(mapping = aes(pies = Dx, donuts = smoking), interactive = FALSE) +
labs(title = "Male")
p2 <-
acs %>%
filter(sex == "Female") %>%
ggPieDonut(mapping = aes(pies = Dx, donuts = smoking), interactive = FALSE) +
labs(title = "Female")
p1 + p2
Output:
As @MikkoMarttila suggested, it might be better to create this as a function. If I were to reuse the function, I would probably write it like this:
make_faceted_plot <- function(data, pie, donut, facet_by) {
data %>%
dplyr::pull( {{facet_by}} ) %>%
unique() %>%
purrr::map(
~ data %>%
dplyr::filter( {{facet_by}} == .x) %>%
ggiraphExtra::ggPieDonut(
ggplot2::aes(pies = {{pie}}, donuts = {{donut}}),
interactive = FALSE
) +
ggplot2::labs(title = .x)
) %>%
patchwork::wrap_plots()
}
This can then be used to facet on however many categories we want, and on any dataset, for example:
library(patchwork)
library(dplyr)
# Expandable example data
df <- data.frame(
eyes = sample(c("Blue", "Bown", "Green"), size = 100, replace = TRUE),
hair = sample(c("blonde", "brunette", "raven"), size = 100, replace = TRUE),
sex = sample(c("male", "female"), size = 100, replace = TRUE)
)
df %>%
make_faceted_plot(
pie = eyes,
donut = sex,
facet_by = hair
)
Again, as suggested by @MikkoMarttila, this can be piped into ggiraph::girafe(code = print(.))
to add some interactivity.
The OP wants the labels to be the same in the static and interactive plots.
The labels for both the static and interactive plots are stored inside <the plot object>$plot_env
. From here it's just a matter of looking around, and replacing the static labels with the interactive ones. Since the interactive labels contains HTML-tags, we do some cleaning first. I would wrap this in a function, as such:
change_label <- function(plot) {
plot$plot_env$Pielabel <-
plot$plot_env$data2$label %>%
stringr::str_replace_all("<br>", "\n") %>%
stringr::str_replace("\\(", " \\(")
plot$plot_env$label2 <-
plot$plot_env$dat1$label %>%
stringr::str_replace_all("<br>", "\n") %>%
stringr::str_replace("\\(", " \\(") %>%
stringr::str_remove("(NSTEMI\\n|STEMI\\n|Unstable Angina\n)")
plot
}
By adding this function to make_plot()
we get the labels we want:
make_faceted_plot <- function(data, pie, donut, facet_by) {
data %>%
dplyr::pull( {{facet_by}} ) %>%
unique() %>%
purrr::map(
~ data %>%
dplyr::filter( {{facet_by}} == .x) %>%
ggiraphExtra::ggPieDonut(
ggplot2::aes(pies = {{pie}}, donuts = {{donut}}),
interactive = FALSE
) +
ggplot2::labs(title = .x)
) %>%
purrr::map(change_label) %>% # <-- added change_label() here
patchwork::wrap_plots()
}
acs %>%
make_faceted_plot(
pie = Dx,
donut = smoking,
facet_by = sex
)
Upvotes: 2
Reputation: 11908
It seems @jpiversen and I had the same idea about this, so I initially didn’t post my answer after seeing theirs. However, by request of the OP, and in case it proves useful in some way, here’s my take on this:
I think the short answer is: you can’t.
There’s no option for facetting in the function call, and the resulting
ggplot2
object has had it’s data modified enough to not have access to the
original columns anymore, making ggplot facetting afterwards impossible.
However, you can get in the vicinity of facetting by constructing the plots separately for each level of the variable you wanted to facet by, and then assembling the plots together.
Here’s how I’d approach that:
library(tidyverse)
library(ggiraphExtra)
library(moonBook)
make_plot <- function(data) {
ggPieDonut(data = data, mapping = aes(pies = Dx, donuts = smoking))
}
plots <- by(acs, acs$sex, make_plot)
combined_plots <- patchwork::wrap_plots(plots) +
patchwork::plot_annotation(tag_levels = list(names(plots)))
combined_plots
I don’t know how to do the combination step if you specify interactive = TRUE
,
but as a workaround some approximation of interactivity could be also reached
with a simple girafe()
call on the combined plots:
ggiraph::girafe(code = print(combined_plots))
Upvotes: 2