代码之家  ›  专栏  ›  技术社区  ›  Sylvia Rodriguez

根据R中的值将多列折叠为一列

  •  1
  • Sylvia Rodriguez  · 技术社区  · 2 年前

    我有 data.frame df1 :

     df1 <- data.frame(apple = c('0', '0', '0', '1', '0', '1', '0', '0', '0', '1'),
          banana = c('1', '0', '0', '0', '1', '0', '1', '0', '0', '0'),
          cherry = c('0', '1', '0', '0', '0', '0', '0', '1', '0', '0'),
          date = c('0', '0', '1', '0', '0', '0', '0', '0', '1', '0'))
     rownames(df1) <- c('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten')
    

    如何用列名替换每列中的所有“ones”,然后将该列折叠成一个新列?预期结果为 df2 :

     df2 <- data.frame(fruit = c('banana', 'cherry', 'date', 'apple', 'banana', 
          'apple', 'banana', 'cherry', 'date', 'apple'))
     rownames(df2) <- c('one', 'two', 'three', 'four', 'five', 
          'six', 'seven', 'eight', 'nine', 'ten')
    

    我在这里找到了第二步( r collapsing data from multiple columns into one )但我还没有完全到达那里。

    3 回复  |  直到 2 年前
        1
  •  1
  •   TarJae    2 年前

    这是 tidyverse 方式使用 transmute unite :

    library(dplyr)
    library(tidyr)
    
    df1 %>%
      transmute(across(apple:date, ~case_when(. == 1 ~ cur_column()), .names = 'new_{col}')) %>% 
      unite(fruit, starts_with('new'), na.rm = TRUE, sep = ' ')
    
           fruit
    one   banana
    two   cherry
    three   date
    four   apple
    five  banana
    six    apple
    seven banana
    eight cherry
    nine    date
    ten    apple
    
        2
  •  1
  •   user2974951    2 年前

    使用apply

    > apply(df1==1,1,function(x){colnames(df1)[x]})
         one      two    three     four     five      six    seven    eight     nine      ten 
    "banana" "cherry"   "date"  "apple" "banana"  "apple" "banana" "cherry"   "date"  "apple"
    

    另一个选项使用

    > df2=as.data.frame(which(df1==1,arr.ind=T))
    > df2=df2[order(df2$row),]
    > df2$fruit=colnames(df1)[df2$col]
          row col  fruit
    one     1   2 banana
    two     2   3 cherry
    three   3   4   date
    four    4   1  apple
    five    5   2 banana
    six     6   1  apple
    seven   7   2 banana
    eight   8   3 cherry
    nine    9   4   date
    ten    10   1  apple
    
        3
  •  1
  •   jay.sf    2 年前

    使用 max.col 子集 names ,创建 data.frame 用相应的 rownames .

    > (res <- data.frame(fruit=names(df1)[max.col(df1)]) |> `rownames<-`(rownames(df1)))
           fruit
    one   banana
    two   cherry
    three   date
    four   apple
    five  banana
    six    apple
    seven banana
    eight cherry
    nine    date
    ten    apple
    > all.equal(df2, res)
    [1] TRUE
    

    基准

    > DF1 <- df1[sample.int(nrow(df1), 1e4, replace=TRUE), ]
    > microbenchmark::microbenchmark(max.col=data.frame(fruit=names(DF1)[max.col(DF1)]) |> `rownames<-`(rownames(DF1)),
    +                                tidy=DF1 %>%
    +                                  transmute(across(apple:date, ~case_when(. == 1 ~ cur_column()), .names = 'new_{col}')) %>% 
    +                                  unite(fruit, starts_with('new'), na.rm = TRUE, sep = ' '),
    +                                check='equivalent')
    ~$ Rscript --vanilla foo.R
    Unit: milliseconds
        expr       min        lq      mean    median        uq        max neval cld
     max.col  4.915768  5.041378  5.213677  5.193255  5.321326   5.821633   100  a 
        tidy 65.275247 67.524774 72.382577 71.061575 72.707830 145.169879   100   b
    

    数据:

    > dput(df1)
    structure(list(apple = c("0", "0", "0", "1", "0", "1", "0", "0", 
    "0", "1"), banana = c("1", "0", "0", "0", "1", "0", "1", "0", 
    "0", "0"), cherry = c("0", "1", "0", "0", "0", "0", "0", "1", 
    "0", "0"), date = c("0", "0", "1", "0", "0", "0", "0", "0", "1", 
    "0")), class = "data.frame", row.names = c("one", "two", "three", 
    "four", "five", "six", "seven", "eight", "nine", "ten"))