Быстрый подсчет символов в символьном векторе - PullRequest
3 голосов
/ 22 октября 2011

У меня очень длинный вектор из отдельных символов, т.е. somechars<-c("A","B","C","A"...) (длина где-то в миллионах)

Какой самый быстрый способ подсчета общего числа случаев, скажем, "A" и "B"в этом векторе?Я пытался использовать grep и lapply, но все они так долго выполнялись.

Мое текущее решение:

tmp<-table(somechars)
sum(tmp["A"],tmp["B"])

Но это все еще требует времени для вычисления.Есть ли какой-нибудь более быстрый способ, которым я могу это сделать?Или есть какие-то пакеты, которые я могу использовать, чтобы это делало это уже быстрее?Я посмотрел на пакет stringr, но они используют простой grep.

Ответы [ 4 ]

9 голосов
/ 22 октября 2011

Я думал, что это будет быстрее ...

sum(somechars %in% c('A', 'B'))

И это быстрее, чем ...

sum(c(somechars=="A",somechars=="B"))

Но не быстрее, чем ...

sum(somechars=="A"|somechars=="B")

Но это зависит от того, сколько сравнений вы проводите ... что возвращает меня к моему первому предположению.Если вы хотите сложить более 2 букв, используйте версию% in% быстрее всего.

8 голосов
/ 22 октября 2011

Регулярные выражения стоят дорого. Вы можете получить результат в своем вопросе с точным сравнением.

> somechars <- sample(LETTERS, 5e6, TRUE)
> sum(c(somechars=="A",somechars=="B"))
[1] 385675
> system.time(sum(c(somechars=="A",somechars=="B")))
   user  system elapsed 
  0.416   0.072   0.487 

ОБНОВЛЕНО, чтобы включить время из OP и другие ответы. Также включен тест, превышающий 2-символьный регистр.

> library(rbenchmark)
> benchmark( replications=5, order="relative",
+   grep = sum(grepl("A|B",somechars)),
+   table = sum(table(somechars)[c("A","B")]),
+   c = sum(c(somechars=="A",somechars=="B")),
+   OR = sum(somechars=="A"|somechars=="B"),
+   IN = sum(somechars %in% c("A","B")),
+   plus = sum(somechars=="A")+sum(somechars=="B") )
   test replications elapsed relative user.self sys.self user.child sys.child
6  plus            5   4.289 1.000000     3.836    0.436          0         0
3     c            5   4.991 1.163675     4.156    0.804          0         0
5    IN            5   5.480 1.277687     4.549    0.880          0         0
4    OR            5   5.574 1.299604     5.000    0.544          0         0
1  grep            5  16.426 3.829797    16.205    0.172          0         0
2 table            5  17.834 4.158079    12.793    4.884          0         0
> 
> benchmark( replications=5, order="relative",
+   grep = sum(grepl("A|B|C|D",somechars)),
+   table = sum(table(somechars)[c("A","B","C","D")]),
+   c = sum(c(somechars=="A",somechars=="B",
+             somechars=="C",somechars=="D")),
+   OR = sum(somechars=="A"|somechars=="B"|
+            somechars=="C"|somechars=="D"),
+   IN = sum(somechars %in% c("A","B","C","D")),
+   plus = sum(somechars=="A")+sum(somechars=="B")+
+          sum(somechars=="C")+sum(somechars=="D") )
   test replications elapsed relative user.self sys.self user.child sys.child
5    IN            5   5.513 1.000000     4.464    1.004          0         0
6  plus            5   8.603 1.560493     7.705    0.860          0         0
3     c            5  10.283 1.865228     8.648    1.560          0         0
4    OR            5  12.348 2.239797    10.849    1.464          0         0
2 table            5  17.960 3.257754    12.877    4.921          0         0
1  grep            5  21.692 3.934700    21.405    0.192          0         0
2 голосов
/ 22 октября 2011

Согласно моим ожиданиям, sum(x=='A') + sum(x=='B') является самым быстрым.

В отличие от других предлагаемых здесь решений, он не должен выполнять никаких других ненужных операций, таких как объединение промежуточных результатов с использованием c(..) или |. Это просто подсчет - единственное, что действительно необходимо!

R 2.13.1:

> x <- sample(letters, 1e7, TRUE)
> system.time(sum(x=='A') + sum(x=='B'))
   user  system elapsed 
   1.75    0.16    1.98 
> system.time(sum(c(x=='A', x=='B')))
   user  system elapsed 
   2.40    0.23    4.27 
> system.time(sum(x=='A' | x=='B'))
   user  system elapsed 
   2.25    0.19    2.54 

Но действительно интересно сравнение sum(x %in% c('A','B')) спервое, самое быстрое решение.В R 2.13.1 это занимает то же самое время, что в R 2.11.1, это намного медленнее (тот же результат, о котором сообщил Джон)!Поэтому я бы рекомендовал использовать первое решение: sum(x=='A')+sum(x=='B').

0 голосов
/ 22 октября 2011

Мой любимый инструмент, хотя «я не проверял время по сравнению с решениями Tomas», это

rle(sort(your_vector)) 

Это, безусловно, самое простое решение :-)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...