3

Wrangling data with data.table's, I find myself writing lines like this third line a lot:

DT = data.table(a = 1:10)
name = 'a'
DT[,eval(parse(text=sprintf('%s_plus_one := %s + 1',name,name)))]

which I had hoped to reduce to

DT[,s('%s_plus_one := %s + 1',name,name)]

using a function like:

# s is *very* short for substitute and evalute
s <- function(...)
    eval(parse(text=sprintf(...)))

but then I get this error:

> DT[,s('%s_plus_one := %s + 1',name,name)]
    Check that is.data.table(DT) == TRUE. Otherwise, := and `:=`(...) are defined for use in j, once only and in particular ways. See help(":=").

I know I can do this:

# sp is short for substitute and parse
sp <- function(...)
    parse(text=sprintf(...))

DT[,eval(sp('%s_plus_one := %s + 1',name,name))]
DT
#>      a a_plus_one
#>  1:  1          2
#>  2:  2          3
#>  3:  3          4
#>  4:  4          5

but building strings to be evaluated in a data.table assignment is so common, that I'd hoped minimize typing as much as possible.

1
  • 1
    why not build call from language objects instead of parsing string? it gives kind of validation before it is being evaluated. Commented Apr 1, 2016 at 2:41

2 Answers 2

7

Much of the time there shouldn't even be a need to go into the world of eval(parse()). I.e., for this example, put the name in parentheses on the left-hand side and use get on the right-hand side:

DT[, (paste0(name,"_plus_one")) := get(name) + 1]

DT
#     a a_plus_one
# 1:  1          2
# 2:  2          3
# 3:  3          4
# 4:  4          5
# 5:  5          6
# 6:  6          7
# 7:  7          8
# 8:  8          9
# 9:  9         10
#10: 10         11
Sign up to request clarification or add additional context in comments.

2 Comments

I didn't try, but I'm pretty sure the parentheses are not needed on lhs
@eddi - in this case, yep, but I tend to do it out of reflex as DT[, (name) := get(name) + 1] and DT[, name := get(name) + 1] give different results.
2

We could use .SDcols

DT[,(paste0(name,"_plus_one")) := .SD+1, .SDcols=name]
DT
#     a a_plus_one
# 1:  1          2
# 2:  2          3
# 3:  3          4
# 4:  4          5
# 5:  5          6
# 6:  6          7
# 7:  7          8
# 8:  8          9
# 9:  9         10
#10: 10         11

As @thelatemail mentioned this would also work with multiple columns

DT = data.table(a = 1:10, b=2:11)
name <- c("a","b")
DT[,(paste0(name,"_plus_one")) := .SD + 1, .SDcols=name] 

2 Comments

Bonus question: is there a way to use this solution when merging two data.tables which contain variables of the same name, as in this question?
@Jthorpe Perhaps you may check .EACHI . Without an example, it is not clear how you want to do it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.