【问题标题】:Vectorizing lagged operations向量化滞后操作
【发布时间】:2021-08-26 18:46:53
【问题描述】:

如何在 R 中对涉及使用 Z 的滞后值递归修改列 Z 的以下操作进行矢量化?

library(dplyr)
set.seed(5)

initial_Z=1000

df <- data.frame(X=round(100*runif(10),0), Y=round(100*runif(10),0))
df
    X  Y
1  20 27
2  69 49
3  92 32
4  28 56
5  10 26
6  70 20
7  53 39
8  81 89
9  96 55
10 11 84

df <- df %>%  mutate(Z=if_else(row_number()==1, initial_Z-Y, NA_real_)) 
df
    X  Y   Z
1  20 27 973
2  69 49  NA
3  92 32  NA
4  28 56  NA
5  10 26  NA
6  70 20  NA
7  53 39  NA
8  81 89  NA
9  96 55  NA
10 11 84  NA

for (i in 2:nrow(df)) {
  
  df$Z[i] <- (df$Z[i-1]*df$X[i-1]/df$X[i])-df$Y[i]
  
}
df
    X  Y           Z
1  20 27  973.000000
2  69 49  233.028986
3  92 32  142.771739
4  28 56  413.107143
5  10 26 1130.700000
6  70 20  141.528571
7  53 39  147.924528
8  81 89    7.790123
9  96 55  -48.427083
10 11 84 -506.636364

因此首先设置 Z 的第一个值,基于 initial_Z 和 Y 的第一个值。Z 的其余值使用 X 和 Z 的滞后值以及 Y 的当前值计算。

我的实际df很大,我需要在模拟中重复这个操作数千次。使用 for 循环需要太多时间。我更喜欢使用 dplyr 来实现这一点,但也欢迎使用其他方法。

非常感谢您的任何帮助。

【问题讨论】:

    标签: r dataframe for-loop dplyr vectorization


    【解决方案1】:

    我不知道你可以避免for 循环的影响,但总的来说R 应该很擅长它们。鉴于此,这里有一个 Reduce 变体可能对您来说就足够了:

    set.seed(5)
    initial_Z=1000
    df <- data.frame(X=round(100*runif(10),0), Y=round(100*runif(10),0))
    
    df$Z <- with(df, Reduce(function(prevZ, i) {
      if (i == 1) return(prevZ - Y[i])
      prevZ*X[i-1]/X[i] - Y[i]
    }, seq_len(nrow(df)), init = initial_Z, accumulate = TRUE))[-1]
    df
    #     X  Y           Z
    # 1  20 27  973.000000
    # 2  69 49  233.028986
    # 3  92 32  142.771739
    # 4  28 56  413.107143
    # 5  10 26 1130.700000
    # 6  70 20  141.528571
    # 7  53 39  147.924528
    # 8  81 89    7.790123
    # 9  96 55  -48.427083
    # 10 11 84 -506.636364
    

    需要明确的是,Reduce 在内部使用 for 循环来获取数据。我一般不喜欢使用 indices 作为Reducex 的值,但由于Reduce 只迭代一个值,我们需要XY ,索引(行)是必需的步骤。

    【讨论】:

      【解决方案2】:

      同样可以使用accumulate2 来完成。请注意,这些只是 for 循环。您应该考虑在 Rcpp 中编写 for 循环,如果它在 R

      中引起问题的话
      df %>%
        mutate(Z = accumulate2(Y, c(1, head(X, -1)/X[-1]), ~ ..1 * ..3 -..2, .init = 1000)[-1])
      
          X  Y         Z
      1  20 27       973
      2  69 49   233.029
      3  92 32  142.7717
      4  28 56  413.1071
      5  10 26    1130.7
      6  70 20  141.5286
      7  53 39  147.9245
      8  81 89  7.790123
      9  96 55 -48.42708
      10 11 84 -506.6364
      

      你可以unlist(Z):

      df %>%
         mutate(Z = unlist(accumulate2(Y, c(1, head(X, -1)/X[-1]), ~ ..1 * ..3 -..2, .init = 1000))[-1])
      

      【讨论】:

        猜你喜欢
        • 2021-05-31
        • 2014-06-11
        • 2011-11-04
        • 1970-01-01
        • 1970-01-01
        • 2022-11-05
        • 2015-07-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多