【问题标题】:how to transform multiple binary into categorical如何将多个二进制转换为分类
【发布时间】:2020-07-15 03:00:44
【问题描述】:

我想根据二进制列的行名将多个二进制列转换为具有分类内容的一列。我曾尝试在其他questions 中使用此论坛中的代码,但由于我的情况不同,有些行没有条目,我没有设法找到有效的解决方案。 dplyr 提出的解决方案在我的数据集上速度很慢。我的数据集有超过 2 个 Mio 行。

这是数据

m <- matrix(0,10,10)
colnames(m) <- c("a","b","c","d","e","f","g","h","i","j")
m[3,2] <- 1
m[4,8] <- 1
m[5,8] <- 1
m[6,1] <- 1

看起来像这样

      a b c d e f g h i j
 [1,] 0 0 0 0 0 0 0 0 0 0
 [2,] 0 0 0 0 0 0 0 0 0 0
 [3,] 0 1 0 0 0 0 0 0 0 0
 [4,] 0 0 0 0 0 0 0 1 0 0
 [5,] 0 0 0 0 0 0 0 1 0 0
 [6,] 1 0 0 0 0 0 0 0 0 0
 [7,] 0 0 0 1 0 0 0 0 0 0
 [8,] 0 0 0 0 0 0 0 0 0 0
 [9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0

我想买

      colname 
 [1,] "" 
 [2,] "" 
 [3,] "b"
 [4,] "h" 
 [5,] "h" 
 [6,] "a" 
 [7,] "d" 
 [8,] "" 
 [9,] "" 
[10,] "" 

【问题讨论】:

    标签: r vector dplyr


    【解决方案1】:

    这应该很快(借用 Ronak 的提示并使用 ties.method = "first"

    mc = max.col(m, ties.method = "first")
    result = ifelse(m[cbind(1:nrow(m), mc)] == 0, "", colnames(m)[mc])
    result
    # [1] ""  ""  "b" "h" "h" "a" ""  ""  ""  "" 
    

    一般来说,矩阵比数据帧快很多。与全矩阵运算相比,将矩阵转换为数据框以使用 dplyr 会很慢。

    这是另一种可能性:

    nm = colnames(m)
    apply(m, 1, function(x) if (any(x == 1)) nm[which.max(x)] else "")
    

    max.col 解决方案非常快,尤其是 Ronak 的解决方案,在 2M x 325 矩阵上占用我的笔记本电脑不到 5 秒:

    ## Generate data
    nm = combn(letters, 2, FUN = paste, collapse = "")
    nr = 2e6
    nc = length(nm)
    
    m = matrix(0, nrow = nr, ncol = nc)
    n_ones = 1.5e6
    ones = cbind(sample(1:nr, size = n_ones), sample(1:nc, size = n_ones, replace = TRUE))
    m[ones] = 1
    
    system.time(
      {r1 = apply(m, 1, function(x) if (any(x == 1)) nm[which.max(x)] else "")}
    )
    #  user  system elapsed 
    # 13.94    3.87   19.50 
    
    system.time({
      mc = max.col(m, ties.method = "first")
      r2 = ifelse(m[cbind(1:nrow(m), mc)] == 0, "", nm[mc])
    })
    # user  system elapsed 
    # 3.56    0.01    3.72 
    
    # Ronak's solution
    system.time({
      cols <- max.col(m, ties.method = "first")
      vec <- colnames(m)[cols]
      vec[cols == 1 & m[, 1] != 1] <- ''
    })
    # user  system elapsed 
    # 3.16    0.00    3.31 
    

    max.col 解决方案比apply很多,而且 Ronak 的优化使其非常好。

    【讨论】:

      【解决方案2】:

      使用max.col 的另一种方法是获取指定ties.method = "first" 的每一行中最大元素的索引并获取相应的列名。然后我们可以将列名替换为空白,它返回 max.col 值为 1(第一列)但在 m 的第一列中没有 1。

      cols <- max.col(m, ties.method = "first")
      vec <- colnames(m)[cols]
      vec[cols == 1 & m[, 1] != 1] <- ''
      #[1] ""  ""  "b" "h" "h" "a" ""  ""  ""  "" 
      

      【讨论】:

      • 很好,只看第一列可能比我更复杂的索引要快一点。并且避免ifelse也不错。
      • 好吧,我真的很震惊 - 大幅加速归功于 ties.method = "first",这使得昂贵的 max.col 操作快了大约 5 倍。
      • 也许ties.method = "first" 必须跳过一些计算以使其更快。
      猜你喜欢
      • 2020-10-04
      • 2018-03-17
      • 1970-01-01
      • 1970-01-01
      • 2015-05-27
      • 2017-09-08
      • 2016-09-03
      • 2020-02-03
      • 1970-01-01
      相关资源
      最近更新 更多