【问题标题】:how to generate a vector or factor from a numeric matrix based on logic (no for loop)如何根据逻辑从数字矩阵生成向量或因子(无 for 循环)
【发布时间】:2013-10-22 17:58:38
【问题描述】:

我有一个 30,000 行和 3 列的数字矩阵。我想根据矩阵每一行中的 3 个值生成一个简单的 PASS/FAIL 向量(或因子)。我想应用以下逻辑:

如果行中的所有 3 个值都 > 3,则输入 PASS,否则输入 FAIL。

我知道如何使用 for 循环来做到这一点,但我怎样才能更快地做到这一点?我有几十个这样的矩阵...谢谢!

as.matrix(rbind(c(129,129,120),c(135,97,96),c(0,0,0),c(39,4,2)))

期望的输出: 通过,通过,失败,失败

【问题讨论】:

  • 我要感谢所有在这里回答的人!很抱歉,我对 PASS/FAIL 没有更准确 - 我知道编写一些解决方案只是为了使输出符合该要求。感谢您花时间回答,我学习了向量化函数,逻辑向量,ifelse和apply。继续使用 stackexchange 总是一个好主意。谢谢大家!

标签: r vector matrix


【解决方案1】:

使用allapply(尽管apply 使用它自己的循环)。

m <- as.matrix(rbind(c(129,129,120),c(135,97,96),c(0,0,0),c(39,4,2)))

apply(m, 1, function(x) all(x > 3))
# [1]  TRUE  TRUE FALSE FALSE

如果您真的想要“PASS”和“FAIL”,您可以factorapply 步骤的结果。

factor(apply(m, 1, function(x) all(x > 3)), 
       levels = c(FALSE, TRUE), 
       labels = c("FAIL", "PASS"))
# [1] PASS PASS FAIL FAIL
# Levels: FAIL PASS

稍微扩展 Codoremifa 的答案,类似的方法适用于 data.table,特别是因为您指定要使用向量或因子作为输出。

library(data.table)
DT <- data.table(m)
DT[, all(.SD > 3), by = 1:nrow(DT)][, factor(V1, labels = c("FAIL", "PASS"))]
# [1] PASS PASS FAIL FAIL
# Levels: FAIL PASS

【讨论】:

  • 我认为 data.table 结构非常简洁,但我设置了 500000 行 data.table 并且需要很长时间。是因为'by'吗?有什么解决方法吗?
  • @Codoremifa,不太确定。真正的长是多少? :)
  • &gt; dt &lt;- data.table( + V1 = round(rnorm(500000,0,1),1), + V2 = round(rnorm(500000,0,1),1), + V3 = round(rnorm(500000,0,1),1) + ) &gt; &gt; system.time(dt[V1 &gt; .7 &amp; V2 &gt; .7 &amp; V3 &gt; .7, Indicator :="PASS" ]) user system elapsed 0.14 0.00 0.14 &gt; system.time(dt[, all(.SD &gt; .7), by = 1:nrow(dt)][, factor(V1, labels = c("FAIL", "PASS"))]) user system elapsed 75.61 0.05 76.25
  • @Codoremifa,我猜这与逐行使用 .SD 有关。请参阅here,但我还没有想到解决方法。
【解决方案2】:

与此处的其他答案不同,它使用 rowSums 但这不是在 R 中循环,并且可以超过多个子集和逻辑。这应该是最快的路线了。

mat <- as.matrix(rbind(c(129,129,120),c(135,97,96),c(0,0,0),c(39,4,2)))

vec <- ifelse(rowSums(mat > 3) == 3, TRUE, FALSE)

我们也可以绕过ifelse,让它变得更快。

vec <- rowSums(mat > 3) == 3

如果你测试这些时间,那很可能是赢家。在我的系统上,使用 30,000 行矩阵,我的第一个答案的输出速度大约是 gung 答案的两倍,第二个答案的输出速度是 10 倍,并且可以在大约 2 秒内在 1000 个 30,000 行矩阵上执行。 Codoremifa 答案是此处基于data.table 的最快答案,需要 20 秒(类似于 gung 答案)。

注意:我有点忽略了您对“PASS”、“FAIL”向量的请求,因为您似乎表示速度是最重要的,而且这是一个微不足道的语义区别。此外,如果需要,逻辑向量已经准备好对矩阵进行子集化。

【讨论】:

  • 这正是我一直在寻找的,你是对的,PASS/FAIL 只是一个想法。不知道逻辑向量,非常有用!谢谢 - 我使用了 2 行解决方案!
  • @Stefan,我相信您只需一行:rowSums(m &gt; 3) == ncol(m)
  • 对...更新了答案,因为最初我用 PASS/FAIL 编写了一些东西并且没有完全转换但是...为什么要使用 ncol?
  • @John,只是作为一种自动化的方式(所以我们不必先计算原始矩阵中有多少列)。
【解决方案3】:
library(data.table)
dt <- as.matrix(rbind(c(129,129,120),c(135,97,96),c(0,0,0),c(39,4,2)))

dt <- data.table(dt)
dt[, Indicator :="FAIL"]
dt[V1 > 3 & V2 >3 & V3 >3, Indicator :="PASS" ]

【讨论】:

    【解决方案4】:

    另外,mapply:

    mat <- as.matrix(rbind(c(129,129,120),c(135,97,96),c(0,0,0),c(39,4,2)))
    
    fun <- function(x, y, z) { ifelse(x > 3 & y > 3 & z > 3, "PASS", "FAIL") } 
    mapply(fun, mat[,1], mat[,2], mat[,3])
    #[1] "PASS" "PASS" "FAIL" "FAIL"
    

    【讨论】:

      【解决方案5】:

      对于这样的问题,我的第一个倾向是结合?all?apply?ifelse,也许就像@Ananda提供的解决方案一样。正如他所提到的,apply() 正在使用循环。如果您想要一个完全矢量化的解决方案,您可以尝试:

      newVector <- ifelse((xMatrix[,1]>3 & xMatrix[,2]>3 & xMatrix[,3]>3), 
                          "PASS", "FAIL")
      

      向量化是 R 的一个方便的特性,它比循环快得多。您可以阅读有关矢量化的信息here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-03
        • 2014-11-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多