【问题标题】:Taking subset of data to do calculation in data.table取数据子集在 data.table 中进行计算
【发布时间】:2019-01-01 16:24:20
【问题描述】:

假设我有这个 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)会是昨天……

谢谢

【问题讨论】:

标签: r data.table


【解决方案1】:

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

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
 ...

【讨论】:

  • 我已经想到了这个答案,但是如果我需要滞后 3 年的数据,那么我需要移动 360*3 次...
【解决方案2】:

远离 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

我认为你想要什么。

【讨论】:

  • 你应该添加包名 (purrr?) 因为map 不是基本函数
【解决方案3】:

解决方案

这样的事情呢

(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 中子集的重要信息。

【讨论】:

  • 谢谢 Nate 让我试试这个!我认为 subset2 是我所追求的功能。
猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-25
  • 1970-01-01
相关资源
最近更新 更多