【问题标题】:Count values in the columns separated by ":"计算由“:”分隔的列中的值
【发布时间】:2014-10-12 23:03:27
【问题描述】:

我是 r 的新手,我必须计算以“:”分隔的列中的值。

数据集中有 4 个类别,我必须计算每个类别的操作数。每个 log_id 代表一个类别中的唯一操作。如果一个 log_id 有 2 个或多个类别,则意味着该特定操作将计入所有提及的类别。

数据是这样的

user_id   log_id  categories
  001     1334    Perform:Sport_Well:Com.Tent
  001     1323    Com.Tent
  001     1212    Active
  002     1113    NA
  002     1478    Com.Tent:Active
  002     1134    Sport_Well:Perform
  002     1256    Perform
  002     1590    Perform
  002     1345    NA
  002     1478    Com.Tent
  002     1134    Sport_Well:Perform
  002     1256    Perform
  003     1590    Perform
  003     1345    Active:Perform
  003     1190    Perform:Com.Tent
  003     1239    Active:Perform

这是dput

dat <- structure(list(user_id = c("001", "001", "001", "002", "002", 
  "002", "002", "002", "002", "002", "002", "002", "003", "003", 
  "003", "003"), log_id = c("1334", "1323", "1212", "1113", "1478", 
  "1134", "1256", "1590", "1345", "1478", "1134", "1256", "1590", 
  "1345", "1190", "1239"), categories = c("Perform:Sport_Well:Com.Tent", 
  "Com.Tent", "Active", NA, "Com.Tent:Active", "Sport_Well:Perform", 
  "Perform", "Perform", NA, "Com.Tent", "Sport_Well:Perform", "Perform", 
  "Perform", "Active:Perform", "Perform:Com.Tent", "Active:Perform")), 
  .Names = c("user_id", "log_id", "categories"), class = "data.frame", row.names = c(NA, -16L))

所需的输出如下:

user_id   category        NumActions
  001     Perform             1
  001     Sport_Well          1
  001     Com.Tent            2
  001     Active              1
  002     Com.Tent            2
  002     Active              1
  002     Perform             5
  002     Sport_Well          2
  003     Com.Tent            2
  003     Active              2
  003     Perform             4

我正在尝试拆分类别,但不知道如何计算具有多个类别的 log_id。

df$cate = str_split(string = df$Ch_Category, pattern = ":")

【问题讨论】:

    标签: r


    【解决方案1】:

    dplyr 这是一个 dplyr 解决方案:

    library(dplyr)
    
    dat %>% 
       group_by(user_id) %>% 
       do(strsplit(.$categories, ":") %>% 
            unlist %>% 
            table(dnn = "category") %>% 
            as.data.frame(responseName = "numActions", stringsAsFactors = FALSE))
    

    给出:

    Source: local data frame [11 x 3]
    Groups: user_id
    
       user_id categories numActions
    1      001     Active          1
    2      001   Com.Tent          2
    3      001    Perform          1
    4      001 Sport_Well          1
    5      002     Active          1
    6      002   Com.Tent          2
    7      002    Perform          5
    8      002 Sport_Well          2
    9      003     Active          2
    10     003   Com.Tent          1
    11     003    Perform          4
    

    请注意,如果您不关心标题名称,那么我们可以省略 dnn=...responseName=...,如果可以忽略的警告是可以的,那么我们可以省略 stringsAsFactors=...,所以有了这些警告,它可以是缩短为:

    dat %>% 
       group_by(user_id) %>% 
       do(strsplit(.$categories, ":") %>% unlist %>% table %>% as.data.frame)
    

    data.table 这可以在data.table 中类似地完成:

    library(data.table)
    DT <- data.table(dat)
    DT[, as.data.frame(table(unlist(strsplit(categories, ":")), dnn = "categories"),
                     responseName = "numActions"), by = user_id]
    

    以及缩短的最后一条语句,但需要注意的是列名不相同:

    DT[, as.data.frame(table(unlist(strsplit(categories, ":")))), by = user_id]
    

    【讨论】:

    • 非常感谢您的帮助。由于我是 R 新手,所以我不明白 "%>% "。如果您能简要解释一下这段代码是如何工作的,我将不胜感激。
    • 阅读 magrittr 包小插图。
    【解决方案2】:

    拆分列中的字符串,将行添加到临时数据框中,然后进行计数。此示例使用dplyr 成语,但如果您不能使用dplyr,我相信其他人会发布基本R 解决方案:

    library(dplyr)
    
    cats <- strsplit(dat$categories, ":")
    tmp <- data.frame(user_id = rep(dat$user_id, sapply(cats, length)), categories = unlist(cats))
    tmp %>% 
      group_by(user_id, categories) %>% 
      summarise(NumActions=n()) %>% 
      ungroup
    
    ##    user_id categories NumActions
    ## 1      001     Active          1
    ## 2      001   Com.Tent          2
    ## 3      001    Perform          1
    ## 4      001 Sport_Well          1
    ## 5      002     Active          1
    ## 6      002   Com.Tent          2
    ## 7      002    Perform          5
    ## 8      002 Sport_Well          2
    ## 9      002         NA          2
    ## 10     003     Active          2
    ## 11     003   Com.Tent          1
    ## 12     003    Perform          4
    

    【讨论】:

      【解决方案3】:

      我今天一直在玩 tidyr,所以这里有一个使用该软件包的解决方案。

      首先我将separate 合并为三个列。我使用gather 将生成的数据集重塑为长格式(删除缺失值)。然后我使用 dplyr group_bysummarise 将每个组的数字相加。

      library(tidyr)
      library(dplyr)
      

      将一列分成三列:

      dat %>% 
          separate(categories, c("a", "b", "c"), sep = ":", extra = "merge")
      
         user_id log_id          a          b        c
      1      001   1334    Perform Sport_Well Com.Tent
      2      001   1323   Com.Tent       <NA>     <NA>
      3      001   1212     Active       <NA>     <NA>
      4      002   1113       <NA>       <NA>     <NA>
      5      002   1478   Com.Tent     Active     <NA>
      6      002   1134 Sport_Well    Perform     <NA>
      7      002   1256    Perform       <NA>     <NA>
      8      002   1590    Perform       <NA>     <NA>
      9      002   1345       <NA>       <NA>     <NA>
      10     002   1478   Com.Tent       <NA>     <NA>
      11     002   1134 Sport_Well    Perform     <NA>
      12     002   1256    Perform       <NA>     <NA>
      13     003   1590    Perform       <NA>     <NA>
      14     003   1345     Active    Perform     <NA>
      15     003   1190    Perform   Com.Tent     <NA>
      16     003   1239     Active    Perform     <NA>
      

      制作成长格式(类别一栏):

      dat %>% 
          separate(categories, c("a", "b", "c"), sep = ":", extra = "merge") %>%
          gather(variable, category, a:c, na.rm = TRUE)
      
         user_id log_id variable   category
      1      001   1334        a    Perform
      2      001   1323        a   Com.Tent
      3      001   1212        a     Active
      4      002   1478        a   Com.Tent
      5      002   1134        a Sport_Well
      6      002   1256        a    Perform
      7      002   1590        a    Perform
      ...
      

      然后按user_idcategory 分组,并在每组中计数。

      dat %>% 
      separate(categories, c("a", "b", "c"), sep = ":", extra = "merge") %>%
      gather(variable, category, a:c, na.rm = TRUE) %>%
      group_by(user_id, category) %>%
      summarise(NumActions = n())
      
         user_id   category NumActions
      1      001     Active          1
      2      001   Com.Tent          2
      3      001    Perform          1
      4      001 Sport_Well          1
      5      002     Active          1
      6      002   Com.Tent          2
      7      002    Perform          5
      8      002 Sport_Well          2
      9      003     Active          2
      10     003   Com.Tent          1
      11     003    Perform          4
      

      【讨论】:

        【解决方案4】:

        以下基本 R 代码给出相同的输出,但格式不同:

        > aa = aggregate(categories~user_id, data=dat, function(x) paste(x,collapse=':'))
        > sapply(sapply(split(aa, aa$user_id), function(x) strsplit(x$categories, ':')  ), table )
        $`001`
        
            Active   Com.Tent    Perform Sport_Well 
                 1          2          1          1 
        
        $`002`
        
            Active   Com.Tent    Perform Sport_Well 
                 1          2          5          2 
        
        $`003`
        
          Active Com.Tent  Perform 
               2        1        4 
        

        【讨论】:

        • 感谢@mso,但我需要汇总各个类别。
        【解决方案5】:

        您可以在“data.table”中使用my cSplit function.N,如下所示:

        cSplit(dat, "categories", ":", "long")[, list(NumActions = .N), 
                                               by = list(user_id, categories)]
        #     user_id categories NumActions
        #  1:     001    Perform          1
        #  2:     001 Sport_Well          1
        #  3:     001   Com.Tent          2
        #  4:     001     Active          1
        #  5:     002         NA          2
        #  6:     002   Com.Tent          2
        #  7:     002     Active          1
        #  8:     002 Sport_Well          2
        #  9:     002    Perform          5
        # 10:     003    Perform          4
        # 11:     003     Active          2
        # 12:     003   Com.Tent          1
        

        请注意,这也算NA,您可能想要也可能不想要。如果你不想要它。删除这些值只需要一个简单的na.omit。要删除NA“类别”,只需将以下内容添加到上述命令的末尾:

        [!is.na(categories)]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-02
          • 2013-02-05
          • 1970-01-01
          相关资源
          最近更新 更多