Apply a custom function rowwise with a character vector of column names as an argument

74 Views Asked by At
ref_tables <- tibble(`col 1` = letters[1:5], `col 2` = letters[6:10], `col 3` = letters[11:15])

  `col 1` `col 2` `col 3`
  <chr>   <chr>   <chr>  
1 a       f       k      
2 b       g       l      
3 c       h       m      
4 d       i       n      
5 e       j       o  

index_selection <- c("col 1", "col 2", "col 3")

I want to create a custom function that applies to each row of ref_tables. I want the result in a fourth column called dfs.

This is a trivial function:

f <- function(x) {
  return(paste(x, collapse = " "))
}

If I explicitly type the column names using backticks like this, I get results I'm expecting.

d <- ref_tables %>%
  rowwise() %>%
  mutate(dfs = list(f(c(`col 1`, `col 2`, `col 3`))))

# d$dfs[1] returns the result i'm expecting "a f k".

But I want to use the character vector index_selection.

d <- ref_tables %>%
  rowwise() %>%
  mutate(dfs = list(f(index_selection)))

# d$dfs[1] returns a result I am not expecting. "col 1 col 2 col 3".

I've tried using a combination of !!, !!!, sym() and cannot figure out how to do this. Can anyone help?

3

There are 3 best solutions below

0
dufei On BEST ANSWER

You can use column names from another vector with the help of all_of() within a selecting function such as c_across():

ref_tables %>%
  rowwise() %>%
  mutate(dfs = list(f(c_across(all_of(index_selection)))))
0
r2evans On

If your function can accept arbitrary arguments,

f <- function(...) return(paste(..., collapse = " "))

then we can simply do

ref_tables %>%
  mutate(dfs = do.call(mapply, c(list(FUN=f), pick(all_of(index_selection)))))
# # A tibble: 5 × 4
#   `col 1` `col 2` `col 3` dfs  
#   <chr>   <chr>   <chr>   <chr>
# 1 a       f       k       a f k
# 2 b       g       l       b g l
# 3 c       h       m       c h m
# 4 d       i       n       d i n
# 5 e       j       o       e j o

If you cannot change f, then a hack could be

g <- function(...) f(unlist(list(...)))

and use g in place of f above.

Another approach:

ref_tables %>%
  mutate(dfs = apply(pick(all_of(index_selection)), 1, f))
# # A tibble: 5 × 4
#   `col 1` `col 2` `col 3` dfs  
#   <chr>   <chr>   <chr>   <chr>
# 1 a       f       k       a f k
# 2 b       g       l       b g l
# 3 c       h       m       c h m
# 4 d       i       n       d i n
# 5 e       j       o       e j o
0
jay.sf On

Using paste in do.call and just cbind.

> ref_tables |> cbind(dfs=do.call('paste', ref_tables[index_selection]))
  col 1 col 2 col 3   dfs
1     a     f     k a f k
2     b     g     l b g l
3     c     h     m c h m
4     d     i     n d i n
5     e     j     o e j o