Replace NULL value in nested tibble / dataframe

Viewed 164

I have this tibble of tibbles:

a = tibble(a = c(1:3))
df <- tibble::tibble(a1 = list(a,a), a2 = list(NULL,a))

# A tibble: 2 x 2
  a1               a2              
  <list>           <list>          
1 <tibble [3 x 1]> <NULL>          
2 <tibble [3 x 1]> <tibble [3 x 1]>

I'd like to replace the <NULL> value with another tibble, let's say a.

But,

df %>% tidyr::replace_na(list(a2 = a))

Gives this

# A tibble: 2 x 2
  a1               a2              
  <list>           <list>          
1 <tibble [3 x 1]> <int [3]>       
2 <tibble [3 x 1]> <tibble [3 x 1]>

While I'd like this

# A tibble: 2 x 2
  a1               a2              
  <list>           <list>          
1 <tibble [3 x 1]> <tibble [3 x 1]>   
2 <tibble [3 x 1]> <tibble [3 x 1]>

Is there an easy workaround?

3 Answers
a <- tibble::tibble(a = 1:3)
df <- tibble::tibble(a1 = list(a, a), a2 = list(NA, a))
df[is.na(df)] <- list(a)
df
# # A tibble: 2 × 2
#   a1               a2              
#   <list>           <list>          
# 1 <tibble [3 × 1]> <tibble [3 × 1]>
# 2 <tibble [3 × 1]> <tibble [3 × 1]>

Update as the OP modified the question:

library(dplyr)

a <- tibble(a = 1:3)
df <- tibble(a1 = list(a, a), a2 = list(NULL, a))
df %>% replace(. == "NULL", list(a))
# # A tibble: 2 × 2
#   a1               a2              
#   <list>           <list>          
# 1 <tibble [3 × 1]> <tibble [3 × 1]>
# 2 <tibble [3 × 1]> <tibble [3 × 1]>

Another possible solution:

library(tidyverse)

a = tibble(a = c(1:3))
df <- tibble::tibble(a1 = list(a,a), a2 = list(NULL,a))

df %>% 
  replace_na(list(a2 = list(a)))

#> # A tibble: 2 × 2
#>   a1               a2              
#>   <list>           <list>          
#> 1 <tibble [3 × 1]> <tibble [3 × 1]>
#> 2 <tibble [3 × 1]> <tibble [3 × 1]>

Another (general) solution, given that the level of nesting is known in advance:

library(purrr)

map_depth(df, .depth = 2, .f = ~if(is.null(.x)) a else .x) %>% 
  as_tibble()

#> # A tibble: 2 × 2
#>   a1               a2              
#>   <list>           <list>          
#> 1 <tibble [3 × 1]> <tibble [3 × 1]>
#> 2 <tibble [3 × 1]> <tibble [3 × 1]>

For arbitrary levels of nesting, we can also use rrapply() in the rrapply-pacakge:

library(rrapply)

rrapply(df, condition = is.null, f = function(x) a)

#> # A tibble: 2 × 2
#>   a1               a2              
#>   <list>           <list>          
#> 1 <tibble [3 × 1]> <tibble [3 × 1]>
#> 2 <tibble [3 × 1]> <tibble [3 × 1]>
Related