【问题标题】:Change row values to zero if less than row standard deviation如果小于行标准差,则将行值更改为零
【发布时间】:2013-04-16 20:50:04
【问题描述】:

如果一行的所有值小于该行的标准差,我想将它们更改为零。

set.seed(007)
X <- data.frame(matrix(sample(c(5:50), 100, replace=TRUE), ncol=10))

   X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
1  37 10 43 45 11 17 39 13 13  44
2  10 24 32 16  7 50 41 47  9  39
3  23 49 46 35 16 30 22 10 11  46
4  41 46 19 28 47 39 27 40 49  13
5  29 23 49 10 50 17 42 43  7  31
6  31 26 11 36 35 43 45 29 33   9
7  21 12  5 21 29 12 31 30  7  30
8  32 24  8 43  9 17 35 44 41   8
9  20 44 39  8 40 17 27 45 14  37
10 50  8  5 48 27 15 15 12 30  15

以下几行似乎可以完成这项工作,但在我的实际用例中速度非常慢,而且我有点不确定 sapply 返回的是什么......

Y <- t(sapply(1:nrow(X), function(i) 
      sapply(1:ncol(X), function(j) 
        ifelse(X[i,][[j]] < sd(X[i,]), 0, X[i,][[j]]))))

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]   37    0   43   45    0   17   39    0    0    44
 [2,]    0   24   32    0    0   50   41   47    0    39
 [3,]   23   49   46   35   16   30   22    0    0    46
 [4,]   41   46   19   28   47   39   27   40   49    13
 [5,]   29   23   49    0   50   17   42   43    0    31
 [6,]   31   26    0   36   35   43   45   29   33     0
 [7,]   21   12    0   21   29   12   31   30    0    30
 [8,]   32   24    0   43    0   17   35   44   41     0
 [9,]   20   44   39    0   40   17   27   45   14    37
[10,]   50    0    0   48   27    0    0    0   30     0

什么是更快更有效的方法?

更新非常感谢大家快速有效的回答!

这是它们的叠加方式...

set.seed(007)
size <- 1e5
X <- matrix(sample(c(5:50), size, replace=TRUE), ncol=size/2)

library(microbenchmark)    
results <- microbenchmark(
  X[ sweep(X, 1, apply(X,1,sd) ) < 0 ] <- 0,
  X[t(apply(X, 1, function(x) x - sd(x) < 0))] <- 0,
  sapply(X, function(x) ifelse(x < sd(x), 0, x)),
  times = 100L)
print(results)
Unit: milliseconds
                                              expr         min          lq     median         uq        max neval
          X[sweep(X, 1, apply(X, 1, sd)) < 0] <- 0    7.966167   10.869785   12.38399   15.00107   45.41557   100
 X[t(apply(X, 1, function(x) x - sd(x) < 0))] <- 0    7.344227    9.675577   11.22283   14.34280   53.70728   100
    sapply(X, function(x) ifelse(x < sd(x), 0, x)) 3028.336236 3221.325598 3302.16115 3466.66875 4539.88358   100
# plot
if (require("ggplot2")) {
  plt <- ggplot2::qplot(y=time, data=results, colour=expr)
  plt <- plt + ggplot2::scale_y_log10()
  print(plt)
}

看起来 Arun 的答案是最快的(正如 Arun 指出的那样)。但是,DWin 的输入减少了 8 个字符,并且以使用奇异的(对我而言)sweep 函数而著称。

一个小的娱乐更新,Arun 的方法明显更快(t = 2.0112,df = 191.985,p 值 = 0.04571),或者,如果您愿意,Arun 函数的平均速度比 DWin 的平均速度快得多(使用this robust Bayesian estimation method,第 1 组 = DWin,第 2 组 = Arun,虽然 Arun 的计时不适合 t-dist):

【问题讨论】:

标签: r rows standard-deviation


【解决方案1】:

这个怎么样?

X[t(apply(X, 1, function(x) x - sd(x) < 0))] <- 0
#    X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
# 1  50  0 34 36 41 31  0 18 45  20
# 2  23 15 18 17 22 38 28 32 45   0
# 3   0 40 50  0 39 40 40 43 16  46
# 4   0  0 46  0 25 33 36 33 39   0
# 5  16 25 50 22 46 38 30  0 22  38
# 6  41  0  0 43 19 22 35 31  0  31
# 7  20 30 33 27  0 12 26 25  0  29
# 8  49  0 27 41 42  0 27 25 40  21
# 9   0 50 49 43 46 22 20 33 21  42
# 10 26 19 21 26 49 17 24 47 24  13

【讨论】:

    【解决方案2】:

    我怀疑这比 apply 解决方案要慢,但由于不需要添加 data.frame 步骤以及 apply.data.frame 非常慢的事实,我可能仍然“获胜”或“保持平衡”至少在其他参赛者发现我使用矩阵对象之前。

    set.seed(007)
    X <- matrix(sample(c(5:50), 100, replace=TRUE), ncol=10)
    X[ sweep(X, 1, apply(X,1,sd) ) < 0 ] <- 0
    

    请注意,我和 Richardo 的起点与 OP 相同,尽管我认为如果他想要行操作,他需要转置:

    > X
       X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
    1  50  0 34 36 41 31  0 18 45  20
    2  23 15 18 17 22 38 28 32 45   0
    3   0 40 50  0 39 40 40 43 16  46
    4   0  0 46  0 25 33 36 33 39   0
    5  16 25 50 22 46 38 30  0 22  38
    6  41  0  0 43 19 22 35 31  0  31
    7  20 30 33 27  0 12 26 25  0  29
    8  49  0 27 41 42  0 27 25 40  21
    9   0 50 49 43 46 22 20 33 21  42
    10 26 19 21 26 49 17 24 47 24  13
    

    补充说明:我正在使用 rowMeans 函数,看看是否可以提出一个矢量化替代方案来代替 apply(X,1,sd) 版本的 sd():

    sqrt(rowSums((X[1:10, ]-rowMeans(X))^2)/9)
    

    所以:

     sdbyrow <- function(mat) sqrt(rowSums((mat-rowMeans(mat))^2)/(ncol(mat)-1) )
     all.equal(apply(X,1,sd), sdbyrow(X) )
    #[1] TRUE
    

    【讨论】:

    • 带有转置 (t) 的 apply 版本在我对 1e5 x 1e3 data.frame 的基准测试中似乎比 sweep 有点优势。 (2.37 对 2.72 秒)
    • 如果允许我进一步澄清,如果我只想保留小于该行最小值一个标准差的行值,这是否正确:XX[ sweep(XX, 1, (apply(XX,1,min) + apply(XX,1,sd)) ) &lt; 0 ] &lt;- 0 ?
    • 对我来说看起来是正确的。在其中一个贡献的软件包中还有一个 rowMin 可能与 apply(X,1,min) 甚至可能是 rowSD 相同,尽管我还没有检查过这种可能性。
    • 太好了,谢谢。我已经为你最近的一些其他答案投票以表示感谢,因为投票 cmets 毫无价值(无论如何都这样做了)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 2020-07-28
    • 2023-02-22
    • 1970-01-01
    相关资源
    最近更新 更多