How can I deselect columns given in the ...
argument of a self written function. (I also need to select the columns at another point, so just specifying the columns with -
in ...
does not solve my problem.)
Any soltions are apreciated, select
-helpers, manipulating quosures
or expressions, ...
# very simple example data
test <- data.frame(a=1:3, b=1:3, c=1:3)
# function skeleton
testfun <- function(x, ...){
y <- select(x, ...)
z <- select(x, -...) # does of course not work like this
return(list(y, z)) # just as an example
}
# calling the function to select different columns
testfun(test, a)
testfun(test, a, b)
These easiest solution would be to select the positive columns, and then compare names to figure out which columns to drop, as in this answer.
To work on the dots directly,
- We will capture them in a list of quosures (
quos
).
- Unquote and splice the dots in with
UQS
for the positive selection.
- Do the same thing inside of
c()
so that we have a vector of selection.
- Negate that vector to do the negative selection.
This is the transformation described by (3) and (4).
library(dplyr)
dots <- quos(a, b)
quos(-c(UQS(dots)))
#> [[1]]
#> <quosure: frame>
#> ~-c(~a, ~b)
#>
#> attr(,"class")
#> [1] "quosures"
The full solution then would be
test <- data.frame(a = 1:3, b = 1:3, c = 1:3)
# function skeleton
testfun <- function(x, ...) {
dots <- quos(...)
y <- select(x, UQS(dots))
z <- select(x, -c(UQS(dots)))
return(list(y, z))
}
testfun(test, a)
#> [[1]]
#> a
#> 1 1
#> 2 2
#> 3 3
#>
#> [[2]]
#> b c
#> 1 1 1
#> 2 2 2
#> 3 3 3
testfun(test, a, b)
#> [[1]]
#> a b
#> 1 1 1
#> 2 2 2
#> 3 3 3
#>
#> [[2]]
#> c
#> 1 1
#> 2 2
#> 3 3
Test on selection helpers.
testfun(test, starts_with("b"), one_of("c"))
#> [[1]]
#> b c
#> 1 1 1
#> 2 2 2
#> 3 3 3
#>
#> [[2]]
#> a
#> 1 1
#> 2 2
#> 3 3
You can use this trick with purrr::modify_at
library(purrr)
testfun <- function(x, ...){
y <- select(x, ...)
z <- modify_at(x,c(...),~NULL)
return(list(y, z)) # just as an example
}
testfun(test,"a")
# [[1]]
# a
# 1 1
# 2 2
# 3 3
#
# [[2]]
# b c
# 1 1 1
# 2 2 2
# 3 3 3
What about that one?
testfun <- function(x, ...){
y <- select(x, ...)
z <- x[, !names(x) %in% names(y)]
return(list(y, z))
}
You can try:
testfun <- function(x, y, z){
y1 <- select(x, y)
z1 <- select(x, -one_of(z))
return(list(y1, z1))
}
testfun(test, "a", "b")
[[1]]
a
1 1
2 2
3 3
[[2]]
a c
1 1 1
2 2 2
3 3 3
You can also specify more variables using c
testfun(test, c("a", "c"), c("b", "a"))