一段时间内R的累计总和

时间:2018-08-13 06:50:10

标签: r

  id   date     goal      date_followup_3month  cumulative_sum
  1 2004-12-31    1           2005-03-31              3
  1 2005-01-21    2           2005-04-21              6
  1 2005-04-04    3           2005-07-03              4
  1 2005-04-04    1           2005-07-03              1
  2 2001-01-05    4           2001-04-05              4
  2 2002-02-05    3           2002-05-06              5

cumulative_sum列是每个ID从日期到三个月的目标累计值。

我认为的代码如下,结果没有出来。

  

错误代码:参数“是”缺失,没有默认值

for(i in 1:length(id)){

    cumulative_sum[i] <-  for(j in 1:length(id))
    {max(cumsum(ifelse(id[i] == id[j]
                       & date[j] >= date[i]
                       & date[j] <= date_followup_3month[i])
                ,goal[j],0))}

}

我想知道是否还有其他好的代码。非常感谢。

2 个答案:

答案 0 :(得分:4)

这里有几种可能性。基于SQL的解决方案(1)似乎具有很高的可读性,并且由于SQL可以优化联接,因此可能节省空间。 data.table解决方案(2)会产生较大的中间结果,但请参阅@Frank的注释来避免这种情况。在(3)和(4)中会创建更大的中间结果,如果数据足够大,这可能是不可行的。基于循环的解决方案(5)节省空间,但是使用R中通常不使用的样式。(5)可以按照我们在(6)中所示的直接方式将其转换为C ++(使用Rcpp)。

1)sqldf 可以在复杂的逻辑条件下使用自联接在SQL中表示该问题:

library(sqldf)
sqldf("select a.*, sum(b.goal) cumulative_sum
  from DF a
  join DF b on a.id = b.id and b.rowid >= a.rowid and b.date <= a.date_followup_3month
  group by a.rowid")

给予:

  id       date goal date_followup_3month cumulative_sum
1  1 2004-12-31    1           2005-03-31              3
2  1 2005-01-21    2           2005-04-21              6
3  1 2005-04-04    3           2005-07-03              4
4  1 2005-04-04    1           2005-07-03              1
5  2 2001-01-05    4           2001-04-05              4
6  2 2002-02-05    3           2002-05-06              3

2)data.table 这也可以在data.table中完成,尽管请注意,这涉及到创建具有大量行的中间对象,而sql可能会优化它。

library(data.table)

DT <- as.data.table(DF)
DT[, seq:=.I][
  DT, on = .(id == id, seq <= seq, date_followup_3month >= date)][
  , list(id = id[1], 
         date = date[1], 
         date_followup_3month = date_followup_3month[1],
         cumulative_sum = sum(i.goal)), by = seq]

3)基本R 这是一个基本解决方案,该解决方案仅在id上显式执行自连接,然后将条件中其他项的行向下子集化。最后,它使用tapply进行求和。它涉及显式生成s,这是更大的中间结果。

DF0 <- cbind(seq = 1:nrow(DF), DF)
s <- subset(merge(DF0, DF0, by = "id"), 
       seq.x <= seq.y & date_followup_3month.x >= date.y)
transform(DF, cumulative_sum = tapply(s$goal.y, s$seq.x, sum))

4)dplyr 这使用了dplyr,像(3)一样,由于它仅对id执行自联接,因此涉及潜在的非常大的中间结果。

library(dplyr)
DF %>% 
  mutate(seq = 1:n()) %>% 
  inner_join(., ., by = "id", suffix = c("", ".x")) %>%
  filter(seq.x >= seq & date.x <= date_followup_3month) %>%
  group_by(seq, date, goal, date_followup_3month) %>%
  summarize(cumulative_sum = sum(goal.x)) %>%
  ungroup %>%
  select(-seq)

5)循环-基本R 在R中不建议使用显式循环,该循环可能很慢,但另一方面,它相对简单且节省空间。这可以用作将代码转换为C ++的模型,我们将在此之后的解决方案中进行此操作。请注意,我们包括了一些优化。由于对输入进行了排序,因此j循环可以从i开始,而不是从1开始,并且一旦j循环中的条件失败,我们就可以立即退出j循环,因为令人满意的行必定同时出现。

n <- nrow(DF)
Sum <- numeric(n)
for(i in 1:n) {
  for(j in i:n) {
    if (with(DF, id[i] == id[j] && date[j] <= date_followup_3month[i])) {
      Sum[i] <- Sum[i] + DF$goal[j]
    } else break
  }
}
transform(DF, cumulative_sum = Sum)

6)Rcpp 我们可以将(5)转换为C ++。假设我们有一个名为cum_sum.cpp的文件,其中包含以下内容:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector cum_sum(NumericVector id, IntegerVector date, 
  IntegerVector date_followup_3month, NumericVector goal) {
  auto n = id.size();
  NumericVector Sum(n);
  for(auto i = 0; i < n; i++) {
    Sum[i] = 0.0;
    for(auto j = i; j < n; j++) {
      if (id[i] == id[j] && date[j] <= date_followup_3month[i]) {
        Sum[i] = Sum[i] + goal[j];
      } else break;
    }
  }
  return Sum;
}

然后运行:

library(Rcpp)
sourceCpp("cum_sum.cpp")
transform(DF, cumulative_sum = 
                cum_sum(id, date, date_followup_3month, as.numeric(goal)))

注意

可重复形式的输入DF为:

Lines <- "id   date     goal      date_followup_3month
  1 2004-12-31    1           2005-03-31
  1 2005-01-21    2           2005-04-21
  1 2005-04-04    3           2005-07-03
  1 2005-04-04    1           2005-07-03
  2 2001-01-05    4           2001-04-05
  2 2002-02-05    3           2002-05-06"
DF <- read.table(text = Lines, header = TRUE)
DF$date <- as.Date(DF$date)
DF$date_followup_3month <- as.Date(DF$date_followup_3month)

答案 1 :(得分:1)

对于满足日期和id条件的行,您可以仅使用sum来代替max(cumsum)。为了避免嵌套循环,可以使用函数。简化示例如下:

      goalsum <- function(date, i){
      start <- date$date[i]
      end <- date$date_followup_3month[i]
      ind <- date$id[i]
      tot_goal <- date%>%
        filter(date>=start & date<=end & id==ind)%>%
        summarise(sum(goal))
      return(tot_goal[1,1])
    }

    for(i in 1:length(date)){date$res[i] <-goalsum(date, i)}