Как векторизовать R strsplit?

При создании функций то использование strsplit, векторные исходные данные не ведут себя, как желаемый, и sapply потребности, которые будут использоваться. Это происходит из-за вывода списка это strsplit производит. Существует ли способ векторизовать процесс - то есть, функция производит корректный элемент в списке для каждого из элементов входа?

Например, для подсчета длин слов в символьном векторе:

words <- c("a","quick","brown","fox")

> length(strsplit(words,""))
[1] 4 # The number of words (length of the list)

> length(strsplit(words,"")[[1]])
[1] 1 # The length of the first word only

> sapply(words,function (x) length(strsplit(x,"")[[1]]))
a quick brown   fox 
1     5     5     3 
# Success, but potentially very slow

Идеально, что-то как length(strsplit(words,"")[[.]]) где . интерпретируется как существо соответствующая часть входного вектора.

15
задан smci 18 May 2015 в 02:00
поделиться

1 ответ

В общем, вы должны попытаться использовать векторизованную функцию для начала. Использование strsplit часто требует некоторой итерации (что будет медленнее), поэтому старайтесь избегать этого, если возможно. В вашем примере вместо этого следует использовать nchar:

> nchar(words)
[1] 1 5 5 3

В более общем случае воспользуйтесь тем, что strsplit возвращает список, и используйте lapply:

> as.numeric(lapply(strsplit(words,""), length))
[1] 1 5 5 3

Или же используйте функцию семейства l*ply из plyr. Например:

> laply(strsplit(words,""), length)
[1] 1 5 5 3

Edit:

В честь Bloomsday я решил проверить производительность этих подходов, используя "Улисс" Джойса:

joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt")
joyce <- unlist(strsplit(joyce, " "))

Теперь, когда у меня есть все слова, мы можем заняться подсчетами:

> # original version
> system.time(print(summary(sapply(joyce, function (x) length(strsplit(x,"")[[1]])))))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   3.000   4.000   4.666   6.000  69.000 
   user  system elapsed 
   2.65    0.03    2.73 
> # vectorized function
> system.time(print(summary(nchar(joyce))))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   3.000   4.000   4.666   6.000  69.000 
   user  system elapsed 
   0.05    0.00    0.04 
> # with lapply
> system.time(print(summary(as.numeric(lapply(strsplit(joyce,""), length)))))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   3.000   4.000   4.666   6.000  69.000 
   user  system elapsed 
    0.8     0.0     0.8 
> # with laply (from plyr)
> system.time(print(summary(laply(strsplit(joyce,""), length))))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   3.000   4.000   4.666   6.000  69.000 
   user  system elapsed 
  17.20    0.05   17.30
> # with ldply (from plyr)
> system.time(print(summary(ldply(strsplit(joyce,""), length))))
       V1        
 Min.   : 0.000  
 1st Qu.: 3.000  
 Median : 4.000  
 Mean   : 4.666  
 3rd Qu.: 6.000  
 Max.   :69.000  
   user  system elapsed 
   7.97    0.00    8.03 

Векторизованная функция и lapply значительно быстрее, чем оригинальная версия sapply. Все решения возвращают один и тот же ответ (как видно из итогового вывода).

По-видимому, последняя версия plyr быстрее (здесь используется немного более старая версия).

21
ответ дан 1 December 2019 в 03:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: