代码之家  ›  专栏  ›  技术社区  ›  DSA

基于列名中的匹配值和R中的特定列创建0-1数据帧

  •  4
  • DSA  · 技术社区  · 7 年前

    > mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                       C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    > mat.data
     A B C D cat
     1 0 0 0   A
     1 1 0 0   A
     0 1 0 0   C
     0 0 0 1   B 
    

    match(mat.data[,5],colnames(mat.data[1:4]))

    我希望根据数据的列名和第5列之间的真正匹配来重新填充0-1值(因此,当第5列是给定行的a时,我希望在名为“a”的列下为“1”,其他列为“0”)。

    为了更好的解释,期望的输出是:

    > mat.data
     A B C D cat
     1 0 0 0   A
     1 0 0 0   A
     0 0 1 0   C
     0 1 0 0   B 
    

    任何建议,使它干净,不那么复杂将是伟大的。

    5 回复  |  直到 7 年前
        1
  •  4
  •   lroha    7 年前

    一种可能的方法是使用 model.matrix cat 变量的级别与原始矩阵的列名相对应:

    mat.data$cat <- factor(mat.data$cat, levels = head(names(mat.data), -1))
    new.mat <- data.frame(model.matrix( ~  mat.data$cat - 1))
    names(new.mat) <- levels(mat.data$cat)
    
    new.mat
      A B C D
    1 1 0 0 0
    2 1 0 0 0
    3 0 0 1 0
    4 0 1 0 0
    
        2
  •  3
  •   mt1022    7 年前

    另一个选择是 data.table::dcast :

    library(data.table)
    setDT(mat.data)
    mat.data[, cat := factor(cat, levels = names(mat.data)[1:4])]
    res <- dcast(mat.data, cat + seq_along(cat) ~ cat, fun.agg = length, fill = 0, drop = c(T, F))
    res[, cat_1 := NULL]
    
    # > res
    #    cat A B C D
    # 1:   A 1 0 0 0
    # 2:   A 1 0 0 0
    # 3:   B 0 1 0 0
    # 4:   C 0 0 1 0
    
        3
  •  3
  •   Gramposity    7 年前

    这里有一种使用 sapply 依靠逻辑到数字的转换:

    > cat <- c("A", "A", "C", "B")
    > lvls <- LETTERS[1:4]
    > 
    > mat.data <- t(sapply(cat, function(x) as.numeric(lvls == x)))
    > colnames(mat.data) <- lvls
    > mat.data
      A B C D
    A 1 0 0 0
    A 1 0 0 0
    C 0 0 1 0
    B 0 1 0 0
    

    目前所有答案的时间安排:

    > microbenchmark(
    +   model.matrix = {
    +     mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                                         C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    +     mat.data$cat <- factor(mat.data$cat, levels = head(names(mat.data), -1))
    +     new.mat <- data.frame(model.matrix( ~  mat.data$cat - 1))
    +     names(new.mat) <- levels(mat.data$cat)
    +   },
    +   dcast = {
    +     mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                           C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    +     setDT(mat.data)
    +     mat.data[, cat := factor(cat, levels = names(mat.data)[1:4])]
    +     res <- dcast(mat.data, cat + seq_along(cat) ~ cat, fun.agg = length, fill = 0, drop = c(T, F))
    +     res[, cat_1 := NULL]
    +   },
    +   outer = {
    +     mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                           C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    +     match_cols <- setdiff(names(mat.data), "cat")
    +     new.data <- outer(X = mat.data[["cat"]], Y = match_cols, stringi::stri_count_fixed)
    +     colnames(new.data) <- match_cols
    +     cbind(new.data, mat.data["cat"])
    +   },
    +   sapply = {
    +     mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                           C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    +     lvls <- LETTERS[1:4]
    +     new.mat <- t(sapply(mat.data$cat, function(x) as.numeric(lvls == x)))  
    +     colnames(new.mat) <- lvls
    +   },
    +   tidy = {
    +     mat.data = data.frame(A = c(rep(1,2),rep(0,2)), B = c(0,rep(1,2),0) , 
    +                           C = rep(0,4), D = c(rep(0,3),1), cat = c(rep("A",2),"C","B"))
    +     mat.data[5] %>% 
    +       rowid_to_column %>% 
    +       mutate(value=1) %>% 
    +       spread(cat,value, fill=0) %>%
    +       select(-rowid)
    +   }
    + )
    Using 'cat' as value column. Use 'value.var' to override (x100)
    Unit: microseconds
             expr      min       lq      mean    median       uq       max neval
     model.matrix  894.835 1027.983 1185.7946 1173.6940 1313.258  1640.453   100
            dcast 4432.031 4935.079 5603.5700 5290.8000 5725.408 12495.376   100
            outer  508.123  564.671  666.4618  610.9195  758.261  1008.386   100
           sapply  463.534  496.724  611.6146  549.5260  672.997  2526.964   100
             tidy 3936.329 4525.921 5000.3296 4917.7735 5257.409 10660.893   100
    
        4
  •  1
  •   markus    7 年前

    outer 和 stringi::stri_count_fixed

    match_cols <- setdiff(names(mat.data), "cat")
    new.data <- outer(X = mat.data[["cat"]], Y = match_cols, stringi::stri_count_fixed)
    colnames(new.data) <- match_cols
    cbind(new.data, mat.data["cat"])
    #  A B C D cat
    #1 1 0 0 0   A
    #2 1 0 0 0   A
    #3 0 0 1 0   C
    #4 0 1 0 0   B
    

    stringi 你能做到的

    new.data <- 1 * outer(X = mat.data[["cat"]], Y = count_cols, `==`)
    
        5
  •  1
  •   moodymudskipper    7 年前

    这里有一个 tidyverse tidyr::spread :

    library(tidyverse)
    mat.data[5] %>% 
      rowid_to_column %>% 
      mutate(value=1) %>% 
      spread(cat,value, fill=0) %>%
      select(-rowid)
    #   A B C
    # 1 1 0 0
    # 2 1 0 0
    # 3 0 0 1
    # 4 0 1 0
    

    如你所见 D 不存在,如果有的话会存在 "D" 在你的 cat 不过是专栏。