Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map dplyr function to each combination of variable pairs in an R dataframe

Tags:

r

dplyr

purrr

I want to map a function to each combination pair of variables in a dataframe in R, returning a dataframe with the function output for each pair. I can do this manually like so:

library(tidyverse)

df <- tibble(a = c(1, 2), b = c(4, 3), c = c(5, 7))

f <- function(a, b) a - b # a simple function for sake of example

df %>% transmute(a_minus_b = f(a, b),
                 a_minus_c = f(a, c),
                 b_minus_c = f(b, c),
                 b_minus_a = f(b, a),
                 c_minus_a = f(c, a),
                 c_minus_b = f(c, b))

Doing this manually is obviously impractical for a dataframe with many variables. How can I apply my function to each combination pair of variables using iteration?

like image 375
cengstro Avatar asked Jun 29 '20 13:06

cengstro


2 Answers

Another approach using dplyr and purrr may look like so:

library(tidyverse)

df <- tibble(a = c(1, 2), b = c(4, 3), c = c(5, 7))

f <- function(a, b) a - b # a simple function for sake of example

f_help <- function(x) {
  df %>% 
    transmute_at(setdiff(names(.), x), ~ f(!!sym(x), .x)) %>%
    rename_all(.funs = ~ paste0(x, "_minus_", .x))
}

map(names(df), f_help) %>% 
  bind_cols()
#> # A tibble: 2 x 6
#>   a_minus_b a_minus_c b_minus_a b_minus_c c_minus_a c_minus_b
#>       <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
#> 1        -3        -4         3        -1         4         1
#> 2        -1        -5         1        -4         5         4
like image 198
stefan Avatar answered Sep 18 '22 12:09

stefan


One dplyr and purrr solution could be:

map_dfc(.x = c(combn(rev(names(df)), 2, simplify = FALSE),
               combn(names(df), 2, simplify = FALSE)),
        ~ df %>%
         rowwise() %>%
         transmute(!!paste(.x, collapse = "_") := reduce(c_across(all_of(.x)), `-`)) %>%
         ungroup())

    c_b   c_a   b_a   a_b   a_c   b_c
  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     4     3    -3    -4    -1
2     4     5     1    -1    -5    -4

Or using the specified function:

map_dfc(.x = c(combn(rev(names(df)), 2, simplify = FALSE),
               combn(names(df), 2, simplify = FALSE)),
        ~ df %>%
         rowwise() %>%
         transmute(!!paste(.x, collapse = "_") := reduce(c_across(all_of(.x)), f)) %>%
         ungroup())
like image 23
tmfmnk Avatar answered Sep 18 '22 12:09

tmfmnk