【问题标题】:dplyr to store mutliple variables from a vectordplyr 存储向量中的多个变量
【发布时间】:2018-10-05 12:46:43
【问题描述】:

我正在对 4x4 方差-协方差矩阵的后验分布执行特征分解。为此,我在 dplyr/tidyverse 管道中使用 eigen 函数:

set.seed(1)
# Variance and covariances of 4 variables
A1  <- rnorm(1000,10,1)
A2  <- rnorm(1000,10,1)
A3  <- rnorm(1000,10,1)
A4  <- rnorm(1000,10,1)
C12 <- rnorm(1000,0,1)
C13 <- rnorm(1000,0,1)
C14 <- rnorm(1000,0,1)
C23 <- rnorm(1000,0,1)
C24 <- rnorm(1000,0,1)
C34 <- rnorm(1000,0,1)

# Create posterior tibble
w1_post <- as_tibble(cbind(A1, C12, C13, C14, A2, C23, C24, A3, C34, A4))

# Get 1st-4th eigenvalues of each variance-covariance matrix
w1_post %>%
  rowwise %>%
    mutate(
      eig1 = 
        eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
          A3, C34, C14, C24, C34, A4), nrow = 4))[[1]][1],
      eig2 = 
        eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
          A3, C34, C14, C24, C34, A4), nrow = 4))[[1]][2],
      eig3 = 
        eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
          A3, C34, C14, C24, C34, A4), nrow = 4))[[1]][3],
      eig4 = 
        eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
          A3, C34, C14, C24, C34, A4), nrow = 4))[[1]][4]) %>%
  select(starts_with('eig')) -> eig_post

制作

> eig_post
Source: local data frame [1,000 x 4]
Groups: <by row>

# A tibble: 1,000 x 4
    eig1  eig2  eig3  eig4
   <dbl> <dbl> <dbl> <dbl>
 1  12.3 11.0  10.4   6.67
 2  12.8 10.1   9.19  7.61
 3  13.5 12.2   8.20  7.34
 4  12.7 12.2   8.91  7.68
 5  12.9  9.70  9.41  6.74
 6  12.2 10.6   8.62  7.70
 7  13.1 12.5   9.21  8.34
 8  12.9  9.76  7.87  6.96
 9  12.8 11.6   8.21  6.46
10  12.5 11.6   9.85  8.13
# ... with 990 more rows

如您所见,这是每行执行四次特征分解 - 这比实际需要的要多 4 倍,并且减慢了我的脚本! 我可以让一个 dplyr/tidyverse 管道同时改变多个变量,将eigen(*matrix*)[[1]][1:4] 生成的向量分散到四个变量中吗? 所以我需要得到上面代码产生的结果,但只做一个每行的特征分解。我认为这样的事情会奏效,但没有运气:

w1_post %>%
  rowwise %>%
    mutate(c(eig1, eig2, eig3, eig4) = 
      eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
        A3, C34, C14, C24, C34, A4), nrow = 4))[[1]][1:4]) %>%
  select(starts_with('eig')) -> eig_post

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    您可以通过首先将计算存储为列表列,然后在后续步骤中提取值来避免计算特征分解 4 次。如果您想将其保留在您的管道中,您可以这样做:

    eig_post <- w1_post %>%
      rowwise %>%
      mutate(
        pre_eig = list(eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
                         A3, C34, C14, C24, C34, A4), nrow = 4)))
      ) %>%
      mutate( 
        eig1 = pre_eig[[1]][1], 
        eig2 = pre_eig[[1]][2], 
        eig3 = pre_eig[[1]][3], 
        eig4 = pre_eig[[1]][4]) %>%
      select(starts_with("eig"))
    

    【讨论】:

    • 谢谢 - 像魅力一样工作;作为旁注,我将其更改为在同一变异中执行所有变异:mutate(pre_eig = list(eigen(matrix(c(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23, A3, C34, C14, C24, C34, A4), nrow = 4))), eig1 = pre_eig[[1]][1], eig2 = pre_eig[[1]][2], eig3 = pre_eig[[1]][3], eig4 = pre_eig[[1]][4])
    • 当然也可以,但为了清楚起见,我将其拆分为答案。一项快速的微基准测试表明,这种方法至少比原来的方法快 3 倍
    【解决方案2】:

    这是一个利用purrr::map 系列函数的解决方案:

    eig_post <- w1_post %>%
    
        ## Collapse columns into a vector
        transmute( x = pmap( list(A1, C12, C13, C14, C12, A2, C23, C24, C13, C23,
                                  A3, C34, C14, C24, C34, A4), c ) ) %>%
    
        ## Compose the 4x4 matrices from each vector
        mutate( mtx = map( x, matrix, nrow=4 ) ) %>%
    
        ## Perform a single decomposition and retrieve all 4 eigenvalues
        mutate( eig = map( mtx, ~eigen(.x)$values ) ) %>%
    
        ## Annotate the vector of eigenvalues with the desired names
        mutate( eig = map( eig, set_names, str_c("eig", 1:4) ) ) %>%
    
        ## Reshape the data frame by effectively unnesting the vector
        with( invoke( bind_rows, eig ) )
    

    【讨论】:

    • 这似乎比我的方法略快(大约快 25 %)
    猜你喜欢
    • 2013-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 2018-08-30
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多