【问题标题】:How to count frequencies of certain character in a string?如何计算字符串中某个字符的频率?
【发布时间】:2015-06-24 09:55:05
【问题描述】:

如果我有一系列字符,例如"AABBABBBAAAABBAAAABBBAABBBBABABB"

有没有办法让 R 计算 A 的运行次数并说明每个长度有多少?

所以我想知道连续有多少个 3 A 的实例,有多少个单个 A 的实例,连续有多少个 2 A 的实例,等等。

【问题讨论】:

    标签: regex r string


    【解决方案1】:
    table(rle(strsplit("AABBABBBAAAABBAAAABBBAABBBBABABB","")[[1]]))
    

    给予

           values
    lengths A B
          1 3 1
          2 2 3
          3 0 2
          4 2 1
    

    其中(向下读取 A 列)意味着有 3 个长度为 1 的 A 运行、2 个长度为 2 的 A 运行和 2 个长度为 4 的运行。

    【讨论】:

    • 问题中没有提到速度作为主要考虑因素,但会有更快的方法。
    • @akrun 是的,rle 通常很快,但strsplit 可能很慢。
    • 是的,我可能会使用它来获得更好的输出(考虑到用长度表示不同的字符串,并且因为我怀疑速度很重要)。或者只是 table(nchar(strsplit(x, "[^A]+")[[1]])),因为 OP 只关心“A”。
    • 最后的 [[1]] 是做什么的?
    • strsplit 返回一个列表(在这种情况下,由于参数仅包含一个元素,因此它返回一个包含一个元素的列表,其中包含拆分后的字符向量)。我想要字符向量而不是它包含的列表; [[1]] 是实现这一目标的一种方法。
    【解决方案2】:

    试试

     v1 <- scan(text=gsub('[^A]+', ',', str1), sep=',', what='', quiet=TRUE)
     table(v1[nzchar(v1)])
     # A   AA AAAA 
     # 3    2    2 
    

    或者

     library(stringi)
     table(stri_extract_all_regex(str1, '[A]+')[[1]])
     # A   AA AAAA 
     # 3    2    2 
    

    基准测试

     set.seed(42)
     x1 <- stri_rand_strings(1,1e7, pattern='[A-G]')
    
     system.time(table(stri_split_regex(x1, "[^A]+", omit_empty = TRUE)))
     #   user  system elapsed 
     #  0.829   0.002   0.831 
    
     system.time(table(stri_extract_all_regex(x1, '[A]+')[[1]]))
     #   user  system elapsed 
     #   0.790   0.002   0.791 
    
     system.time(table(rle(strsplit(x1,"")[[1]])) )
     #   user  system elapsed 
     #  30.230   1.243  31.523 
    
     system.time(table(strsplit(x1, "[^A]+")))
     # user  system elapsed 
     # 4.253   0.006   4.258 
    
    
     system.time(table(attr(gregexpr("A+",x1)[[1]], 'match.length')))
     #  user  system elapsed 
     #  1.994   0.004   1.999 
    
    
     library(microbenchmark)
     microbenchmark(david=table(stri_split_regex(x1, "[^A]+", omit_empty = TRUE)),
        akrun=  table(stri_extract_all_regex(x1, '[A]+')[[1]]),
        david2 =  table(strsplit(x1, "[^A]+")),
        glen = table(rle(strsplit(x1,"")[[1]])),
        plannapus = table(attr(gregexpr("A+",x1)[[1]], 'match.length')),
             times=20L, unit='relative')
    
    #Unit: relative
    #     expr       min        lq      mean    median         uq       max    neval  cld
    #   david  1.0000000  1.000000  1.000000  1.000000  1.0000000  1.000000    20       a  
    #   akrun  0.7908313  1.023388  1.054670  1.336510  0.9903384  1.004711    20       a
    #  david2  4.9325256  5.461389  5.613516  6.207990  5.6647301  5.374668    20       c 
    #    glen 14.9064240 15.975846 16.672339 20.570874 15.8710402 15.465140    20       d
    #plannapus 2.5077719  3.123360  2.836338  3.557242  2.5689176  2.452964    20       b 
    

    数据

     str1 <- 'AABBABBBAAAABBAAAABBBAABBBBABABB'
    

    【讨论】:

    • 时间安排很有趣。
    • @Glen_b 我正在用新帖子再次运行它
    • @akrun 我认为我的速度不会特别好:)
    • @plannapus 和 stringi,很难竞争,但我会更新以使其完整
    • @plannapus 你是对的。我尝试了微基准测试,但我不得不停止它,因为它需要很长时间。我正在使用 system.time 进行更新
    【解决方案3】:

    这是使用strsplit的另一种方式

    x <- "AABBABBBAAAABBAAAABBBAABBBBABABB"
    table(strsplit(x, "[^A]+"))
    # A   AA AAAA 
    # 3    2    2 
    

    或与stringi 包类似

    library(stringi)
    table(stri_split_regex(x, "[^A]+", omit_empty = TRUE))
    

    【讨论】:

      【解决方案4】:

      为了完整起见,这里有另一种方法,使用 regmatchesgregexpr 组合来提取正则表达式:

      x <- "AABBABBBAAAABBAAAABBBAABBBBABABB"
      table(regmatches(x,gregexpr("A+",x))[[1]])
      #   A   AA AAAA 
      #   3    2    2
      

      或者实际上,由于gregexpr 将捕获的子字符串的长度作为属性,甚至可以直接这样做:

      table(attr(gregexpr("A+",x)[[1]],'match.length'))
      # 1 2 4 
      # 3 2 2 
      

      【讨论】:

      • 我更新了基准。您的版本在基本 R 函数中更快
      • 我尝试创建一个包含替换的 100 个随机样本,结果如​​预期的那样混合了“A”和“B”。我认为这样做会产生更多问题,因为我现在似乎有 100 个单独的字符串。这是个坏主意吗?
      • 如果您想创建一个 100 A 或 Bs 的随机样本并进行替换,您可以使用基于 @akrun stringi 的技术(请参阅他的答案)或 base 方式:paste(sample(c("A","B"),100,replace=TRUE),collapse="")
      猜你喜欢
      • 2011-10-06
      • 1970-01-01
      • 2020-01-04
      • 1970-01-01
      • 2016-05-18
      • 2017-04-19
      • 1970-01-01
      • 1970-01-01
      • 2012-06-04
      相关资源
      最近更新 更多