Map dplyr function to each combination of variable pairs in an R dataframe
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
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())
a tidyverse using set_names
library(tidyverse)
f <- function(a, b) a - b # a simple function for sake of example
c(combn(df, 2, simplify = F),
combn(rev(df), 2, simplify = F)) %>%
set_names(map_chr(., ~paste(names(.), collapse = "_minus_"))) %>%
map(., ~f(.x[1], .x[2]) %>% pull) %>%
bind_cols()
# A tibble: 2 x 6
a_minus_b a_minus_c b_minus_c c_minus_b c_minus_a b_minus_a
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 -3 -4 -1 1 4 3
2 -1 -5 -4 4 5 1
Here is a base R solution that does what you want:
# Create combination
combos <- combn(names(df), 2, simplify = F)
combos <- c(combos, lapply(combos, rev))
# Apply function to each element of combos and add names
as.data.frame(lapply(combos, function(j) `names<-`(f(df[j[1]], df[j[2]]), paste0(j, collapse = "_minus_"))))
a_minus_b a_minus_c b_minus_c b_minus_a c_minus_a c_minus_b
1 -3 -4 -1 3 4 1
2 -1 -5 -4 1 5 4
# Same thing but easier to read
l <- lapply(combos, function(j) {
res <- f(df[j[1]], df[j[2]])
names(res) <- paste0(j, collapse = "_minus_")
res
})
as.data.frame(l)
Or, if you want the purrr
equivalent:
# Tidyverse equivalent
map_dfc(combos, ~ `names<-`(f(df[.[1]], df[.[2]]), paste0(., collapse = "_minus_")))