【问题标题】:How to calculate the number of occurrence of a given character in each row of a column of strings?如何计算给定字符在一列字符串的每一行中出现的次数?
【发布时间】:2012-09-07 18:54:31
【问题描述】:

我有一个 data.frame,其中某些变量包含一个文本字符串。我希望计算每个字符串中给定字符的出现次数。

例子:

q.data<-data.frame(number=1:3, string=c("greatgreat", "magic", "not"))

我希望为 q.data 创建一个新列,其中包含字符串中“a”的出现次数(即 c(2,1,0))。

我管理过的唯一复杂的方法是:

string.counter<-function(strings, pattern){  
  counts<-NULL
  for(i in 1:length(strings)){
    counts[i]<-length(attr(gregexpr(pattern,strings[i])[[1]], "match.length")[attr(gregexpr(pattern,strings[i])[[1]], "match.length")>0])
  }
return(counts)
}

string.counter(strings=q.data$string, pattern="a")

 number     string number.of.a
1      1 greatgreat           2
2      2      magic           1
3      3        not           0

【问题讨论】:

    标签: regex r dataframe


    【解决方案1】:

    stringr 包提供了str_count 函数,它似乎可以做你感兴趣的事情

    # Load your example data
    q.data<-data.frame(number=1:3, string=c("greatgreat", "magic", "not"), stringsAsFactors = F)
    library(stringr)
    
    # Count the number of 'a's in each element of string
    q.data$number.of.a <- str_count(q.data$string, "a")
    q.data
    #  number     string number.of.a
    #1      1 greatgreat           2
    #2      2      magic           1
    #3      3        not           0
    

    【讨论】:

    • 你的速度要快得多,尽管它确实需要一个 as.character() 围绕主要参数才能成功解决所提出的问题。
    • @DWin - 没错,但我通过在定义数据框时添加 stringsAsFactors = FALSE 避免了这个问题。
    • 对不起,我不清楚。我实际上是在回复 tim riffe 并告诉他他的函数在所提出的问题上出现了错误。他可能使用了你对问题的重新定义,但他没有这么说。
    • 是的,我也这样做了,stringsAsFactors=TRUE 在我的 comp 上,但没有提到这个
    • 在因子中搜索字符串会起作用,即 str_count(d$factor_column,'A') 但反之则不行
    【解决方案2】:

    如果你不想离开base R,这里有一个相当简洁和富有表现力的可能性:

    x <- q.data$string
    lengths(regmatches(x, gregexpr("a", x)))
    # [1] 2 1 0
    

    【讨论】:

    • 好吧——也许只有在你同时使用regmatchesgregexpr 几次后才会觉得富有表现力,但这个组合足够强大,我认为它值得一个插件。
    • regmatches 相对较新。它是在 2.14 中引入的。
    • 我认为您不需要 regmatches 位。函数 gregexpr 返回一个列表,其中包含 x 的每个元素的匹配项的索引。
    • @savagent -- 你介意分享你用来计算每个字符串中匹配数的代码吗?
    • 抱歉,我忘记了 -1。仅当每行至少有一个匹配项 sapply(gregexpr("g", q.data$string), length) 时才有效。
    【解决方案3】:
    nchar(as.character(q.data$string)) -nchar( gsub("a", "", q.data$string))
    [1] 2 1 0
    

    请注意,在传递给 nchar 之前,我将因子变量强制转换为字符。正则表达式函数似乎在内部执行此操作。

    这是基准测试结果(测试规模扩大到 3000 行)

     q.data<-q.data[rep(1:NROW(q.data), 1000),]
     str(q.data)
    'data.frame':   3000 obs. of  3 variables:
     $ number     : int  1 2 3 1 2 3 1 2 3 1 ...
     $ string     : Factor w/ 3 levels "greatgreat","magic",..: 1 2 3 1 2 3 1 2 3 1 ...
     $ number.of.a: int  2 1 0 2 1 0 2 1 0 2 ...
    
     benchmark( Dason = { q.data$number.of.a <- str_count(as.character(q.data$string), "a") },
     Tim = {resT <- sapply(as.character(q.data$string), function(x, letter = "a"){
                                sum(unlist(strsplit(x, split = "")) == letter) }) }, 
    
     DWin = {resW <- nchar(as.character(q.data$string)) -nchar( gsub("a", "", q.data$string))},
     Josh = {x <- sapply(regmatches(q.data$string, gregexpr("g",q.data$string )), length)}, replications=100)
    #-----------------------
       test replications elapsed  relative user.self sys.self user.child sys.child
    1 Dason          100   4.173  9.959427     2.985    1.204          0         0
    3  DWin          100   0.419  1.000000     0.417    0.003          0         0
    4  Josh          100  18.635 44.474940    17.883    0.827          0         0
    2   Tim          100   3.705  8.842482     3.646    0.072          0         0
    

    【讨论】:

    • 这是答案中最快的解决方案,但通过将可选的fixed=TRUE 传递给gsub,您的基准测试速度提高了约 30%。在某些情况下,fixed=TRUE 将是必需的(即,当您要计算的字符可能被解释为正则表达式断言时,例如.)。
    【解决方案4】:

    stringi 包提供了stri_countstri_count_fixed 非常快的函数。

    stringi::stri_count(q.data$string, fixed = "a")
    # [1] 2 1 0
    

    基准测试

    @42-'s answerequivalent function from the stringr package 的最快方法相比,对于具有 30.000 个元素的向量。

    library(microbenchmark)
    
    benchmark <- microbenchmark(
      stringi = stringi::stri_count(test.data$string, fixed = "a"),
      baseR = nchar(test.data$string) - nchar(gsub("a", "", test.data$string, fixed = TRUE)),
      stringr = str_count(test.data$string, "a")
    )
    
    autoplot(benchmark)
    

    数据

    q.data <- data.frame(number=1:3, string=c("greatgreat", "magic", "not"), stringsAsFactors = FALSE)
    test.data <- q.data[rep(1:NROW(q.data), 10000),]
    

    【讨论】:

      【解决方案5】:

      另一个不错的选择,使用 charToRaw

      sum(charToRaw("abc.d.aa") == charToRaw('.'))
      

      【讨论】:

        【解决方案6】:

        https://stackoverflow.com/a/12430764/589165 的变体是

        > nchar(gsub("[^a]", "", q.data$string))
        [1] 2 1 0
        

        【讨论】:

          【解决方案7】:

          我相信有人可以做得更好,但这很有效:

          sapply(as.character(q.data$string), function(x, letter = "a"){
            sum(unlist(strsplit(x, split = "")) == letter)
          })
          greatgreat      magic        not 
               2          1          0 
          

          或在函数中:

          countLetter <- function(charvec, letter){
            sapply(charvec, function(x, letter){
              sum(unlist(strsplit(x, split = "")) == letter)
            }, letter = letter)
          }
          countLetter(as.character(q.data$string),"a")
          

          【讨论】:

          • 我似乎收到了第一个错误...第二个...(试图对所有这些进行基准测试。)
          【解决方案8】:

          你可以只使用字符串除法

          require(roperators)
          my_strings <- c('apple', banana', 'pear', 'melon')
          my_strings %s/% 'a'
          

          这将为您提供 1、3、1、0。您还可以将字符串除法与正则表达式和整个单词一起使用。

          【讨论】:

            【解决方案9】:

            恕我直言,最简单和最干净的方法是:

            q.data$number.of.a <- lengths(gregexpr('a', q.data$string))
            
            #  number     string number.of.a`
            #1      1 greatgreat           2`
            #2      2      magic           1`
            #3      3        not           0`
            

            【讨论】:

            • 这是怎么做的?对我来说,lengths(gregexpr('a', q.data$string)) 返回 2 1 1,而不是 2 1 0
            【解决方案10】:

            以下问题已移至此处,但此页面似乎并未直接回答 Farah El 的问题。 How to find number 1s in 101 in R

            所以,我会在这里写一个答案,以防万一。

            library(magrittr)
            n %>% # n is a number you'd like to inspect
              as.character() %>%
              str_count(pattern = "1")
            

            https://stackoverflow.com/users/8931457/farah-el

            【讨论】:

              【解决方案11】:

              另一个base R 选项可能是:

              lengths(lapply(q.data$string, grepRaw, pattern = "a", all = TRUE, fixed = TRUE))
              
              [1] 2 1 0
              

              【讨论】:

                【解决方案12】:

                下一个表达式完成这项工作,也适用于符号,而不仅仅是字母。

                表达式的作用如下:

                1:它在数据帧 q.data 的列上使用 lapply 来迭代第 2 列的行 ("lapply(q.data[,2],"),

                2:它适用于第 2 列的每一行一个函数“function(x){sum('a' == strsplit(as.character(x), '')[[1]])}”。 该函数获取第 2 列 (x) 的每一行值,转换为字符(例如,如果它是一个因素),并在每个字符上拆分字符串(“strsplit(as.character(x), ' ')”)。因此,我们有一个向量,其中包含第 2 列每一行的字符串值的每个字符。

                3:将向量的每个向量值与要计数的所需字符进行比较,在本例中为“a”(“'a' ==”)。此操作将返回 True 和 False 值“c(True,False,True,....)”的向量,当向量中的值与要计数的所需字符匹配时为 True。

                4:字符“a”出现在行中的总次数计算为向量“sum(....)”中所有“真”值的总和。

                5:然后应用“unlist”函数解包“lapply”函数的结果并将其分配给数据帧中的新列(“q.data$number.of.a

                q.data$number.of.a<-unlist(lapply(q.data[,2],function(x){sum('a' == strsplit(as.character(x), '')[[1]])}))
                
                >q.data
                
                #  number     string     number.of.a
                #1   greatgreat         2
                #2      magic           1
                #3      not             0
                

                【讨论】:

                • 如果能解释一下它的作用,你的答案会好很多,尤其是对于新用户,因为它不是一个简单表达式。
                • 感谢@Khaine775 的评论,我很抱歉缺少对帖子的描述。我已经编辑了帖子并添加了一些 cmets 以便更好地描述它的工作原理。
                【解决方案13】:
                s <- "aababacababaaathhhhhslsls jsjsjjsaa ghhaalll"
                p <- "a"
                s2 <- gsub(p,"",s)
                numOcc <- nchar(s) - nchar(s2)
                

                可能不是有效的,但可以解决我的目的。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-03-07
                  • 2019-03-12
                  • 2022-10-15
                  • 2011-07-01
                  相关资源
                  最近更新 更多