【问题标题】:Retain and lag function in R as SASR中的保留和滞后功能作为SAS
【发布时间】:2014-01-05 09:05:46
【问题描述】:

我正在寻找 R 中的函数,类似于 SAS 中的 lag1lag2retain 函数,可与 data.tables 一起使用。

我知道 R 中有 embedlag 之类的函数,但它们不返回单个值或前一个值。它们返回一组完整的向量。

R 中有什么东西可以和 data.table 一起使用吗?

有关 SAS 功能的更多信息:

【问题讨论】:

  • 我们大多数 R 用户多年前要么从未使用过 SAS,要么烧毁了我们的 SAS 手册来取暖。这些函数在 SAS 中有什么作用?
  • Joris,我不打算卷入编辑大战,但我进行这些编辑是有原因的。这不是 SAS 问题,因此不需要标签;并且这个问题可以而且应该很容易独立地作为一个关于 R 如何工作的问题而无需参考 SAS,而不仅仅是将 lag 函数作为那些知道两种语言的人的帮助者(以及对正在思考相同事情的未来用户的指南)关于滞后)。该问题的回答者无需阅读 SAS 文档即可回答此问题。
  • @Joe OP 的问题与他的 SAS 经验以及 SAS 和 R 之间的差异直接相关。他的问题只有在您知道 SAS 中的 DATA 步骤如何工作以及这与R方法。主要问题是他的问题在 R 上下文中没有意义。 R 中没有像 SAS 中的 LAG 函数返回的“先前记录”这样的东西。因此,我对您的编辑(直言不讳)进行了逆转。抱歉,如果我粗鲁,那不是我的本意。我社交能力不是很强。幸运的是我的电脑可以处理;-)
  • 我非常同情来到 R 尝试复制保留的 SAS 用户。在我使用 R 的早期,我搜索并发现了一个 lag 函数,并在尝试使用搞砸的 R 时间序列对象时完全偏离了方向。 NewRbies 应该学习使用“zoo”包。
  • @Joe 使用 SAS 标签是完全合理的,以便最大限度地查看可能独立关注 R 和 SAS 标签的人。查看 SAS 问题的人可能也有 R 的知识来回答这个问题。我认为我们不需要对单个标签那么迂腐!

标签: r data.table


【解决方案1】:

您必须知道,R 的工作方式与 SAS 中的数据步骤非常不同。 SAS 中的lag 函数用于数据步骤,并用于该数据步骤的隐式循环结构中。 retain 函数也是如此,它只是在进行数据循环时保持值不变。

另一方面,R 完全矢量化。这意味着您必须重新考虑自己想要做什么,并相应地进行调整。

  • retain 在 R 中根本没用,因为 R 默认会回收参数。如果您想明确地执行此操作,您可以查看例如rep() 来构造一个具有恒定值和一定长度的向量。
  • lag 是使用索引的问题,只是移动向量中所有值的位置。为了保持相同长度的向量,您需要添加一些NA 并删除一些额外的值。

一个简单的例子:这个 SAS 代码滞后一个变量 x 并添加一个具有常量值的变量 year

data one;
   retain year 2013;
   input x @@;
   y=lag1(x);
   z=lag2(x);
   datalines;
1 2 3 4 5 6
;

在 R 中,您可以像这样编写自己的滞后函数:

mylag <- function(x,k) c(rep(NA,k),head(x,-k))

这一行在向量的开头添加了 k 次 NA,并从向量中删除了最后 k 个值。结果是 SAS 中 lag1 等给出的滞后向量。

这允许类似:

nrs <- 1:6 # equivalent to datalines
one <- data.frame(
   x = nrs,
   y = mylag(nrs,1),
   z = mylag(nrs,2),
   year = 2013  # R automatically loops, so no extra command needed
)

结果是:

> one
  x  y  z year
1 1 NA NA 2013
2 2  1 NA 2013
3 3  2  1 2013
4 4  3  2 2013
5 5  4  3 2013
6 6  5  4 2013

同样适用于data.table 对象。这里的重要说明是重新考虑您的策略:在使用 R 时,您必须开始考虑向量和索引,而不是像 SAS 中的 DATA 步骤那样循环思考。

【讨论】:

    【解决方案2】:

    我会说相当于retainlag1lag2 的壁橱将是quantmod package 中的Lag function

    使用data.tables 非常容易。例如:

    library(data.table)
    library(quantmod)
    d <- data.table(v1=c(rep('a', 10), rep('b', 10)), v2=1:20)
    setkeyv(d, 'v1')
    d[,new_var := Lag(v2, 1), by='v1']
    d[,new_var2 := v2-Lag(v2, 3), by='v1']
    d[,new_var3 := Next(v2, 2), by='v1']
    

    这会产生以下结果:

    print(d)
        v1 v2 new_var new_var2 new_var3
     1:  a  1      NA       NA        3
     2:  a  2       1       NA        4
     3:  a  3       2       NA        5
     4:  a  4       3        3        6
     5:  a  5       4        3        7
     6:  a  6       5        3        8
     7:  a  7       6        3        9
     8:  a  8       7        3       10
     9:  a  9       8        3       NA
    10:  a 10       9        3       NA
    11:  b 11      NA       NA       13
    12:  b 12      11       NA       14
    13:  b 13      12       NA       15
    14:  b 14      13        3       16
    15:  b 15      14        3       17
    16:  b 16      15        3       18
    17:  b 17      16        3       19
    18:  b 18      17        3       20
    19:  b 19      18        3       NA
    20:  b 20      19        3       NA
    

    如您所见,Lag 让您回顾过去,Next 让您展望未来。这两个函数都很好,因为它们用 NA 填充结果,使其与输入具有相同的长度。

    如果您想获得更好的性能和更高的性能,您可以研究使用 data.table 对象的滚动连接。这与您所要求的有点不同,但在概念上是相关的,并且非常强大和令人敬畏,我必须分享。

    从 data.table 开始:

    library(data.table)
    library(quantmod)
    set.seed(42)
    d1 <- data.table(
        id=c(rep('a', 10), rep('b', 10)), 
        time=rep(1:10,2), 
        value=runif(20))
    setkeyv(d1, c('id', 'time'))
    print(d1)
    
        id time     value
     1:  a    1 0.9148060
     2:  a    2 0.9370754
     3:  a    3 0.2861395
     4:  a    4 0.8304476
     5:  a    5 0.6417455
     6:  a    6 0.5190959
     7:  a    7 0.7365883
     8:  a    8 0.1346666
     9:  a    9 0.6569923
    10:  a   10 0.7050648
    11:  b    1 0.4577418
    12:  b    2 0.7191123
    13:  b    3 0.9346722
    14:  b    4 0.2554288
    15:  b    5 0.4622928
    16:  b    6 0.9400145
    17:  b    7 0.9782264
    18:  b    8 0.1174874
    19:  b    9 0.4749971
    20:  b   10 0.5603327
    

    您有另一个要加入的 data.table,但并非所有时间索引都存在于第二个表中:

    d2 <- data.table(
            id=sample(c('a', 'b'), 5, replace=TRUE), 
            time=sample(1:10, 5), 
            value2=runif(5))
    setkeyv(d2, c('id', 'time'))
    print(d2)
       id time      value2
    1:  a    4 0.811055141
    2:  a   10 0.003948339
    3:  b    6 0.737595618
    4:  b    8 0.388108283
    5:  b    9 0.685169729
    

    常规合并会产生大量缺失值:

    d2[d1,,roll=FALSE]
        id time      value2     value
     1:  a    1          NA 0.9148060
     2:  a    2          NA 0.9370754
     3:  a    3          NA 0.2861395
     4:  a    4 0.811055141 0.8304476
     5:  a    5          NA 0.6417455
     6:  a    6          NA 0.5190959
     7:  a    7          NA 0.7365883
     8:  a    8          NA 0.1346666
     9:  a    9          NA 0.6569923
    10:  a   10 0.003948339 0.7050648
    11:  b    1          NA 0.4577418
    12:  b    2          NA 0.7191123
    13:  b    3          NA 0.9346722
    14:  b    4          NA 0.2554288
    15:  b    5          NA 0.4622928
    16:  b    6 0.737595618 0.9400145
    17:  b    7          NA 0.9782264
    18:  b    8 0.388108283 0.1174874
    19:  b    9 0.685169729 0.4749971
    20:  b   10          NA 0.5603327
    

    但是,data.table 允许您在主索引内向前滚动二级索引!

    d2[d1,,roll=TRUE]
        id time      value2     value
     1:  a    1          NA 0.9148060
     2:  a    2          NA 0.9370754
     3:  a    3          NA 0.2861395
     4:  a    4 0.811055141 0.8304476
     5:  a    5 0.811055141 0.6417455
     6:  a    6 0.811055141 0.5190959
     7:  a    7 0.811055141 0.7365883
     8:  a    8 0.811055141 0.1346666
     9:  a    9 0.811055141 0.6569923
    10:  a   10 0.003948339 0.7050648
    11:  b    1          NA 0.4577418
    12:  b    2          NA 0.7191123
    13:  b    3          NA 0.9346722
    14:  b    4          NA 0.2554288
    15:  b    5          NA 0.4622928
    16:  b    6 0.737595618 0.9400145
    17:  b    7 0.737595618 0.9782264
    18:  b    8 0.388108283 0.1174874
    19:  b    9 0.685169729 0.4749971
    20:  b   10 0.685169729 0.5603327
    

    这真是太酷了:旧的观察结果会及时滚动,直到它们被新的观察结果取代。如果您想在系列开始时替换 NA 值,您可以通过向后滚动第一个观察来实现:

    d2[d1,,roll=TRUE, rollends=c(TRUE, TRUE)]
        id time      value2     value
     1:  a    1 0.811055141 0.9148060
     2:  a    2 0.811055141 0.9370754
     3:  a    3 0.811055141 0.2861395
     4:  a    4 0.811055141 0.8304476
     5:  a    5 0.811055141 0.6417455
     6:  a    6 0.811055141 0.5190959
     7:  a    7 0.811055141 0.7365883
     8:  a    8 0.811055141 0.1346666
     9:  a    9 0.811055141 0.6569923
    10:  a   10 0.003948339 0.7050648
    11:  b    1 0.737595618 0.4577418
    12:  b    2 0.737595618 0.7191123
    13:  b    3 0.737595618 0.9346722
    14:  b    4 0.737595618 0.2554288
    15:  b    5 0.737595618 0.4622928
    16:  b    6 0.737595618 0.9400145
    17:  b    7 0.737595618 0.9782264
    18:  b    8 0.388108283 0.1174874
    19:  b    9 0.685169729 0.4749971
    20:  b   10 0.685169729 0.5603327
    

    这些滚动连接绝对令人难以置信,而且我从未见过它们在任何其他开源包中实现(请参阅?data.table 了解更多信息)。关闭您的“SAS 大脑”并打开您的“R 大脑”需要一点时间,但是一旦您克服了最初的困难,您会发现该语言更具表现力。

    【讨论】:

      【解决方案3】:

      要保留,试试这个:

      retain<-function(x,event,outside=NA)
      {
        indices <- c(1,which(event==TRUE), nrow(df)+1)
        values <- c(outside,x[event==TRUE])
        y<- rep(values, diff(indices)) 
      }
      

      有数据:我想在 w==b 时保留值

      df <- data.frame(w = c("a","b","c","a","b","c"), x = 1:6, y = c(1,1,2,2,2,3), stringsAsFactors = FALSE)
      df$z<-retain(df$x-df$y,df$w=="b")
      df
      

      这是相反的,SAS 中不存在:

      obtain<-function(x,event,outside=NA)
      {
        indices <- c(0,which(event==TRUE), nrow(df))
        values <- c(x[event==TRUE],outside)
        y<- rep(values, diff(indices)) 
      }
      

      这是一个例子。我想提前获取 w==b

      的值
      df$z2<-obtain(df$x-df$y,df$w=="b")
      df
      

      感谢朱利安的帮助。

      【讨论】:

        【解决方案4】:

        这是一个例子:使用 sqldf 累积值:

        > w_cum <-
         sqldf("select t1.id, t1.SomeNumt, SUM(t2.SomeNumt) as cum_sum
               from w_cum       t1
               inner join w_cum t2 on t1.id >= t2.id
               group by t1.id, t1.SomeNumt
               order by t1.id
        

        ")

           id SomeNumt cum_sum
        
        • 1 11 11
        • 2 12 23
        • 3 13 36
        • 4 14 50
        • 5 15 65
        • 6 16 81
        • 7 17 98
        • 8 18 116
        • 9 19 135
        • 10 20 155

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-12
          • 2015-12-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多