3

我有一个包含 n 行观察的矩阵。观测值是特征的频率分布。我想将频率分布转换为每行之和为 1 的概率分布。因此,矩阵中的每个元素都应除以该元素的行之和。

我编写了以下 R 函数来完成这项工作,但它对于大型矩阵非常慢:

prob_dist <- function(x) {

    row_prob_dist <- function(row) {
       return (t(lapply(row, function(x,y=sum(row)) x/y)))
       }

    for (i in 1:nrow(x)) {
       if (i==1) p_dist <- row_prob_dist(x[i,])
       else p_dist <- rbind(p_dist, row_prob_dist(x[i,]))
       }
    return(p_dist)
}

B = matrix(c(2, 4, 3, 1, 5, 7), nrow=3, ncol=2)
B
     [,1] [,2]
[1,]    2    1
[2,]    4    5
[3,]    3    7

prob_dist(B)
     [,1]      [,2]    
[1,] 0.6666667 0.3333333
[2,] 0.4444444 0.5555556
[3,] 0.3       0.7     

您能否建议完成这项工作的 R 函数和/或告诉我如何优化我的函数以更快地执行?

4

4 回答 4

5

这是一个尝试,但在数据框而不是矩阵上:

df <- data.frame(replicate(100,sample(1:10, 10e4, rep=TRUE)))

我尝试了一种dplyr方法:

library(dplyr)
df %>% mutate(rs = rowSums(.)) %>% mutate_each(funs(. / rs), -rs) %>% select(-rs)

结果如下:

library(microbenchmark) 
mbm = microbenchmark(
dplyr = df %>% mutate(rs = rowSums(.)) %>% mutate_each(funs(. / rs), -rs) %>% select(-rs),
t = t(t(df) / rep(rowSums(df), each=ncol(df))),
apply = t(apply(df, 1, prop.table)),
times = 100
)

在此处输入图像描述

#> mbm
#Unit: milliseconds
#  expr       min        lq      mean    median        uq       max neval
# dplyr  123.1894  124.1664  137.7076  127.3376  131.1523  445.8857   100
#     t  384.6002  390.2353  415.6141  394.8121  408.6669  787.2694   100
# apply 1425.0576 1520.7925 1646.0082 1599.1109 1734.3689 2196.5003   100

编辑:@David benchmark 更符合 OP,所以如果你要使用矩阵,我建议你考虑他的方法。

于 2015-02-01T23:50:01.110 回答
4

没有应用,一行中的矢量化解决方案:

t(t(B) / rep(rowSums(B), each=ncol(B)))
          [,1]      [,2]
[1,] 0.6666667 0.3333333
[2,] 0.4444444 0.5555556
[3,] 0.3000000 0.7000000

或者:

diag(1/rowSums(B)) %*% B
于 2015-02-01T21:21:43.620 回答
2

实际上,我快速考虑了一下,最好的 vecotization 就是

B/rowSums(B)
#           [,1]      [,2]
# [1,] 0.6666667 0.3333333
# [2,] 0.4444444 0.5555556
# [3,] 0.3000000 0.7000000

实际上,@Stevens 基准测试具有误导性,因为 OP 有一个矩阵,而 Steven 基准测试是在数据框上。

这是一个带有矩阵的基准。dplyr因此,对于矩阵,两种矢量化解决方案都比不适用于矩阵的解决方案要好

set.seed(123)
m <- matrix(sample(1e6), ncol = 100)

library(dplyr)
library(microbenchmark) 

Res <- microbenchmark(
  dplyr = as.data.frame(m) %>% mutate(rs = rowSums(.)) %>% mutate_each(funs(. / rs), -rs) %>% select(-rs),
  t = t(t(m) / rep(rowSums(m), each=ncol(m))),
  apply = t(apply(m, 1, prop.table)),
  DA = m/rowSums(m),
  times = 100
)

在此处输入图像描述

于 2015-02-03T07:56:34.440 回答
0

我不确定您的函数是否有任何价值,因为您可以使用histordensity函数来完成相同的结果。此外,使用apply将按上述方式工作。但它可以作为一个合理的编程示例。

您的代码中有几个效率低下的地方。

  • 您使用 for 循环而不是矢量化您的代码。这是非常昂贵的。您应该使用上述评论中提到的 apply 。
  • 您正在使用rbind而不是为输出预先分配空间。这也是极其昂贵的。

    out <- matrix(NA, nrow= n, ncol= ncol(B))
    for (i in 1:nrow(B)) {
      out[i,] <- row_prob_dist(B[i,])
    }
    
于 2015-02-01T21:30:33.920 回答