I try to write a function for custom tables with one or more column variables. I realised it for tables with one variable: .
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