Is there a slick way to join data tables in R where key values of time are close, but not exactly the same? For example, suppose I have a data table of results that are given for different time periods:
DT1 = data.table(x=rep(c("a","b","c"),each=3), time=c(10,30,60), v=1:9)
Here, we have some values (v) for different categories (x) taken at different times (time). Now suppose that I have data from another source that provides some time values for the different categories:
DT2=data.table(x=rep(c("a","b","c"),each=1),time=c(10,10,60))
I might be interested in trying to match the times in DT2 as closely as I can to DT1 to predict a value, v, for my DT2 categories. I would like to do something like
setkeyv(DT2,c("x","time"))
merge(DT1,DT2,by=c("time","v")
Which returns:
time x v
1: 10 a 1
2: 10 b 4
3: 60 c 9
But what if my times didn't have the same precision? For example:
DT2=data.table(x=rep(c("a","b","c"),each=1),time=c(17,54,3))
Is there a way to perform a similar merge but pick times of DT2 that are close to those of DT1? That is 17 would be close to 30, 54 close to 60, and 3 close to 10?
If this simple example isn't clear, I'll briefly explain the larger problem that I am having. I have a data table with columns: category, time, output1, output2... There are hundreds of categories with associated times. I might want to pull output 1 for all categories at a specific time. Since the times were sampled with no apparent logic, sometimes the time is rounded to the nearest even second; in other cases the times are rounded to the nearest minute or even 10 minutes.
I could write a script to rewrite times in more common format, but I am curious if there is a slick data.table solution that I haven't seen. I have explored the rolling merge with no success.
You can use
findInterval
to accomplish this:The idea: First sort the data.table by time because the second argument of
findInterval
requires increasing order of values. Now, usefindInterval
to find in which interval of3, 17, 54
does the values inDT1$time
fall and store it inid
. In this particular case, it happens to range from 1 to 3. So, set these values asid
column forDT2
. Once you find the intervals and getid
, then it's straightforward. Instead of settingx
andtime
, setx
andid
as keys and do your merge.Note: Suppose your
DT1$time
had a value of 0, then, the interval for that would have been 0. So, you'd get 4 unique values (0:3). In that case, it may be better to have DT2 with a time = 0 value as well. I just wanted to note this point here. I'll leave it to you.Another option may be
roll='nearest'
(new in v1.8.8 on CRAN).Note that 17 appears to be closer to 10 than 30, hence the result in the first row.
If you need to roll to the next observation (next observation carried backwards) :