Replace NAs in a dataframe list column that contains ggplots, within a dplyr chain

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?

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.