Seeking a tidyverse esque way to replace a list column of plots that contain NA to a dummy plot? Something like ifelse().
I have a data frame that contains list columns (meta, do I call this a 'list column df' or what's the correct name for a var of this kind?).
Here's an example:
library(tidyverse)
library(patchwork)
exdata <- diamonds %>%
group_by(cut) %>%
nest %>%
crossing(dummy = 1:3) %>%
mutate(plots = map(.x = data, ~ ggplot(.x, aes(x = x, y = y)) + geom_point()))
patchwork::wrap_plots(exdata$plots)
This outputs some plots in a grid:
But, if some of the plots are NA:
exdata$plots[[3]] <- NA
patchwork::wrap_plots(exdata$plots)
# "Error: Only know how to add ggplots and/or grobs"
I would therefore like to replace any NAs with a dummy plot:
dummy_plot <- ggplot() + theme_void() # creates a blank plot
How can I integrate into my dplyr chain above that creates exdata
a ifelse where if plots is NA, then replace with dummy_plot, else leave as is?
library(tidyverse)
library(patchwork)
exdata <- diamonds %>%
group_by(cut) %>%
nest %>%
crossing(dummy = 1:3) %>% mutate(
#mess with the 3rd entry
data = if_else(row_number()==3,list(NA),data)) %>%
mutate(plots =
map(.x = data,
~ if(is_tibble(.x)){
ggplot(.x, aes(x = x, y = y)) +
geom_point()} else{
ggplot() +theme_void()
}))
patchwork::wrap_plots(exdata$plots)
Thanks but what I need is an ifelse check on the already existing plots column. This looks like it's doing a check on the data that generate the plots?
I tried this:
> blah <- exdata %>% mutate(plots = ifelse(is.na(plots), dummy_plot, plots))
> blah %>% head
# A tibble: 6 x 4
cut data dummy plots
<ord> <list> <int> <list>
1 Fair <tibble [1,610 × 9]> 1 <gg>
2 Fair <tibble [1,610 × 9]> 2 <gg>
3 Fair <tibble [1,610 × 9]> 3 <ScalsLst>
4 Good <tibble [4,906 × 9]> 1 <gg>
5 Good <tibble [4,906 × 9]> 2 <gg>
6 Good <tibble [4,906 × 9]> 3 <gg>
Not sure what ScaleLst is. I tried to look at it with:
blah$plots[[3]]
<ggproto object: Class ScalesList, gg>
add: function
clone: function
find: function
get_scales: function
has_scale: function
input: function
n: function
non_position_scales: function
scales: list
super: <ggproto object: Class ScalesList, gg>
Might be close? Not sure where to go from here.
For my own taste I like this approach:
library(tidyverse)
library(patchwork)
exdata <- diamonds %>%
group_by(cut) %>%
nest() %>%
crossing(dummy = 1:3) %>%
mutate(plots = map(
.x = data,
~ ggplot(.x, aes(x = x, y = y))
+geom_point()
)) %>% #poison the plots
mutate(
plots =
if_else(row_number() == 3, list(NA), plots)
)
exdata$plots <- map(
exdata$plots,
~ if (is.logical(.x)) {
ggplot() +
theme_void()
} else {
.x
})
patchwork::wrap_plots(
exdata$plots
)
1 Like
This works, thanks. Interesting that your test was for is.logical?
oh, you can test if is.ggplot, as you say its more readable
exdata$plots <- map(
exdata$plots,
~ if (is.ggplot(.x)) {.x} else {
ggplot() + theme_void()
})
1 Like
Thanks again! Tried modifying your answer slightly and this doesn't work as expected, any ideas why? Not important, I already have what I need but for kicks tried:
exdata <- exdata %>%
mutate(plots = map(.x = plots, ~ ifelse(is.ggplot(.x), .x, ggplot() + theme_void())))
Gives things such as:
exdata$plots[[3]]
[[1]]
list()
attr(,"class")
[1] "waiver"
exdata$plots[[1]]
[[1]]
# A tibble: 1,610 x 9
carat color clarity depth table price x y z
<dbl> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
1 0.22 E VS2 65.1 61 337 3.87 3.78 2.49
2 0.86 E SI2 55.1 69 2757 6.45 6.33 3.52
3 0.96 F SI2 66.3 62 2759 6.27 5.95 4.07
4 0.7 F VS2 64.5 57 2762 5.57 5.53 3.58
5 0.7 F VS2 65.3 55 2762 5.63 5.58 3.66
6 0.91 H SI2 64.4 57 2763 6.11 6.09 3.93
7 0.91 H SI2 65.7 60 2763 6.03 5.99 3.95
8 0.98 H SI2 67.9 60 2777 6.05 5.97 4.08
9 0.84 G SI1 55.1 67 2782 6.39 6.2 3.47
10 1.01 E I1 64.5 58 2788 6.29 6.21 4.03
# … with 1,600 more rows
Expected plots to be output with the above. Works with your solution, but not when I try using ifelse?
system
Closed
April 22, 2021, 4:23pm
9
This topic was automatically closed 7 days after the last reply. New replies are no longer allowed. If you have a query related to it or one of the replies, start a new topic and refer back with a link.