Home > OS >  R function to bind_rows the results of another function
R function to bind_rows the results of another function

Time:02-19

I try to write a function for custom tables with one or more column variables. I realised it for tables with one variable: Example of a table (in german).

Now I try to implement a function to get a custom table for a set of variables with for instance means and multiple column tables. My Problem is to bind them together.

This is, what I have:

library(tidyverse)

## at first some example data:

dv1 <- c(1, 0, 1, 0, 1) # dependent variable 1
dv2 <- c(1, 0, 1, 1, 1) # dependent variable 2
iv1 <- c("m", "f", "f", "m", "m") # independent variable 1
iv2 <- c(30, 40, 30, 40, 40) # independent variable 2
iv3 <- c("b", "c", "b", "a", "a") # ...

DATA <- data_frame(iv1, iv2, iv3, dv1, dv2) # build data frame

# the help function

cross_fun <- function(.data, DV, IV = IVs, fn = ~ mean(.x)) {

   df <- .data   %>% 
      select(all_of({{ IV }}), {{ DV }})  %>% 
    mutate(var = "dv") %>% # here I would like to have the {{ DV }} Argument as values of var, but mutate(var = {{ DV }}) or mutate (var = quote(DV)) does'nt work
    mutate(across(all_of({{IV}}), as.character)) # for using it in "names_from" in pivot_wider

  LIST <- list() # define a list

  for (i in 1:(ncol(df)-2)) { # -1 for the DV
    LIST[[i]] <- df %>% select(i, {{ DV }}, var)
  }
  dt <- purrr::map(
  .x = LIST,
  .f = ~ tidyr::pivot_wider(.x, names_from = 1, values_from = 2, values_fn = fn)
                  ) %>%
    purrr::reduce(left_join, by ="var")

  return(dt)
}

# What I can do
## simple custom table

DATA %>% cross_fun(dv1, IV = c('iv3', 'iv1', 'iv2'))  

## or I use a set (IVs is standard in cross_fun) in multiple tables 
IVs <- c('iv3', 'iv1', 'iv2')

DATA %>% cross_fun(dv2)

## I can change the Variables for the columns and the function
DATA %>% 
  cross_fun(dv2, IV = c('iv3', 'iv1', 'iv2'), fn = ~sum(.x))

## now I try to bind them together in a way, that I can use it later in another function
List_2 <- list()

## I could write it in a List_2 ...
List_2[[1]] <- DATA %>% cross_fun(dv1)

# ... for every variable ...
List_2[[2]] <- DATA %>% cross_fun(dv2)

# ... and bind the rows
List_2 %>% 
  bind_rows()

# here comes my Problem, it doesn't work in my try with for loop ...
for (i in c('dv1', 'dv2')) { 
  Liste2[[i]] <- DATA %>% 
    cross_fun(DATA[[i]])
}

# or with map
DATA %>% 
  map(.x = c(dv1:dv2), .f = ~cross_fun(.x)) %>% # the cross_fun-function for more than one dependent variable
  bind_rows() 

Sorry for the messy code. I'm a beginner with R-functions.

greetings, ben

CodePudding user response:

Solution

First, an answer: purrr::map_dfr automatically row-binds its results, and you should specify it like this:

map_dfr(c("dv1", "dv2"), cross_fun, .data = DATA)

# # A tibble: 2 x 8
#   var       b     c     a     m     f  `30`  `40`
#   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 dv        1     0   0.5 0.667   0.5     1 0.333
# 2 dv        1     0   1   1       0.5     1 0.667

Critique

There are a few issues in your code, several having to do with passing incorrect arguments. e.g., your for loop is passing DATA[[i]] to DV instead of simply i; this passes the actual vector of values from DATA[["dv1"]], instead of just the name "dv1", which is what your function expects.

The following fixes this:

List_2 <- list()

# `i` is confusing because the loop iterates over characters, not integers;
# use something like `varname` instead
for (varname in c('dv1', 'dv2')) { 
  List_2[[varname]] <- DATA %>% 
    cross_fun(varname)
}
bind_rows(List_2)

# # A tibble: 2 x 8
#   var       b     c     a     m     f  `30`  `40`
#   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 dv        1     0   0.5 0.667   0.5     1 0.333
# 2 dv        1     0   1   1       0.5     1 0.667

Your map call has two issues. One, you're not passing DATA to cross_function(); you're just instead passing .x to the .data argument and nothing to the other args. Two, you're trying to pass DV as a symbol instead of character. While this is possible, it's tricky (and trying to iterate over symbols using map makes it trickier), and your code isn't set up to handle it correctly.

The following fixes this:

map(c("dv1", "dv2"), .f = ~ cross_fun(DATA, .x)) %>% 
  bind_rows() 

# # A tibble: 2 x 8
#   var       b     c     a     m     f  `30`  `40`
#   <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 dv        1     0   0.5 0.667   0.5     1 0.333
# 2 dv        1     0   1   1       0.5     1 0.667
  • Related