user51462
user51462

Reputation: 2022

Composite functions in mutate_at

I have a tibble:

df = tibble(one = list('a', 'b'), two = list(c('p1', 'p2', 'p3'), NA_character_), three = list(NA_character_, c('z1', 'z2', 'z3')))

df
# A tibble: 2 x 3
  one   two       three    
  <chr> <list>    <list>   
1 a     <chr [3]> <chr [1]>
2 b     <chr [1]> <chr [3]>

I would like to replace the missing values in columns two and three with the values of column one using coalesce() and then collapse each character vector (rowwise) in two and three into a single string using toString(). My expected output looks like this:

df = tibble(one = c('a', 'b'), two = list('p1, p2, p3', 'b'), three = list('a', 'z1, z2, z3'))
df
# A tibble: 2 x 3
  one   two       three    
  <chr> <list>    <list>   
1 a     <chr [1]> <chr [1]>
2 b     <chr [1]> <chr [1]>

This is what I had tried initially:

df %>% mutate_at(vars(two, three), funs(coalesce(., one) %>% map(., toString)))

I know that funs() quotes its arguments but I don't understand why it doesn't work with the pipeline. The documentation also advises that funs is being deprecated but I'm not sure what to use in its place. I was wondering if someone could shed some light on why the above doesn't work since I'm not at all familiar with the inner workings of scoped verbs.

Upvotes: 3

Views: 287

Answers (2)

akrun
akrun

Reputation: 887241

With tidyverse, we can make use of pmap

library(tidyverse)
out <- df %>% 
          mutate_at(vars(two, three),
               list(~ pmap(list(., one), ~ list(...) %>% 
                        reduce(coalesce) %>%
                        toString)))

out
# A tibble: 2 x 3
#  one   two       three    
#  <chr> <list>    <list>   
#1 a     <chr [1]> <chr [1]>
#2 b     <chr [1]> <chr [1]>
out$two
#[[1]]
#[1] "p1, p2, p3"

#[[2]]
#[1] "b"

out$three
#[[1]]
#[1] "a"

#[[2]]
#[1] "z1, z2, z3"

Or using Map from base R

df[-1] <- lapply(df[-1], function(x) do.call(Map, 
     c(f = function(x, y) toString(coalesce(x, y)), list(x, df$one))))
df
# A tibble: 2 x 3
#  one   two       three    
#  <chr> <list>    <list>   
#1 a     <chr [1]> <chr [1]>
#2 b     <chr [1]> <chr [1]>
df$two
#[[1]]
#[1] "p1, p2, p3"

#[[2]]
#[1] "b"

df$three
#[[1]]
#[1] "a"

#[[2]]
#[1] "z1, z2, z3"

Upvotes: 1

Ronak Shah
Ronak Shah

Reputation: 389055

We can use map2 , use coalesce and then toString.

library(dplyr)
library(purrr)

df1 <- df %>% 
        mutate_at(vars(two, three), ~map2(., one, ~toString(coalesce(.x, .y))))

df1

#  one   two       three    
#  <chr> <list>    <list>   
#1 a     <chr [1]> <chr [1]>
#2 b     <chr [1]> <chr [1]>

df1$two
#[[1]]
#[1] "p1, p2, p3"

#[[2]]
#[1] "b"

df1$three
#[[1]]
#[1] "a"

#[[2]]
#[1] "z1, z2, z3"

In the above example, we use lambda style expression using ~ to use it as a function and it is true funs has been deprecated and has been replaced with list() instead. The answer and comments in this question provides more insight into this.

Upvotes: 3

Related Questions