Home > OS >  Apply n-series of dplyr verbs
Apply n-series of dplyr verbs

Time:10-09

I want to apply a series of dplyr-verbs; and for my example specifically, I am interested in mutate. Essentially, I want to produce an output that would look like this:

mtcars %>% mutate(x1=1) %>% mutate(x2=2) %>% mutate(x3=3) ...

Essentially, I have tried:


anotherTest <- function(data,...){
  cols = list(...)
  testFunc <- function(more){
    return(mutate(x = more))
  }
  n <- length(cols)
  addMutation <- replicate(n, testFunc)
  test <- {{data}} %>% lapply(addMutation, function(x){ x(cols)})
  return(test)
}
anotherTest(mtcars, 1)

But this produces the following error:

Error in get(as.character(FUN), mode = "function", envir = envir) : object 'addMutation' of mode 'function' was not found

Here is what I am trying to get - I am more interested in the logic than getting the naming output with mutate, because we are aiming for a series of dplyr-verbs:


anotherTest <- function(data,...){
  cols = enquos(...)
  testFunc <- function(more){
    return(mutate(x = !!!more))
  }
  n <- length(cols)
  addMutation <- replicate(n, testFunc)
  addCars <- replicate(n, data)
  mapply(function(x, y, z)y %>% x(z), addMutation, addCars, cols)
}

anotherTest(mtcars, vs, gear)

However, I get this:

Error in x(., z) : unused argument (z)

Whereas if I do not add an argument, I get this error:

Error in UseMethod("mutate") : no applicable method for 'mutate' applied to an object of class "logical"

Which makes sense as no value as been added for mutate however, adding a value strangely causes an error.

Additionaly example where what I am after can be very useful to understand. If I wanted to created a series of nested lists then I could generalise likeso:

nestLoop <- function(data, main, sub = NULL, ...) {
  cols <- enquos(...)
  
  outerNest <- data %>% nest_by({{main}})
  
  repeatNest <- function(cols) {
    return(mutate(data = map(data, ~ .x %>% nest(data = -{{!!!cols}}))))
  }
  for (num in seq_along(cols)) {
    nested <-
      outerNest %>% ungroup %>% mutate(data = map(data, ~ .x %>% nest(data = -{{sub}}) %>% repeatNest(cols[num])))
    }
  nested
  
}
nestLoop(mtcars, vs, am, gear)

However, this creates the following error:

Error in mutate(): ! Problem while computing data = map(data, ~.x %>% nest(data = -am) %>% repeatNest(cols[num])). Caused by error in repeatNest(): ! unused argument (cols[num])

As you can see from this example the following logic for %>% mutate(x1=1) %>% mutate(x2=2) %>% mutate(x3=3) would have to be exact, otherwise the lists will not nest sequentially.

CodePudding user response:

func <- function(x, ...) {
  dots <- list(...)
  for (ind in seq_along(dots)) {
    nm <- paste0("x", ind)
    x <- mutate(x, {{nm}} := dots[[ind]])
  }
  x
}
func(head(mtcars, 3), 1, 3)
#                mpg cyl disp  hp drat    wt  qsec vs am gear carb x1 x2
# Mazda RX4     21.0   6  160 110 3.90 2.620 16.46  0  1    4    4  1  3
# Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4  1  3
# Datsun 710    22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  1  3

Another option:

func <- function(...) {
  dots <- list(...)
  data.frame(
    setNames(dots, paste0("x", seq_along(dots)))
  )
}
head(mtcars, 3) %>%
  mutate(func(1, 3))
#                mpg cyl disp  hp drat    wt  qsec vs am gear carb x1 x2
# Mazda RX4     21.0   6  160 110 3.90 2.620 16.46  0  1    4    4  1  3
# Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4  1  3
# Datsun 710    22.8   4  108  93 3.85 2.320 18.61  1  1    4    1  1  3

CodePudding user response:

1) Cut down mtcars to make the output shorter. Now, mutate can take a data.frame as an argument so create a one row data.frame with the desired names and values and pass it to mutate.

library(dplyr)

n <- 3
mtcars %>%
  select(1:4) %>%
  head(4) %>%
  mutate(setNames(data.frame(as.list(1:n)), tolower(make.names(1:n))))

giving:

                mpg cyl disp  hp x1 x2 x3
Mazda RX4      21.0   6  160 110  1  2  3
Mazda RX4 Wag  21.0   6  160 110  1  2  3
Datsun 710     22.8   4  108  93  1  2  3
Hornet 4 Drive 21.4   6  258 110  1  2  3

2) cbind mtcars and a list of data.frames.

cbind(mtcars[1:4, 1:4], 
  list(data.frame(x1 = 1), data.frame(x2 = 2), data.frame(x3 = 3)))

3) create a chain of mutates (the result is a function) and use it with mtcars.

chain <- . %>% mutate(x1 = 1) %>% mutate(x2 = 2) %>% mutate(x3 = 3)
mtcars[1:4, 1:4] %>% chain
  • Related