3

I faced a problem when exercising with data.table. Here is my problem. I wrote a simple subtraction function:

minus <- function(a, b){
      return(a - b)
  }

My dataset is a simple data.table:

dt <- as.data.table(data.frame(first=c(5, 6, 7), second=c(1,2,3)))
dt
  first second
1     5      1
2     6      2
3     7      3

I would like to write another function,

myFunc <- function(dt, FUN, ...){
      return(dt[, new := FUN(...)])
  }

The usage is simply:

res <- myFunc(dt, minus, first, second)

and the result would be the following:

res
   first second new
1:     5      1   4
2:     6      2   4
3:     7      3   4

How can I archive such a goal? Thanks!

2 Answers 2

2

Maybe there's a better way, but you can try something like this:

myFunc <- function(indt, FUN, ...) {
  FUN <- deparse(substitute(FUN))    # Get FUN as a string
  FUN <- match.fun(FUN)              # Match it to an existing function
  dots <- substitute(list(...))[-1]  # Get the rest of the stuff
  # I've used `copy(indt)` so that it doesn't affect your original dataset
  copy(indt)[, new := Reduce(FUN, mget(sapply(dots, deparse)))][]
}

(Note that this is very specific to how you've created your minus() function.)

Here it is in action:

res <- myFunc(dt, minus, first, second)
dt  ## Unchanged
#    first second
# 1:     5      1
# 2:     6      2
# 3:     7      3

res
#    first second new
# 1:     5      1   4
# 2:     6      2   4
# 3:     7      3   4
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you! What if I would like to change the new column name based on input?
@Nal-rA, maybe use setnames?
Yeah, that is doable. Thank you again!
What if my minus function change to minus <- function(a, b, c)? Your solution does not seem to work.
You might need something with do.call instead of Reduce. Not in front of my computer right now...
|
0

Here is a solution with do.call:

myFunc <- function(dt, FUN, ...){
  arg.names <- as.character(match.call()[-(1:3)])
  copy(dt)[, "new" := do.call(FUN, lapply(arg.names, function(x) get(x)))]
}
#test
myFunc(dt, minus, first, second)
#   first second new
#1:     5      1   4
#2:     6      2   4
#3:     7      3   4

Comments

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.