【问题标题】:Count how many times a value appears and adding the result to a column计算一个值出现的次数并将结果添加到列中
【发布时间】:2021-11-13 03:02:52
【问题描述】:

我有这个数据框:

   Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
1:          1 0 0 0 0 0 0   4   4   4   4   5   5
2:          2 0 0 0 0 0 0   4   4   4   4   4   4
3:          3 0 0 0 0 0 0   5   5   5   5   5   5
4:          4 0 0 0 0 0 0   4   5   5   5   4   4
5:          5 0 0 0 0 0 0   5   4   4   4   4   4
6:          6 0 0 0 0 0 0   5   5   5   5   4   4

我想修改 16 列,以便每一列都计算右侧列中该值的出现次数 (NP1 - NP6)。也就是说,4 列应该计算4 出现的次数。我希望对每个号码重复这个过程。可以取值介于05 之间的数字。最终的结果应该是这样的:

head(t2 %>% select(1, 2, 3, 4, 5, 6, 7, NP1, NP2, NP3, NP4, NP5, NP6))
   Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
1:          1 0 0 0 4 2 0   4   4   4   4   5   5
2:          2 0 0 0 6 0 0   4   4   4   4   4   4
3:          3 0 0 0 0 6 0   5   5   5   5   5   5
4:          4 0 0 0 3 3 0   4   5   5   5   4   4
5:          5 0 0 0 5 1 0   5   4   4   4   4   4
6:          6 0 0 0 2 4 0   5   5   5   5   4   4

我已经尝试使用包data.table,我做了以下操作:

 t2[NP1 == 4]$`4` <- t2[NP1 == 4]$`4` + 1

但我有以下错误:

[&lt;-.data.table(*tmp*, NP1 == 4, value = c(1, 1, 1, 1)) 中的错误: 不能在同一查询中两次分配给同一列(检测到重复)。

所以我有两个问题:

  • 为什么会出现此错误?
  • 有没有更简单、更直观的方法?

【问题讨论】:

    标签: r dataframe data.table


    【解决方案1】:

    data.table:

    library(data.table)
    
    setDT(t2)
    
    t2[,as.character(1:6):=lapply(1:6, function(n) rowSums(.SD==n)),.SDcols=NP1:NP6][]
    
    #   Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
    #1:          1 0 0 0 4 2 0   4   4   4   4   5   5
    #2:          2 0 0 0 6 0 0   4   4   4   4   4   4
    #3:          3 0 0 0 0 6 0   5   5   5   5   5   5
    #4:          4 0 0 0 3 3 0   4   5   5   5   4   4
    #5:          5 0 0 0 5 1 0   5   4   4   4   4   4
    #6:          6 0 0 0 2 4 0   5   5   5   5   4   4
    

    数据:

    t2 <- read.table(text=
    "Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
              1 0 0 0 0 0 0   4   4   4   4   5   5
              2 0 0 0 0 0 0   4   4   4   4   4   4
              3 0 0 0 0 0 0   5   5   5   5   5   5
              4 0 0 0 0 0 0   4   5   5   5   4   4
              5 0 0 0 0 0 0   5   4   4   4   4   4
              6 0 0 0 0 0 0   5   5   5   5   4   4",header=T)
    
    colnames(t2) <- c('Generacion','1','2','3','4','5','6','NP1','NP2','NP3','NP4','NP5','NP6')
    

    【讨论】:

    • 使用t2[,as.character(1:6):=lapply(1:6, function(n) rowSums(.SD==n)), .SDcols=patterns("^NP")] 会更好data.table-canonical。 (虽然基准测试差异很小,但证明.SD.SDcols 的使用比.SD[...] 稍快。)
    • @r2evans,感谢您的建议,这确实对性能有影响。我更新了我的答案!
    • (其中唯一脆弱的部分是将as.character(1:6) 以编程方式patterns("^NP") 同步。我的猜测是更好的方法是在lapply,也许是nms &lt;- gsub("NP", "", grep("^NP", colnames(t2), value=TRUE)),然后是t2[, (nms) := lapply(nms, function(n) rowSums(.SD==n)), .SDcols=paste0("NP", nms)]。)
    【解决方案2】:

    使用dplyr 的一个选项可以是(使用更正的列名导入的数据):

    df %>%
        mutate(across(X1:X6, ~ rowSums(across(NP1:NP6) == as.numeric(sub("\\D+", "", cur_column())))))
    
       Generacion X1 X2 X3 X4 X5 X6 NP1 NP2 NP3 NP4 NP5 NP6
    1:          1  0  0  0  4  2  0   4   4   4   4   5   5
    2:          2  0  0  0  6  0  0   4   4   4   4   4   4
    3:          3  0  0  0  0  6  0   5   5   5   5   5   5
    4:          4  0  0  0  3  3  0   4   5   5   5   4   4
    5:          5  0  0  0  5  1  0   5   4   4   4   4   4
    6:          6  0  0  0  2  4  0   5   5   5   5   4   4
    

    如果你想使用只包含数字的列名:

    df %>%
        mutate(across(`1`:`6`, ~ rowSums(across(NP1:NP6) == as.numeric(cur_column()))))
    
     Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
    1          1 0 0 0 4 2 0   4   4   4   4   5   5
    2          2 0 0 0 6 0 0   4   4   4   4   4   4
    3          3 0 0 0 0 6 0   5   5   5   5   5   5
    4          4 0 0 0 3 3 0   4   5   5   5   4   4
    5          5 0 0 0 5 1 0   5   4   4   4   4   4
    6          6 0 0 0 2 4 0   5   5   5   5   4   4
    

    【讨论】:

      【解决方案3】:

      首先,获取必须等于整数的列以及以这些整数作为名称的对应列。

      这部分代码对以下两种解决方案都是通用的。

      cols_to_add <- grep("^NP", names(t2), value = TRUE)
      cols_to_change <- match(gsub("[^[:digit:]]", "", cols_to_add), names(t2)[-1])
      

      基础 R

      在我看来,最简单的是基本 R 函数 rowSums

      t2[as.character(cols_to_change)] <- lapply(cols_to_change, \(x) rowSums(t2[cols_to_add] == x))
      t2
      #  Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
      #1          1 0 0 0 4 2 0   4   4   4   4   5   5
      #2          2 0 0 0 6 0 0   4   4   4   4   4   4
      #3          3 0 0 0 0 6 0   5   5   5   5   5   5
      #4          4 0 0 0 3 3 0   4   5   5   5   4   4
      #5          5 0 0 0 5 1 0   5   4   4   4   4   4
      #6          6 0 0 0 2 4 0   5   5   5   5   4   4
      

      data.table.

      这是一个data.table 解决方案,还有一个lapply 循环。

      library(data.table)
      
      setDT(t2)
      t2[, as.character(cols_to_change) := lapply(
        cols_to_change, \(x) rowSums(.SD == x)), 
        .SDcols = cols_to_add]
      t2
      #   Generacion 1 2 3 4 5 6 NP1 NP2 NP3 NP4 NP5 NP6
      #1:          1 0 0 0 4 2 0   4   4   4   4   5   5
      #2:          2 0 0 0 6 0 0   4   4   4   4   4   4
      #3:          3 0 0 0 0 6 0   5   5   5   5   5   5
      #4:          4 0 0 0 3 3 0   4   5   5   5   4   4
      #5:          5 0 0 0 5 1 0   5   4   4   4   4   4
      #6:          6 0 0 0 2 4 0   5   5   5   5   4   4
      

      【讨论】:

      • 我首先也被这个问题弄糊涂了,但是lapply是用来计算所有列的,(看5),不仅仅是4
      • @Waldi 谢谢!我完全错过了这一点。现在更正了。
      • 不幸的是,它不适用于不是最新版本的 R
      • @jangorecki 代替新的 (R4.1.0) lambda 函数 \(x),使用旧的 function(x)
      【解决方案4】:

      tidyverse 解决方案:

      library(dplyr)
      library(tidyr)
      
      df %>% 
        pivot_longer(starts_with("NP")) %>% 
        count(Generacion, value)%>% 
        rbind(expand.grid(Generacion = 1:nrow(df), value = 1:6, n = 0)) %>%
        group_by(Generacion, value) %>% summarise(n = sum(n))%>%
        pivot_wider(id_cols = Generacion, names_from = value, values_from = n) %>%
        bind_cols(df %>% select(NP1:NP6))
      
      # A tibble: 6 x 13
      # Groups:   Generacion [6]
        Generacion   `1`   `2`   `3`   `4`   `5`   `6`   NP1   NP2   NP3   NP4   NP5   NP6
             <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <int> <int> <int> <int> <int> <int>
      1          1     0     0     0     4     2     0     4     4     4     4     5     5
      2          2     0     0     0     6     0     0     4     4     4     4     4     4
      3          3     0     0     0     0     6     0     5     5     5     5     5     5
      4          4     0     0     0     3     3     0     4     5     5     5     4     4
      5          5     0     0     0     5     1     0     5     4     4     4     4     4
      6          6     0     0     0     2     4     0     5     5     5     5     4     4
      

      【讨论】:

        猜你喜欢
        • 2019-04-19
        • 1970-01-01
        • 2016-01-25
        • 2021-08-26
        • 1970-01-01
        • 2016-04-04
        • 2016-05-15
        • 2020-10-14
        • 1970-01-01
        相关资源
        最近更新 更多