4

I'm trying to wrap my head around quasiquotation so that I could use it together with a data.table call. Here is an example:

library(data.table)
library(rlang)
dt <- data.table(col1 = 1:10, col2 = 11:20)

dt[, col1]

If I wanted to wrap this into function, how would I do this? I tried:

foo <- function(dt, col) {
  col <- quo(col)

  expr(dt[, !!col1])
}

foo(dt, col1)

But get Error in enexpr(expr) : object 'col1' not found. I assume I'm missing some steps as data.table evaluates this differently than dplyr.

JBGruber
  • 11,727
  • 1
  • 23
  • 45
  • if at all you need to pass a symbol rather than a character, just use `substitute` then transform to character then use the double period format to subset from the main datatable – Onyambu Oct 21 '19 at 15:27
  • 1
    maybe `foo <- function(dt, col) dt[, eval(substitute(col))]`? – chinsoon12 Oct 22 '19 at 00:40

2 Answers2

5

You want to capture the column name as a symbol with

col <- ensym(col)

rather than quo() and then use

expr(dt[, !!col])

(not col1 which doesn't exist there) but that will just return an expression. If you want to evaluated it, you'd need

eval_tidy(expr(dt[, !!col]))

But really the quasinotation stuff works best in the tidyverse and not with data.table functions natively. The "data.table" way might be more like something in this existing question: Pass column name in data.table using variable. data.table very much prefers strings to symbols.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • Perfect, thanks! `col1` was a typo but `ensym` was the key here :) – JBGruber Oct 21 '19 at 15:41
  • I get what you are saying about the *"data.table" way* but the linked answer doesn't seem to work anymore. I thought about the strings vs. symbols argument but I had a lot of problems using `with = FALSE`, which is why I wanted to learn quasinotation in the first place. – JBGruber Oct 21 '19 at 16:03
1

You can use deparse and substitute and use the argument with=FALSE as in :

foo <- function(dt, col){
  col_str = deparse(substitute(col))
  dt[, col_str, with = F]
}

or you can use eval and substitute and use the default data.table argument with=TRUE as in :

foo <- function(dt, col){
  col_symb = substitute(col)
  dt[, eval(col_symb)] # by default: with=TRUE
}

In both cases, substitute will get the name of the argument that you pass to the parameter col. In the first case deparse will convert this name as a string, thus enabling us to select it from the data.table using with = FALSE. In the second case we evaluate (using eval) the name of the argument in the context of the data.table.

Etienne Kintzler
  • 672
  • 6
  • 12