【问题标题】:Count number of values in row using dplyr使用 dplyr 计算行中的值数
【发布时间】:2016-10-10 11:05:42
【问题描述】:

这个问题应该有一个简单、优雅的解决方案,但我想不通,所以就这样吧:

假设我有以下数据集,我想使用 dplyr 计算每行中存在的 2 的数量。

set.seed(1)
ID <- LETTERS[1:5]
X1 <- sample(1:5, 5,T)
X2 <- sample(1:5, 5,T)
X3 <- sample(1:5, 5,T)

df <- data.frame(ID,X1,X2,X3)
library(dplyr)

现在,以下工作:

df %>%
  rowwise %>%
  mutate(numtwos = sum(c(X1,X2,X3) == 2))

但是如何避免输入所有列名?

我知道没有dplyr 可能更容易做到这一点,但更一般地说,我想知道如何在不输入所有列名的情况下将dplyrmutate 用于多列。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    试试rowSums:

    > set.seed(1)
    > ID <- LETTERS[1:5]
    > X1 <- sample(1:5, 5,T)
    > X2 <- sample(1:5, 5,T)
    > X3 <- sample(1:5, 5,T)
    > df <- data.frame(ID,X1,X2,X3)
    > df
      ID X1 X2 X3
    1  A  2  5  2
    2  B  2  5  1
    3  C  3  4  4
    4  D  5  4  2
    5  E  2  1  4
    > rowSums(df == 2)
    [1] 2 1 0 1 1
    

    或者,dplyr:

    > df %>% mutate(numtwos = rowSums(. == 2))
      ID X1 X2 X3 numtwos
    1  A  2  5  2       2
    2  B  2  5  1       1
    3  C  3  4  4       0
    4  D  5  4  2       1
    5  E  2  1  4       1
    

    【讨论】:

    • 我提到我特别想知道如何用 dplyr 做到这一点,即使它不是最好的解决方案。
    • @C_Z_ 查看我最近的编辑,我认为这是最短的dplyr 解决方案
    • . 究竟是如何工作的?是不是像data.table中的.SD
    • 我认为. 只是引用您正在变异的df 的一种方式
    【解决方案2】:

    这是使用purrr 的另一种选择:

    library(purrr)
    
    df %>%
      by_row(function(x) {
        sum(x[-1] == 2) },
        .to = "numtwos",
        .collate = "cols"
      )
    

    这给出了:

    #Source: local data frame [5 x 5]
    #
    #      ID    X1    X2    X3 numtwos
    #  <fctr> <int> <int> <int>   <int>
    #1      A     2     5     2       2
    #2      B     2     5     1       1
    #3      C     3     4     4       0
    #4      D     5     4     2       1
    #5      E     2     1     4       1
    

    NEWS 中所述,基于行的函数在dplyr 中仍在成熟:

    ​​>

    我们仍在弄清楚 dplyr 中的内容和内容 purrr。期待大量的实验和许多变化 功能。


    基准测试

    我们可以看到rowwise()do()purrr::by_row() 在此类问题上的比较,以及它们在rowSums() 和整洁数据方式上的“表现”:

    largedf <-  df[rep(seq_len(nrow(df)), 10e3), ]
    
    library(microbenchmark)
    microbenchmark(
      steven = largedf %>% 
        by_row(function(x) { 
          sum(x[-1] == 2) }, 
          .to = "numtwos", 
          .collate = "cols"),
      psidom = largedf %>% 
        rowwise %>% 
        do(data_frame(numtwos = sum(.[-1] == 2))) %>% 
        cbind(largedf, .),
      gopala = largedf %>% 
        gather(key, value, -ID) %>% 
        group_by(ID) %>% 
        summarise(numtwos = sum(value == 2)) %>% 
        inner_join(largedf, .),
      evan   = largedf %>% 
        mutate(numtwos = rowSums(. == 2)),
      times  = 10L,
      unit   = "relative"
    )
    

    结果:

    #Unit: relative
    #   expr         min          lq        mean      median         uq         max neval cld
    # steven 1225.190659 1261.466936 1267.737126 1227.762573 1276.07977 1339.841636    10  b 
    # psidom 3677.603240 3759.402212 3726.891458 3678.717170 3728.78828 3777.425492    10   c
    # gopala    2.715005    2.684599    2.638425    2.612631    2.59827    2.572972    10 a  
    #   evan    1.000000    1.000000    1.000000    1.000000    1.00000    1.000000    10 a  
    

    【讨论】:

    • 看起来很完美
    • Purrrfect 确实 ;) 虽然从最近的实验来看,by_row() 对于大型数据集来说非常缓慢。
    • @StevenBeaupré 很酷的比较!谢谢你把它放在一起!
    【解决方案3】:

    只是想添加到@evan.oman 的答案,以防您只想对特定列的行求和,而不是全部。您可以使用常规的select 和/或select_helpers 函数。在此示例中,我们不想在 rowSums 中包含 X1

    df %>% 
      mutate(numtwos = rowSums(select(., -X1) == 2))
    
      ID X1 X2 X3 numtwos
    1  A  2  5  2       1
    2  B  2  5  1       0
    3  C  3  4  4       0
    4  D  5  4  2       1
    5  E  2  1  4       0
    

    【讨论】:

      【解决方案4】:

      一种方法是使用dplyrtidyr的组合将数据转换为长格式,然后进行计算:

      library(dplyr)
      library(tidyr)
      df %>%
        gather(key, value, -ID) %>%
        group_by(ID) %>%
        summarise(numtwos = sum(value == 2)) %>%
        inner_join(df, .)
      

      输出如下:

        ID X1 X2 X3 numtwos
      1  A  2  5  2       2
      2  B  2  5  1       1
      3  C  3  4  4       0
      4  D  5  4  2       1
      5  E  2  1  4       1
      

      【讨论】:

        【解决方案5】:

        您可以使用do,它不会将列添加到原始数据框中,您需要将列添加到原始数据框中。

        df %>%
            rowwise %>%
            do(numtwos = sum(.[-1] == 2)) %>% 
            data.frame
          numtwos
        1       2
        2       1
        3       0
        4       1
        5       1
        

        添加cbind 将新列绑定到原始数​​据框:

        df %>%
             rowwise %>%
             do(numtwos = sum(.[-1] == 2)) %>% 
             data.frame %>% cbind(df, .)
        
          ID X1 X2 X3 numtwos
        1  A  2  5  2       2
        2  B  2  5  1       1
        3  C  3  4  4       0
        4  D  5  4  2       1
        5  E  2  1  4       1 
        

        【讨论】:

        • 谢谢,我希望dplyr 有一个更简洁的方法来做到这一点。哦,好吧!
        • dplyrdata.table 中,Rowwise 操作总是有点痛苦,因为据我所知,数据是按列存储的。
        • @Arun,感谢您的澄清。这也是我的猜测。
        猜你喜欢
        • 1970-01-01
        • 2014-05-11
        • 1970-01-01
        • 2018-06-17
        • 2021-02-22
        • 1970-01-01
        • 1970-01-01
        • 2017-10-02
        相关资源
        最近更新 更多