0

I have the following function, gigl, where I am trying to capture the variables on the left and right of |. Currently my code only captures the variables if it is named exactly s or n.

How can I generalize the following to evaluate any variable regardless of the name?

gigl <- function(form,data){

  s <- rlang::f_lhs(f_lhs(form))
  n <- rlang::f_rhs(f_lhs(form))
  s <- eval_tidy(data$s) # don't like that I have to use the same name as inputed. Make more general.
  n <- eval_tidy(data$n) # don't like that I have to use the same name as inputed. Make more general.
  output <- tibble(n,s) %>% data.matrix()
  output
  }

fit <- gigl(s | n ~ 1 , data=df)

Here is some toy data

library(tidyverse)
df <- tribble(
  ~n, ~s,
  10, 6,
  8, 7,
  6, 5
)

The following should work as above, but is currently not working

df2 <- tribble(
  ~total, ~positive,
  10, 6,
  8, 7,
  6, 5
)

fit <- gigl(total | positive ~ 1 , data=df2)

The output should be

      total  positive
[1,] 10       6
[2,]  8       7
[3,]  6       5
Alex
  • 2,603
  • 4
  • 40
  • 73

1 Answers1

1

Here's one way to do it. However, there's a lot of ways to break this implementation, and you would need to think about what you want to do if the input formula is not exactly in the format you expect. For example, I added a little check to make sure there actually is a | on the left hand side.

library(tidyverse)
library(rlang)

gigl <- function(form, data) {

  # Get the left hand side expression from the formula
  lhs <- f_lhs(form)

  # Check that the lhs actually has a `|`
  stopifnot(is.call(lhs), as_string(lhs[[1]]) == "|")

  # Get the expressions from either side of `|` in a list of length 2
  exprs <- as.list(lhs[-1])

  # Create names from them
  names <- map(exprs, expr_name)

  # Evaluate them to get the values
  values <- map(exprs, eval_tidy, data)

  # Construct the data frame
  df <- invoke(data.frame, set_names(values, names))

  # Return
  data.matrix(df)
}

Check that it works:

df2 <- tribble(
  ~total, ~positive,
  10, 6,
  8, 7,
  6, 5
)

gigl(total | positive ~ 1 , data = df2)
#>      total positive
#> [1,]    10        6
#> [2,]     8        7
#> [3,]     6        5

Or you could have a more succint solution using !!! with select:

gigl2 <- function(form, data) {
  lhs <- f_lhs(form)

  stopifnot(is.call(lhs))
  stopifnot(as_string(lhs[[1]]) == "|")

  exprs <- as.list(lhs[-1])
  df <- select(data, !!!exprs)

  data.matrix(df)
}

gigl2(total | positive ~ 1 , data = df2)
#>      total positive
#> [1,]    10        6
#> [2,]     8        7
#> [3,]     6        5

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

Mikko Marttila
  • 10,972
  • 18
  • 31
  • I have heard of `!!` but never `!!!`. Is that part of the `rlang` package? – Alex Feb 18 '18 at 23:35
  • 1
    Yes, it's basically `!!` for lists of quoted expressions rather than single ones. See for example: https://adv-r.hadley.nz/quasiquotation.html#with-rlang-1 – Mikko Marttila Feb 19 '18 at 05:52