0

I am writing a custom function which requires creating a data frame out of entered arguments. I want users to allow two different way to enter the arguments. As you can see below, one of the way is working, but not the one relying on rlang.

The idea is that I want to give as much flexibility to the function user as possible, therefore the optional argument and two different methods to enter the argument.

Here is a reproducible code:

# loading libraries
library(dplyr)
library(rlang)
library(datasets)

# preparing the dataset
iris <- datasets::iris
iris$newvar <- 1:length(iris$Sepal.Length)
str(iris)
#> 'data.frame':    150 obs. of  6 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ newvar      : int  1 2 3 4 5 6 7 8 9 10 ...

# defining the custom function
myfun <- function(data = NULL, x, y, z = NULL) {
  if (!is.null(data)) {
    if (!is.null(z)) {
      data <-
        dplyr::select(
          .data = data,
          x = !!rlang::enquo(x),
          y = !!rlang::enquo(y),
          z = !!rlang::enquo(z)
        )
    } else {
      data <-
        dplyr::select(
          .data = data,
          x = !!rlang::enquo(x),
          y = !!rlang::enquo(y)
        )
    }
  } else {
    if (!is.null(z)) {
      data <-
        base::cbind.data.frame(x = x,
                               y = y,
                               z = z)
    } else {
      data <- base::cbind.data.frame(x = x,
                             y = y)
    }
  }

  print(str(data))

}

# method 1
# using the custom fuction without the optional argument
myfun(x = iris$Species, y = iris$Sepal.Length)
#> 'data.frame':    150 obs. of  2 variables:
#>  $ x: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ y: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> NULL

# using the custom fuction with the optional argument
myfun(x = iris$Species, y = iris$Sepal.Length, z = iris$newvar)
#> 'data.frame':    150 obs. of  3 variables:
#>  $ x: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ y: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ z: int  1 2 3 4 5 6 7 8 9 10 ...
#> NULL

# method 2
# using the custom fuction without the optional argument
myfun(data = iris, x = Species, y = Sepal.Length)
#> 'data.frame':    150 obs. of  2 variables:
#>  $ x: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ y: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#> NULL
# using the custom fuction with the optional argument
myfun(data = iris,
      x = Species,
      y = Sepal.Length,
      z = newvar)
#> Error in myfun(data = iris, x = Species, y = Sepal.Length, z = newvar): object 'newvar' not found

Created on 2018-02-02 by the reprex package (v0.1.1.9000).

Indrajeet Patil
  • 4,673
  • 2
  • 20
  • 51

1 Answers1

1

It seems that the test !is.null(z)) is what is failing. It's attempting to interpret z, but z is set to the name newvar and and the is.null call can't interpret it, because there is no newvar object to be found.

Try this, instead:

!is.null(quo(z)))
Edward Carney
  • 1,372
  • 9
  • 7
  • Thanks for the suggestion. This does seem to solve the problem I am facing but creates a different problem. If I now enter: `myfun(data = iris, x = Species, y = Sepal.Length)`, it gives me the following error: `Error: NULL must resolve to integer column positions, not NULL` – Indrajeet Patil Feb 02 '18 at 21:30
  • Interesting problem. If you remove the NULL assignment for the`z` in the definition, the `myfun(data = iris, x = Species, y = Sepal.Length)` will work. But, then the other form of the call doesn't. Hmm. – Edward Carney Feb 02 '18 at 21:58