При создании функций то использование 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,"")[[.]])
где .
интерпретируется как существо соответствующая часть входного вектора.
В общем, вы должны попытаться использовать векторизованную функцию для начала. Использование 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
быстрее (здесь используется немного более старая версия).