R 通过id变量计算滚动和,缺少时间点

R 通过id变量计算滚动和,缺少时间点,r,sas,plyr,zoo,R,Sas,Plyr,Zoo,我正在努力学习R,我在SAS做了10多年的一些事情,但我不太清楚在R中做的最好方法。以以下数据为例: id class t count desired -- ----- ---------- ----- ------- 1 A 2010-01-15 1 1 1 A 2010-02-15 2 3 1 B 2010-04-15 3 3 1

我正在努力学习R,我在SAS做了10多年的一些事情,但我不太清楚在R中做的最好方法。以以下数据为例:

 id  class           t  count  desired
 --  -----  ----------  -----  -------
  1      A  2010-01-15      1        1
  1      A  2010-02-15      2        3
  1      B  2010-04-15      3        3
  1      B  2010-09-15      4        4
  2      A  2010-01-15      5        5
  2      B  2010-06-15      6        6
  2      B  2010-08-15      7       13
  2      B  2010-09-15      8       21
我希望在4个月的滚动窗口内,按id、类计算所需的滚动和列。请注意,并非每个id和类的组合都有所有月份

在SAS中,我通常采用以下两种方式之一:

  • 保留
    加上一个按id和类别划分的密码
  • PROC SQL
    在适当的窗口中,在id、class和df1.d-df2.d上使用从df as df1到df as df2的左连接
  • 解决这类问题的最佳方法是什么

    t <- as.Date(c("2010-01-15","2010-02-15","2010-04-15","2010-09-15",
                   "2010-01-15","2010-06-15","2010-08-15","2010-09-15"))
    class <- c("A","A","B","B","A","B","B","B")
    id <- c(1,1,1,1,2,2,2,2)
    count <- seq(1,8,length.out=8)
    desired <- c(1,3,3,4,5,6,13,21)
    df <- data.frame(id,class,t,count,desired)
    

    t我几乎不好意思发布这篇文章。我通常都很好,但一定有更好的方法

    这首先使用
    zoo
    as.yearmon
    来获取月份和年份的日期,然后对其进行整形,为每个
    id
    /
    类的组合获取一列,然后在之前、之后和缺少的月份填入零,然后使用
    zoo
    来获取滚动总和,然后只提取所需的月份,并与原始数据帧合并

    library(reshape2)
    library(zoo)
    df$yearmon <- as.yearmon(df$t)
    dfa <- dcast(id + class ~ yearmon, data=df, value.var="count")
    ida <- dfa[,1:2]
    dfa <- t(as.matrix(dfa[,-c(1:2)]))
    months <- with(df, seq(min(yearmon)-3/12, max(yearmon)+3/12, by=1/12))
    dfb <- array(dim=c(length(months), ncol(dfa)), 
                 dimnames=list(paste(months), colnames(dfa)))
    dfb[rownames(dfa),] <- dfa
    dfb[is.na(dfb)] <- 0
    dfb <- rollsumr(dfb,4, fill=0)
    rownames(dfb) <- paste(months)
    dfb <- dfb[rownames(dfa),]
    dfc <- cbind(ida, t(dfb))
    dfc <- melt(dfc, id.vars=c("class", "id"))
    names(dfc)[3:4] <- c("yearmon", "desired2")
    dfc$yearmon <- as.yearmon(dfc$yearmon)
    out <- merge(df,dfc)
    
    > out
      id class  yearmon          t count desired desired2
    1  1     A Feb 2010 2010-02-15     2       3        3
    2  1     A Jan 2010 2010-01-15     1       1        1
    3  1     B Apr 2010 2010-04-15     3       3        3
    4  1     B Sep 2010 2010-09-15     4       4        4
    5  2     A Jan 2010 2010-01-15     5       5        5
    6  2     B Aug 2010 2010-08-15     7      13       13
    7  2     B Jun 2010 2010-06-15     6       6        6
    8  2     B Sep 2010 2010-09-15     8      21       21
    
    library(重塑2)
    图书馆(动物园)
    
    df$yearmon以下是一些解决方案:

    1) zoo使用
    ave
    ,通过将原始系列
    z
    与网格
    g
    合并,为每组创建一个每月系列
    m
    。然后计算滚动总和并仅保留原始时间点:

    library(zoo)
    f <- function(i) { 
        z <- with(df[i, ], zoo(count, t))
        g <- zoo(, seq(start(z), end(z), by = "month"))
        m <- merge(z, g)
        window(rollapplyr(m, 4, sum, na.rm = TRUE, partial = TRUE), time(z))
    }
    df$desired <- ave(1:nrow(df), df$id, df$class, FUN = f)
    
    注:我们假设时间在每个组内排序(如问题所示)。如果不是这样,则首先排序
    df

    2) sqldf

    library(sqldf)
    sqldf("select id, class, a.t, a.'count', sum(b.'count') desired 
       from df a join df b 
       using(id, class) 
       where a.t - b.t between 0 and 100
       group by id, class, a.t")
    
    其中:

    > df
      id class          t count desired
    1  1     A 2010-01-15     1       1
    2  1     A 2010-02-15     2       3
    3  1     B 2010-04-15     3       3
    4  1     B 2010-09-15     4       4
    5  2     A 2010-01-15     5       5
    6  2     B 2010-06-15     6       6
    7  2     B 2010-08-15     7      13
    8  2     B 2010-09-15     8      21
    
      id class          t count desired
    1  1     A 2010-01-15     1       1
    2  1     A 2010-02-15     2       3
    3  1     B 2010-04-15     3       3
    4  1     B 2010-09-15     4       4
    5  2     A 2010-01-15     5       5
    6  2     B 2010-06-15     6       6
    7  2     B 2010-08-15     7      13
    8  2     B 2010-09-15     8      21
    
    注意:如果合并太大而无法放入内存,则使用
    sqldf(“…”,dbname=tempfile())
    将中间结果存储在数据库中,然后动态创建并自动销毁

    3) 基本Rsqldf解决方案激发了这个基本R解决方案,它只是将SQL转换为R:

    m <- merge(df, df, by = 1:2)
    s <- subset(m, t.x - t.y >= 0 & t.x - t.y <= 100)
    ag <- aggregate(count.y ~ t.x + class + id, s, sum)
    names(ag) <- c("t", "class", "id", "count", "desired")
    
    注意:这会在内存中进行合并,如果数据集非常大,这可能是一个问题

    更新:对第一个解决方案进行了较小的简化,并添加了第二个解决方案


    更新2:添加了第三种解决方案。

    可以使用data.table库找到这个问题的非常有效的答案

    ##Utilize the data.table package
    library("data.table")
    data <- data.table(t,class,id,count,desired)[order(id,class)]
    
    ##Assign each customer an ID
    data[,Cust_No:=.GRP,by=c("id","class")]
    
    ##Create "list" of comparison dates and values
    Ref <- data[,list(Compare_Value=list(I(count)),Compare_Date=list(I(t))), by=c("id","class")]
    
    ##Compare two lists and see of the compare date is within N days
    data$Roll.Val <- mapply(FUN = function(RD, NUM) {
      d <- as.numeric(Ref$Compare_Date[[NUM]] - RD)
      sum((d <= 0 & d >= -124)*Ref$Compare_Value[[NUM]])
    }, RD = data$t,NUM=data$Cust_No)
    
    ##Print out data
    data <- data[,list(id,class,t,count,desired,Roll.Val)][order(id,class)]
    data
    
    id class          t count desired Roll.Val
    1:  1     A 2010-01-15     1       1        1
    2:  1     A 2010-02-15     2       3        3
    3:  1     B 2010-04-15     3       3        3
    4:  1     B 2010-09-15     4       4        4
    5:  2     A 2010-01-15     5       5        5
    6:  2     B 2010-06-15     6       6        6
    7:  2     B 2010-08-15     7      13       13
    8:  2     B 2010-09-15     8      21       21
    
    ##利用data.table包
    库(“数据表”)
    数据有了软件包,你可以在滚动窗口上计算一切。下面是使用
    sum\u run

    library(runner)
    df %>%
      group_by(id) %>%
      mutate(
        output = sum_run(count, k = 30*4, idx = t)   
      )
    
    # <dbl> <fct> <date>     <dbl>   <dbl>  <dbl>
    #     1 A     2010-01-15     1       1      1
    #     1 A     2010-02-15     2       3      3
    #     1 B     2010-04-15     3       3      6
    #     1 B     2010-09-15     4       4      4
    #     2 A     2010-01-15     5       5      5
    #     2 B     2010-06-15     6       6      6
    #     2 B     2010-08-15     7      13     13
    #     2 B     2010-09-15     8      21     21
    
    库(运行程序)
    df%>%
    分组依据(id)%>%
    变异(
    输出=总和运行(计数,k=30*4,idx=t)
    )
    #             
    #1A 2010-01-15 11
    #1 A 2010-02-15 2 3 3
    #1B 2010-04-15 3 6
    #1B 2010-09-15 4
    #2A 2010-01-15 5
    #2b 2010-06-15 6
    #2b 2010-08-15 7 13
    #2b 2010-09-15 8 21
    
    什么是
    d
    ?其定义未包含在安装代码中。请查看
    zoo
    软件包。它可以相当容易地对时间一致的数据进行这些滚动总结。如果您对
    sql
    感到满意,可以使用
    sqldf
    包。@MatthewPlourd,我想@ADJ mean
    df我已经在使用sqldf包了。首先,我还没有完全弄清楚如何在sqldf查询中使用R函数,就像我在PROC SQL中使用许多SAS函数一样。在这种情况下,我的首选解决方案将涉及使用SAS的INTCK函数的R版本,以比我的原始示例中描述的更复杂的方式执行日期算法。但由于我是从零开始学习R,我更喜欢学习如何用R的方式做事(见Joe的评论),老实说,对于这样的东西,我只会继续使用SAS.)美好的很好地使用了
    ave
    ,我可能没有尽可能经常地使用它,另外还有一些使用
    zoo
    的新方法。谢谢同时感谢您在
    zoo
    软件包中所做的工作,非常感谢!悬赏,给一个当之无愧的回答。谢谢这是一个124天的滚动时间段。显然,这并不完全是4个月,但代码可以很容易地修改。
    library(runner)
    df %>%
      group_by(id) %>%
      mutate(
        output = sum_run(count, k = 30*4, idx = t)   
      )
    
    # <dbl> <fct> <date>     <dbl>   <dbl>  <dbl>
    #     1 A     2010-01-15     1       1      1
    #     1 A     2010-02-15     2       3      3
    #     1 B     2010-04-15     3       3      6
    #     1 B     2010-09-15     4       4      4
    #     2 A     2010-01-15     5       5      5
    #     2 B     2010-06-15     6       6      6
    #     2 B     2010-08-15     7      13     13
    #     2 B     2010-09-15     8      21     21