Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have function object as an element

Tags:

r

data.table

Short answer: yes, see the accepted reply.


I have the two data.table below.

stocks = data.table(Ticker = c('xx','xx','yy','yy'), Date = c(as.IDate("2000-01-01"), as.IDate("2000-01-02")), t = c(1.8, 3.5))
   Ticker       Date   t
1:     xx 2000-01-01 1.8
2:     xx 2000-01-02 3.5
3:     yy 2000-01-01 1.8
4:     yy 2000-01-02 3.5
tt = data.table(Date = c(as.IDate("2000-01-01"), as.IDate("2000-01-02")), t0 = c(1,2), t1 = c(2,3), t2 = c(3,4), y0 = c(10, 20), y1 = c(-20, -30), y2 = c(33,44))
         Date t0 t1 t2 y0  y1 y2
1: 2000-01-01  1  2  3 10 -20 33
2: 2000-01-02  2  3  4 20 -30 44

For each row in stocks, I want to find the approximate y given t, based on linear interpolation of values in tt.

zz = tt[stocks, on = 'Date']
zz[, y.approx := approx(c(t0,t1,t2), c(y0,y1,y2), t)$y, by = 'Date,Ticker']
         Date t0 t1 t2 y0  y1 y2 Ticker   t y.approx
1: 2000-01-01  1  2  3 10 -20 33     xx 1.8      -14
2: 2000-01-02  2  3  4 20 -30 44     xx 3.5        7
3: 2000-01-01  1  2  3 10 -20 33     yy 1.8      -14
4: 2000-01-02  2  3  4 20 -30 44     yy 3.5        7

The problem is that doing this way has lots of duplicate calculation. Ideally I want to define an approxfun for each day and apply it to each row in stocks. But datatable cannot take function objects as its element.

tt[, ff := approxfun(c(t0,t1,t2), c(y0,y1,y2)), by = Date]
Error in `[.data.table`(tt, , `:=`(ff, approxfun(c(t0, t1, t2), c(y0,  : 
  j evaluates to type 'closure'. Must evaluate to atomic vector or list.

My question is:

  1. Is there a better way than doing approx on each row (and being slow)?
  2. Is it possible for datatable to have function objects as its element?
like image 656
jf328 Avatar asked Apr 12 '16 10:04

jf328


People also ask

Can an object have a function as a property?

7.3. Methods—setting functions as properties of objects. In JavaScript, you can use functions as values, just like numbers, strings, and objects. That means you can pass them as arguments, return them from other functions, and set them as properties of objects.

Can an object value be a function?

An object is a collection of properties, and a property is an association between a name (or key) and a value. A property's value can be a function, in which case the property is known as a method.

Can we use object as a function argument?

To pass an object as an argument we write the object name as the argument while calling the function the same way we do it for other variables. Syntax: function_name(object_name); Example: In this Example there is a class which has an integer variable 'a' and a function 'add' which takes an object as argument.

Can you put a function in an object?

You can call a function inside an object by declaring the function as a property on the object and invoking it, e.g. obj. sum(2, 2) . An object's property can point to a function, just like it can point to a string, number or other values.


3 Answers

It's pretty easy to store functions in a data.table - you just need to put them in a list:

tt[, ff := .(list(approxfun(c(t0,t1,t2), c(y0,y1,y2)))), by = Date]
#         Date t0 t1 t2 y0  y1 y2         ff
#1: 2000-01-01  1  2  3 10 -20 33 <function>
#2: 2000-01-02  2  3  4 20 -30 44 <function>

stocks[tt, y.approx := ff[[1]](t), on = 'Date', by = .EACHI]
stocks
#   Ticker       Date   t y.approx
#1:     xx 2000-01-01 1.8      -14
#2:     xx 2000-01-02 3.5        7
#3:     yy 2000-01-01 1.8      -14
#4:     yy 2000-01-02 3.5        7
like image 59
eddi Avatar answered Sep 29 '22 08:09

eddi


How about something like:

> zz
         Date t0 t1 t2 y0  y1 y2 Ticker   t
1: 2000-01-01  1  2  3 10 -20 33     xx 1.8
2: 2000-01-02  2  3  4 20 -30 44     xx 3.5
3: 2000-01-01  1  2  3 10 -20 33     yy 1.8
4: 2000-01-02  2  3  4 20 -30 44     yy 3.5

> zz[t0<=t & t<=t1, y.approx:={a=(t-t0)/(t1-t0); y0+a*(y1-y0)}]
> zz
         Date t0 t1 t2 y0  y1 y2 Ticker   t y.approx
1: 2000-01-01  1  2  3 10 -20 33     xx 1.8      -14
2: 2000-01-02  2  3  4 20 -30 44     xx 3.5       NA
3: 2000-01-01  1  2  3 10 -20 33     yy 1.8      -14
4: 2000-01-02  2  3  4 20 -30 44     yy 3.5       NA

> zz[t1<=t & t<=t2, y.approx:={a=(t-t1)/(t2-t1); y1+a*(y2-y1)}]
> zz
         Date t0 t1 t2 y0  y1 y2 Ticker   t y.approx
1: 2000-01-01  1  2  3 10 -20 33     xx 1.8      -14
2: 2000-01-02  2  3  4 20 -30 44     xx 3.5        7
3: 2000-01-01  1  2  3 10 -20 33     yy 1.8      -14
4: 2000-01-02  2  3  4 20 -30 44     yy 3.5        7
> 

Don't know how general you need it to be (how many columns you really have). But worth trying to vectorize like this to save the function call overhead by row. Several iterations of a for loop for number of time deltas (2 in this case) should be faster than looping by row (let us know if you go that way and need to generate the query dynamically for each time delta).

like image 32
Matt Dowle Avatar answered Sep 29 '22 09:09

Matt Dowle


We can hack it with a global list of functions and the superassignment operator:

x <- list();
invisible(tt[,{ x[[as.character(Date)]] <<- approxfun(c(t0,t1,t2),c(y0,y1,y2)); 0; },Date]);
x;
## $`2000-01-01`
## function (v)
## .approxfun(x, y, v, method, yleft, yright, f)
## <bytecode: 0x602762000>
## <environment: 0x603118610>
##
## $`2000-01-02`
## function (v)
## .approxfun(x, y, v, method, yleft, yright, f)
## <bytecode: 0x602762000>
## <environment: 0x60312c9d0>
##
stocks[,y.approx:=x[[as.character(Date)]](t),Date];
##    Ticker       Date   t y.approx
## 1:     xx 2000-01-01 1.8      -14
## 2:     xx 2000-01-02 3.5        7
## 3:     yy 2000-01-01 1.8      -14
## 4:     yy 2000-01-02 3.5        7
like image 33
bgoldst Avatar answered Sep 29 '22 07:09

bgoldst