Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aggregate one data frame by time intervals from another data frame

I'm trying to aggregate two data frames (df1 and df2).

The first contains 3 variables: ID, Date1 and Date2.

df1

ID      Date1      Date2
 1 2016-03-01 2016-04-01
 1 2016-04-01 2016-05-01
 2 2016-03-14 2016-04-15
 2 2016-04-15 2016-05-17
 3 2016-05-01 2016-06-10
 3 2016-06-10 2016-07-15

The second also contains 3 variables: ID, Date3 and Value.

df2

ID      Date3 Value
 1 2016-03-15     5
 1 2016-04-04     7
 1 2016-04-28     7
 2 2016-03-18     3
 2 2016-03-27     5
 2 2016-04-08     9
 2 2016-04-20     2
 3 2016-05-05     6
 3 2016-05-25     8
 3 2016-06-13     3

The idea is to get, for each df1 row, the sum of df2$Value that have the same ID and for which Date3 is between Date1 and Date2:

ID      Date1      Date2 SumValue
 1 2016-03-01 2016-04-01        5
 1 2016-04-01 2016-05-01       14
 2 2016-03-14 2016-04-15       17
 2 2016-04-15 2016-05-17        2
 3 2016-05-01 2016-06-10       14
 3 2016-06-10 2016-07-15        3

I know how to make a loop on this, but the data frames are huge! Does someone has an efficient solution? Exploring data.table, plyr and dplyr but could not find a solution.

like image 774
EdM Avatar asked Mar 29 '16 12:03

EdM


2 Answers

A couple of data.table solutions that should scale well (and a good stop-gap until non-equi joins are implemented):

Do the comparison in J using by=EACHI.

library(data.table)
setDT(df1)
setDT(df2)

df1[, `:=`(Date1 = as.Date(Date1), Date2 = as.Date(Date2))]
df2[, Date3 := as.Date(Date3)]

df1[  df2,
      {
        idx = Date1 <= i.Date3 & i.Date3 <= Date2
        .(Date1 = Date1[idx],
          Date2 = Date2[idx],
          Date3 = i.Date3,
          Value = i.Value)
      }, 
      on=c("ID"),
      by=.EACHI][, .(sumValue = sum(Value)), by=.(ID, Date1, Date2)]

#   ID      Date1      Date2 sumValue
# 1:  1 2016-03-01 2016-04-01        5
# 2:  1 2016-04-01 2016-05-01       14
# 3:  2 2016-03-14 2016-04-15       17
# 4:  2 2016-04-15 2016-05-17        2
# 5:  3 2016-05-01 2016-06-10       14
# 6:  3 2016-06-10 2016-07-15        3

foverlap join (as suggested in the comments)

library(data.table)
setDT(df1)
setDT(df2)

df1[, `:=`(Date1 = as.Date(Date1), Date2 = as.Date(Date2))]
df2[, Date3 := as.Date(Date3)]

df2[, Date4 := Date3]


setkey(df1, ID, Date1, Date2)


foverlaps(df2,
          df1,
          by.x=c("ID", "Date3", "Date4"),
          type="within")[, .(sumValue = sum(Value)), by=.(ID, Date1, Date2)]

#     ID      Date1      Date2 sumValue
# 1:  1 2016-03-01 2016-04-01        5
# 2:  1 2016-04-01 2016-05-01       14
# 3:  2 2016-03-14 2016-04-15       17
# 4:  2 2016-04-15 2016-05-17        2
# 5:  3 2016-05-01 2016-06-10       14
# 6:  3 2016-06-10 2016-07-15        3

Further reading

Rolling join on data.table with duplicate keys

foverlap joins in data.table

like image 184
SymbolixAU Avatar answered Oct 21 '22 09:10

SymbolixAU


With the recently implemented non-equi joins feature in the current development version of data.table, v1.9.7, this can be done as follows:

dt2[dt1, .(sum = sum(Value)), on=.(ID, Date3>=Date1, Date3<=Date2), by=.EACHI]
#    ID      Date3      Date3 sum
# 1:  1 2016-03-01 2016-04-01   5
# 2:  1 2016-04-01 2016-05-01  14
# 3:  2 2016-03-14 2016-04-15  17
# 4:  2 2016-04-15 2016-05-17   2
# 5:  3 2016-05-01 2016-06-10  14
# 6:  3 2016-06-10 2016-07-15   3

The column names needs some fixing.. will work on it later.

like image 26
Arun Avatar answered Oct 21 '22 07:10

Arun