代码之家  ›  专栏  ›  技术社区  ›  Z117

R-将df或矩阵的每一行乘以一个向量

  •  2
  • Z117  · 技术社区  · 7 年前

    我没法做到这一点,尽管它看起来相当简单。 我想用向量a乘以矩阵(或数据帧或数据表)b中的每一行。

    a <- data.table(t(1:4))
    b <- matrix(data=2, nrow=3, ncol=4)
    

    所需输出(以矩阵、数据帧或数据表形式):

         [,1] [,2] [,3] [,4]
    [1,]    2    4    6    8  
    [2,]    2    4    6    8
    [3,]    2    4    6    8
    

    有人能帮我如何(有效地)做到这一点吗?

    5 回复  |  直到 7 年前
        1
  •  4
  •   moodymudskipper    7 年前
    b*rep(unlist(a),each=nrow(b))
    #      [,1] [,2] [,3] [,4]
    # [1,]    2    4    6    8
    # [2,]    2    4    6    8
    # [3,]    2    4    6    8
    

    或者只是 b*rep(a,each=nrow(b)) 如果您定义 a <- 1:4

    这只是一个矢量化的元素乘法,没有任何变换 rep

    编辑:

    似乎是rep拖慢了我的解决方案。这里是一个基准测试,我在其中包含了一个带有预计算rep的选项,以及对sweep选项的一些改进(仅从源代码中获取相关部分)。

    a <- data.table(t(1:200))
    b <- matrix(data=2, nrow=100000, ncol=200)
    
    a_vec <- unlist(a)
    rep_a <- rep(a_vec,each=nrow(b))
    microbenchmark::microbenchmark(
      mkr1 = a[,lapply(.SD,function(x)(x*b[,x]))],
      mkr2 = t(t(b) * (as.matrix(a)[1,])),
      mkr_update = a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))],
      mm = b*rep(unlist(a),each=nrow(b)),
      mm_cheat = b*rep_a,
      regular_sweep = sweep(b,2,unlist(a),`*`),
      regular_sweep2 = sweep(b,2,a_vec,`*`),
      improved_sweepA1 = b*aperm(array(unlist(a),rev(dim(b)))),
      improved_sweepA2 = b*aperm(array(a_vec,rev(dim(b)))),
      improved_sweepB1 = b*a[rep_len(1,nrow(b)),],
      improved_sweepB2 = b*t(a_vec)[rep_len(1,nrow(b)),],
      unit = "relative",
      times=50)
    
    
    Unit: relative
                 expr       min        lq      mean    median        uq       max neval
                 mkr1  42.12228  44.15266  50.23959  46.35240  57.20280  65.07289    50
                 mkr2 114.58427 124.19653 125.25660 131.08677 124.17058 114.91137    50
           mkr_update   1.00000   1.00000   1.00000   1.00000   1.00000   1.00000    50
                   mm 231.34331 223.74365 217.50145 225.91117 215.90765 165.64814    50
             mm_cheat  13.38838  13.22556  14.94682  13.36649  12.95260  25.15564    50
        regular_sweep  96.15758 124.26746 121.04428 128.67282 129.19407 119.20210    50
       regular_sweep2  97.79001 124.69191 124.74650 134.64249 134.97407 107.47152    50
     improved_sweepA1  96.57837 124.86189 116.93736 127.08909 124.92805 105.83318    50
     improved_sweepA2  96.27737 122.49773 118.45262 128.13369 126.15029 106.58669    50
     improved_sweepB1 214.95773 227.39523 226.04339 248.38553 232.50401 161.45341    50
     improved_sweepB2  31.20967  32.61873  37.74552  33.70969  41.52149  55.93362    50
    
        2
  •  3
  •   Seymour    7 年前

    在我这方面,我会使用内置的R方法进行矩阵乘法 %*%

    考虑到 矢量 :[注: data.table vector ]

    a <- c(1:4)
    

    考虑到矩阵:

    b <- matrix(data=2, nrow=3, ncol=4)
    

    您的输出由以下内容给出:

    output <- b %*% diag(a)
    
         [,1] [,2] [,3] [,4]
    [1,]    2    4    6    8
    [2,]    2    4    6    8
    [3,]    2    4    6    8
    

    如果您认为此解决方案对您的需求效率很低,那么我建议使用内置功能 sweep :

    sweep(b, 2, a, FUN = "*")
    
         [,1] [,2] [,3] [,4]
    [1,]    2    4    6    8
    [2,]    2    4    6    8
    [3,]    2    4    6    8
    
        3
  •  2
  •   MKR    7 年前

    选项#1: 使用 data.table 特点:

    注意:它之所以有效,是因为列编号和值与 a

    a[,lapply(.SD,function(x)(x*b[,x]))]
    #   V1 V2 V3 V4
    #1:  2  4  6  8
    #2:  2  4  6  8
    #3:  2  4  6  8
    

    选项2: 可能是:

    t(t(b) * (as.matrix(a)[1,]))
         [,1] [,2] [,3] [,4]
    [1,]    2    4    6    8
    [2,]    2    4    6    8
    [3,]    2    4    6    8
    

    更新

    选项3: 在中处理十进制/实际值的步骤

    #Cases when `a` contains decimal values can be handled as
    a <- data.table(t(c(1, 0.24, 3, 4)))
    b <- matrix(data=2, nrow=3, ncol=4)
    
    a[,lapply(V1:V4,function(i)(a[[i]]*b[,i]))]
    #   V1   V2 V3 V4
    #1:  2 0.48  6  8
    #2:  2 0.48  6  8
    #3:  2 0.48  6  8
    
        4
  •  2
  •   Z117    7 年前

    感谢您的回复。我已经在速度(向量和矩阵的实际大小)上测试了上述建议的解决方案,以使用最有效的解决方案:

    a <- data.table(t(1:200))
    b <- matrix(data=2, nrow=100000, ncol=200)
    
    system.time(sweep(b, MARGIN=2, t(a), "*"))
    #   user  system elapsed 
    #   0.31    0.06    0.39 
    
    system.time(a[,lapply(.SD,function(x)(x*b[,x]))])
    #   user  system elapsed 
    #    0.2     0.0     0.2 
    
    #system.time(bind_rows(apply(b,1,`*`,a)))     
    #took 100+ so stopped it manually
    
    system.time(t(t(b)*(as.matrix(a)[1,])))
    #   user  system elapsed 
    #   0.31    0.05    0.36 
    
    system.time(apply(b, 1, `*`, 1:200))
    #   user  system elapsed 
    #   1.20    0.11    1.31 
    
    system.time(b*rep(unlist(a),each=nrow(b)))
    #   user  system elapsed 
    #   0.83    0.05    0.89 
    
    system.time(b*rep((1:200),each=nrow(b)))
    #   user  system elapsed 
    #   0.36    0.06    0.42
    
        5
  •  1
  •   moodymudskipper    7 年前
    dplyr::bind_rows(apply(b, 1, `*`, a))
       V1 V2 V3 V4
    1:  2  4  6  8
    2:  2  4  6  8
    3:  2  4  6  8
    

    棘手的是你的 a 是一个数据。桌子如果它实际上是一个向量,那么它要简单得多:

    apply(b, 1, `*`, 1:4)
         [,1] [,2] [,3]
    [1,]    2    2    2
    [2,]    4    4    4
    [3,]    6    6    6
    [4,]    8    8    8