R中字符串中的快速位数

时间:2017-11-27 17:36:17

标签: r regex string performance count

是否有更有效的方法来计算字符串中最常出现的数字?下面我的R代码为每个字符串调用gsub() 10次;我有很多字符串要处理。

> txt = 'wow:011 test 234567, abc=8951111111111aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
> max(vapply(0:9, function(i) nchar(gsub(paste0('[^',i,']'), '', txt)), integer(1L)))
[1] 12

我不关心数字本身。我只想要最频繁的计数。

我更愿意使用R的核心软件包,除非某些外部软件包具有显着的优异性能。我在Windows 10上使用x64 R版本3.4.1(2017-06-30)。

更新:

下面是优秀建议的(苹果对苹果)性能比较。

> microbenchmark(
+     original = max(vapply(0:9, function(i) nchar(gsub(paste0('[^',i,']'), '', s)), integer(1L))),
+     strsplit = max(table(unlist(strsplit(gsub("\\D+", "", s), "")))),
+     gregexpr = max(vapply(0:9, function(d) sum(unlist(gregexpr(d, s)) > 0), integer(1L))),
+     stringi = max(vapply(0:9, function(x) stri_count_fixed(s, x), integer(1L))),
+     raw=max(vapply(0x30:0x39, function(x) sum(charToRaw(s)==x), integer(1L))),
+     tabulate = max(tabulate(as.integer(charToRaw(paste('a',s))))[48:57]),
+     times=1000L)
Unit: microseconds
     expr     min       lq      mean   median       uq      max neval
 original 476.172 536.9770 567.86559 554.8600 580.0530 8054.805  1000
 strsplit 366.071 422.3660 448.69815 445.3810 469.6410  798.389  1000
 gregexpr 302.622 345.2325 423.08347 360.3170 378.0455 9082.416  1000
  stringi 112.589 135.2940 149.82411 144.6245 155.1990 3910.770  1000
      raw  58.161  71.5340  83.57614  77.1330  82.1090 6249.642  1000
 tabulate  18.039  29.8575  35.20816  36.3890  40.7430   72.779  1000

为什么奇怪的计算?

这个奇怪的公式有助于识别用户输入的一些看似简单的假标识符。例如,一些非创意用户(我也是一个有罪的用户)会为他们的电话号码填写相同的数字。通常,在数据分析中,最好没有电话号码,而不是从一个数据集变为另一个数据集的假电话号码。当然,如果有一个校验位,那将是一个额外的简单验证。

2 个答案:

答案 0 :(得分:4)

max(table(unlist(strsplit(gsub("\\D+", "", txt), ""))))
#OR
max(sapply(0:9, function(d) sum(unlist(gregexpr(d, txt)) > 0)))
#[1] 12

或者如果您关心数字

with(rle(sort(unlist(strsplit(gsub("\\D+", "", txt), "")))),
     setNames(c(max(lengths)), values[which.max(lengths)]))
# 1 
#12 
library(microbenchmark)
set.seed(42)
t = paste(sample(c(letters, 0:9), 1e5, TRUE), collapse = "")
microbenchmark(original = max(sapply(0:9, function(i) nchar(gsub(paste0('[^',i,']'), '', t)))),
               strsplit = max(table(unlist(strsplit(gsub("\\D+", "", t), "")))),
               gregexpr = max(sapply(0:9, function(d) sum(unlist(gregexpr(d, t)) > 0))))
#Unit: milliseconds
#     expr        min         lq       mean     median         uq       max neval cld
# original 215.371764 220.862807 233.368696 228.757529 239.809292 308.94393   100   c
# strsplit  11.224226  11.856327  12.956749  12.320586  12.893789  30.61072   100  b 
# gregexpr   7.542871   7.958818   8.680391   8.302971   8.728735  13.79921   100 a  

答案 1 :(得分:3)

以Santosh的方法为基础,这明显快于其他选项......

max(tabulate(as.integer(charToRaw(txt)))[48:57]) #48:57 picks out ASCII digits

library(microbenchmark)
set.seed(42)
t = paste(sample(c(letters, 0:9), 1e5, TRUE), collapse = "")
microbenchmark(original = max(sapply(0:9, function(i) nchar(gsub(paste0('[^',i,']'), '', t)))),
               strsplit = max(table(unlist(strsplit(gsub("\\D+", "", t), "")))),
               gregexpr = max(sapply(0:9, function(d) sum(unlist(gregexpr(d, t)) > 0))),
               tabulate = max(tabulate(as.integer(charToRaw(t)))[48:57]))

Unit: milliseconds
     expr        min         lq        mean     median          uq       max neval
 original 807.947235 860.112901 1169.744733 935.169003 1154.057709 3513.1401   100
 strsplit  34.100444  36.453163   55.457896  42.881400   58.208820  390.1453   100
 gregexpr  27.205510  29.333569   42.616817  33.146572   49.840566  246.9001   100
 tabulate   1.189702   1.208321    2.150022   1.226319    1.297068   37.4300   100