Reputation: 8626
I'm trying to make a function which takes as input a data.table and condition and then modifies rows which are selected by condition. Of course I can define the condition explicitly as logical vector, like this:
dt1 <- fread(
"id,a,b
id1,1,10
id2,2,30
id3,3,40
")
test1 <- function(dtIn, condition) {
dtIn[condition, newcol:="new text"];
return(dtIn);
}
test1(dt1, dt1$b>10);
But ideally I would like to be able to pass the condition without the table's name, something like this:
test2 <- function(dtIn, condition) {
dtIn[substitute(condition), newcol:="new text"];
return(dtIn);
}
test2(dt1, b>10);
I tried substitute(condition)
, but it gives an error "i has not evaluated to logical, integer or double
". Is it possible to implement desired functionality?
UPD.
As answered by @Gregor, the correct code just uses eval
in addition to substitute
:
test3 <- function(dtIn, condition) {
dtIn[eval(substitute(condition)), newcol:="new text"];
return(dtIn);
}
It can happen also that I need to pass only the column name and build the condition inside my function. As advised from @Gregor, I can build the condition by paste
and then use eval(parse(...))
:
test4p <- function(dtIn, colName) {
condition <- parse(text=paste0(colName, ">20"))
dtIn[eval(condition), newcol:="new text"];
return(dtIn);
}
test4p(dt1, "b");
Myself, I came to another approach which uses get
:
test4g <- function(dtIn, colName) {
dtIn[get(colName)>20, newcol:="new text"];
return(dtIn);
}
test4g(dt1, "b");
The result is the same, and I am not enough competent to explain the difference here between usage of get
and eval(parse(...))
, so your comments are welcome.
With eval(parse(...))
, I was able to make function taking unquoted column name, i.e. for calling test4(dt1, b)
(just added substitute(colName)
to the code), but failed to do something like this without eval(parse(...))
.
Upvotes: 1
Views: 660
Reputation: 145755
You're just missing an eval()
.
test3 <- function(dtIn, condition) {
dtIn[eval(substitute(condition)), newcol:="new text"];
return(dtIn);
}
> test3(dt1, dt1$b>10);
id a b newcol
1: id1 1 10 NA
2: id2 2 30 new text
3: id3 3 40 new text
Even works without the dt1$
> test3(dt1, b>10);
id a b newcol
1: id1 1 10 NA
2: id2 2 30 new text
3: id3 3 40 new text
See Hadley's Non-Standard Evaluation Section for a thorough explanation.
Upvotes: 3