以数据子集在data.table中进行计算

时间:2019-01-01 16:24:21

标签: r data.table

假设我有此数据表。

df = data.table(date = c(20180101, 20180102, 20180103, 20180104, 20180105, 20180106, 20180107, 20180108, 20180109, 20180110, 20180111, 20180112, 20180113, 20180114, 20180115, 20180116, 20180117, 20180118), value = c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18))

我想做一些使用数据子集的计算(例如均值)。例如:在20180103中,平均值将是(昨天)20180102和(今天)20180103值的总和((2 + 3)/ 2 = 2.5)。然后滚动直到该周期结束。

结果是这样的:

    date    mean
20180102     1.5
20180103     2.5
20180104     3.5
20180105     4.5
....

很明显,我可以编写一个for循环,为每次迭代分配数据子集,然后计算均值,存储数据并输出结果。使用for循环被认为太慢了,使用foreach我不知道如何保存结果...


for循环类似于:

datelist = df[, .(date)] 

# initialize the object
data = NA
temp = 0
for (i in 2:nrow(datelist)) {
     today = as.numeric(datelist[i])
     yesterday = as.numeric(datelist[i-1])

     temp = df[date >= yesterday & date <= today]

     temp = temp[, .(mean(value))]

     temp = cbind(datelist[i], mean = temp$V1)


     if (is.na(data)[1]){
         data=temp

         } else {
          data=rbind(data,temp)

         }


}

您可以看到我首先对数据进行了子集处理,然后将其称为temp,然后进行了计算(平均,使用它来执行lm,然后将任何函数堆叠到数据对象中)

这很慢而且效率很低,因为我有数百万个数据点


无论如何,我可以使用data.table语法做到这一点:

result = df[, { data = .SD[date >= yesterday & date <= today]
                mean = mean(data$value)
                list(mean = mean)}, by=.(date)]

我不知道昨天和今天如何表达?因此在for循环的情况下,昨天将是i-1,今天是i?

我做by =。(date)时的理解是data.table将查看每个日期并计算您提供的任何函数。如果我可以获得data.table的日期(即i)的值现在看,那么值(i-1)将是昨天...

谢谢

3 个答案:

答案 0 :(得分:2)

您可以在shift data.table子句中使用j运算符:

df[order(date),
   rollmean := (value + shift(value, n = 1, type = "lag"))/2][]

        date value rollmean
 1: 20180101     1       NA
 2: 20180102     2      1.5
 3: 20180103     3      2.5
 4: 20180104     4      3.5
 5: 20180105     5      4.5
 6: 20180106     6      5.5
 7: 20180107     7      6.5
 8: 20180108     8      7.5
 ...

答案 1 :(得分:0)

解决方案

这样的事情

(df$value[-nrow(df)]+df$value[-1] ) / 2
# yields
# [1]  1.5  2.5  3.5  4.5  5.5  6.5  7.5  8.5  9.5 10.5 11.5 12.5 13.5 14.5 15.5 16.5 17.5

在这里创建数据框

data.table::data.table(date = .subset2(df,1)[-1], 
                       mean = (df$value[-nrow(df)]+df$value[-1] ) / 2)
#        date mean
# 1  20180102  1.5
# 2  20180103  2.5
# 3  20180104  3.5
# 4  20180105  4.5
# 5  20180106  5.5
# ...

使用您提供的数据。


基准

以下是一些基准数据:

# create a bigger data frame
dfLarge <- data.table::data.table(
  date  = seq(as.Date('1989-01-01'),as.Date('2019-01-01'),1),
  value = 1:10958
)
microbenchmark::microbenchmark(sol = {
  data.table::data.table(date = .subset2(dfLarge,1)[-1], 
                         mean = (dfLarge$value[-nrow(dfLarge)]+dfLarge$value[-1] ) / 2)
})
# Unit: microseconds
#  expr     min      lq     mean  median      uq      max neval
#   sol 367.955 423.203 921.4908 530.781 788.969 22095.85   100

附录

如果此处的主要主题不是任务本身而是有效的子集设置,请指定其确切目标为确切(子集本身是一个广泛的主题,因此请添加有关需要完成的任务)。这样一来,您更有可能找到您要寻找的内容,而其他用户不会浪费任何精力。

话虽这么说,here是一个链接,提供了有关R中子集的一些重要信息。

答案 2 :(得分:0)

远离for循环,您可以使用如下purrr map函数:

nvals <- nrow(df) # get the number of rows
vals <- df$value # get the value vector
output <- map(1:nvals, function(x) mean(vals[c(x-1, x)])
output <- unlist(output)
df <- cbind(df, output)

输出向量为:

 1.0  1.5  2.5  3.5  4.5  5.5  6.5  7.5  8.5  9.5 10.5 11.5 12.5 13.5 14.5 15.5 16.5 17.5

我想你想要什么?