1

Apparently I am too stupid to enter the correct search terms, b/c I think that my question is not unique at all.

How to refer to a variable by string in the i part of data.table? with and ..x are all good for the j part, but what would be the equivalent in the i part? Do I have to use evil eval (pun intended ;)

library(data.table)
dt <- data.table(x = 1:4, y = 4:1)

my_filter_fun <- function(var = names(dt)) {
  var <- match.arg(var)
  dt[eval(parse(text = paste(var, "== 1")))]
}
my_filter_fun("x")
my_filter_fun("y")

What is the idiomatic way in data.table to do so? Coming from dplyr I think I am looking for the equivalent of quosures for data.table?

Bonus question: how could i implement my_filter_fun such that such a call

my_filter_fun(x > 1)

would return the same result as

dt[x > 1]
thothal
  • 16,690
  • 3
  • 36
  • 71
  • 1
    might be relevant: https://stackoverflow.com/questions/24833247/how-can-one-work-fully-generically-in-data-table-in-r-with-column-names-in-varia/54800108#54800108 and https://stackoverflow.com/search?tab=newest&q=user%3a1191259%20substitute – chinsoon12 May 25 '20 at 22:49
  • Wow, a great read. Totally immersed into all the links from that post. It does answer a lot f my other questions too and also questions I did not even have yet :) +1 – thothal May 26 '20 at 09:00

2 Answers2

2

For your first question, I suggest using get() to avoid the evil of eval():

my_filter_fun <- function(var = names(dt)) {
  var <- match.arg(var)
  dt[get(var) == 1]
}
my_filter_fun("x")
   x y
1: 1 4

For the bonus question, you could do the following. It might it can be simplified though - just that I don't know how.

bonus_filter_fun <- function(filter) {
  filter <- deparse(substitute(filter))
  dt[eval(parse(text = filter))]
}
bonus_filter_fun(x > 1)
   x y
1: 2 3
2: 3 2
3: 4 1
s_baldur
  • 29,441
  • 4
  • 36
  • 69
  • Perfect! It is still a bit mind-boggling to me to use `data.table` efficiently in functions (being more of a `dplyr` guy myself - and there quasiquotation solves most of the probs) – thothal May 26 '20 at 09:03
  • 2
    see https://stackoverflow.com/a/57432125/1989480. Hence, maybe something like `bonus_filter_fun <- function(iexpr) { eval(substitute(dt[cond], list(cond=substitute(iexpr)))) }` – chinsoon12 May 26 '20 at 09:33
0

You have to use esoteric R magic, but not eval. What you are asking about is NSE or non-standard evaluation. Here's an example similar to yours:

theDf <- tibble(Sticks = 4:7, Stones = 9:12)
#' @title Create a new column by adding 8 to one of the existing columns.
a8Col <- function(df, newName, existName){
  existName <- enquo(existName)
  df %>%
    mutate(!! newName := !! existName + 8L )
}

R > a8Col(theDf, "Bones", Sticks)
# A tibble: 4 x 3
Sticks Stones Bones
<int>  <int> <int>
1      4      9    12
2      5     10    13
3      6     11    14
4      7     12    15
R > a8Col(theDf, "Bones", Stones)
# A tibble: 4 x 3
Sticks Stones Bones
<int>  <int> <int>
1      4      9    17
2      5     10    18
3      6     11    19
4      7     12    20

Notice that I didn't have to put quotes around Sticks or Stones in the calls to a8Col.

NSE is a hard topic. Hadley Wickham has written two chapter on it, (Quasiquotation and Evaluation) in his "Advanced R" book.

David T
  • 1,993
  • 10
  • 18
  • Thanks,. This is, however, `dplyr` syntax which I am very well aware of. What I am asking is to do the same in `data.table` though. – thothal May 25 '20 at 17:47