【问题标题】:Picking top n% percent of elements from matrix rows, different number of elements on each row从矩阵行中选择前 n% 的元素,每行的元素数量不同
【发布时间】:2018-12-13 21:47:57
【问题描述】:

我在选择前 n% 的最大和最小元素时遇到问题 从每个数据矩阵行。具体来说,我想找到那些前 n% 元素的列号。如果每行具有相同数量的非 NA 元素,这将不是问题,但在这种情况下,每行选取的元素数量不同。这是一个例子(实际数据矩阵是 195x1030,所以我不会在这里使用它),其中前 40% 被选中

data=     
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1   NA   100  98   200  78   80   35   NA    55
[2,]   32   67   15   73   NA   12   91   230  3     99
[3,]   NA   NA   NA   45   53   26   112  64   80    41
[4,]   54   38   60   70   163  69   109  205  5     31
[5,]   107  28   296  254  30   40   NA   18   28    90

生成的前 40% 列数矩阵应如下所示(拾取元素的数量是通过向下舍入计算的,就像函数 as.integer 所做的那样)

largest=                              smallest=
      [,1] [,2] [,3] [,4]                   [,1] [,2] [,3] [,4]  
[1,]    5   3    4    NA              [1,]    1   8    10   NA
[2,]    8   10   7    NA              [2,]    9   6    3    NA
[3,]    7   9    NA   NA              [3,]    6   10   NA   NA
[4,]    8   5    7    4               [4,]    9   10   2    1
[5,]    3   4    1    10              [5,]    8   9    2    5

因此,仅查看行的非 NA 元素来选择顶部数字。例如,数据矩阵的第一行仅包含 8 个非 NA 数,因此选择了 40%*8=3,2~3 个元素。这会为结果矩阵创建 NA。

我再次尝试使用 for 循环(这段代码是为了找到最大的 40%):

   largest <- matrix(rep(NA, 20), nrow = 5)
 for(i in 1:5){
   largest[i,]<-order(data[i,], decreasing=T)   
 [1:as.integer(0.4*nrow(data[complete.cases(data[,i]),]))]
 }

但是R返回一个错误:“要替换的项目数不是替换长度的倍数”,我认为这意味着由于不是原始最大矩阵的所有元素在循环时都没有被替换,所以这个for循环不能使用。我说的对吗?

这种拣货怎么做?

【问题讨论】:

  • 我(还)不明白您的预期输出。在largest 中,为什么元素是[1,4] = NA 而不是7?第 7 列包含data 第 1 行中的第四大数字。对于元素 [2,4] 类似,对于其他 NA 条目以此类推。
  • 这是因为我只想选择前 40% 的非 NA 元素。例如对于包含 8 个非 NA 元素的第一行,选取的元素数为 0,4*8=3,2。这四舍五入为 3。因此,最大矩阵的第一行应该只包含 3 个数字,而不是 4。我会指定问题,谢谢您的评论。
  • 您是否需要对顶部/底部 n% 进行排序,或者顶部/底部 n% 的位置是否足够?如果是这样,我会准备好解决方案。
  • 没想到,其实我不需要排序!只是位置就足够了。
  • 太慢了 :) @MauritsEvers 帮你搞定了。

标签: r loops matrix picking


【解决方案1】:

以下重现您的预期输出

# Determine number of columns for output matrix as
# maximum of 40% of all non-NA values per row
ncol <- max(floor(apply(mat, 1, function(x) sum(!is.na(x))) * 0.4))

# Top 40% largest
t(apply(mat, 1, function(x) {
    n <- floor(sum(!is.na(x)) * 0.4);
    replace(rep(NA, ncol), 1:n, order(x, decreasing = T)[1:n])
}))
#     [,1] [,2] [,3] [,4]
#[1,]    5    3    4   NA
#[2,]    8   10    7   NA
#[3,]    7    9   NA   NA
#[4,]    8    5    7    4
#[5,]    3    4    1   NA


# Top 40% smallest
t(apply(mat, 1, function(x) {
    n <- floor(sum(!is.na(x)) * 0.4);
    replace(rep(NA, ncol), 1:n, order(x, decreasing = F)[1:n])
}))
#     [,1] [,2] [,3] [,4]
#[1,]    1    8   10   NA
#[2,]    9    6    3   NA
#[3,]    6   10   NA   NA
#[4,]    9   10    2    1
#[5,]    8    2    9   NA

解释:我们首先确定两个输出矩阵的最大列数;然后我们逐行遍历mat,确定对应于该行中所有非NA数字的40%的非NA条目的特定行数n,并返回一列@前 40% 的减少/增加条目中的 987654326@ 填充有 NAs。最终转置给出了预期的输出。

【讨论】:

  • 非常感谢,这部作品真有魅力!我真的很感谢你的帮助。
【解决方案2】:

以函数的形式发布我的(不太准确且非常相似的)答案,这可能很方便:

toppct <- function(x, p, largest = TRUE){
  t(apply(x, 1, function(y){
    c(which(y %in% sort(y, decreasing = largest)[1:floor(length(which(!is.na(y)))*p)]), 
      rep(NA, floor(length(y)*p) - floor(length(which(!is.na(y)))*p)))
  }))
}

这会产生问题中的输出,只是没有对最高百分比位置进行排序。对于smallest,只需设置largest = FALSE

> toppct(mat, .4)
     [,1] [,2] [,3] [,4]
[1,]    3    4    5   NA
[2,]    7    8   10   NA
[3,]    7    9   NA   NA
[4,]    4    5    7    8
[5,]    1    3    4   NA

> toppct(mat, .4, largest = FALSE)
     [,1] [,2] [,3] [,4]
[1,]    1    8   10   NA
[2,]    3    6    9   NA
[3,]    6   10   NA   NA
[4,]    1    2    9   10
[5,]    2    8    9   NA

我想强调的是,我认为 Maurits 的回答是可以接受的,因为他得到的输出完全符合预期。

【讨论】:

  • 非常感谢这个!试过了,真的很方便。
  • 喜欢函数式方法。非常整洁。 +1
猜你喜欢
  • 1970-01-01
  • 2015-10-23
  • 2013-12-13
  • 1970-01-01
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多