2010-06-16 4 views
10

При создании функций, которые используют strsplit, векторные входы не ведут себя по желанию, а sapply необходимо использовать. Это связано с выходом списка, который производит strsplit. Есть ли способ векторизации процесса, т. Е. Функция создает правильный элемент в списке для каждого из элементов ввода?Как векторизовать R 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,"")[[.]]), где . интерпретируется как существо соответствующая часть входного вектора.

ответ

19

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

+0

Спасибо Шейн, но я не получаю одинаковых результатов от того, что я делаю. Его реализация схемы контрольных цифр Verhoeff. Я изменил мою функцию, чтобы быть совместимой с вышеупомянутыми реализациями, но с вводом вектора длиной 100 000, я получаю список из 8 элементов из первого и вектор из 8 элементов из второго (8 - это наиболее вероятная длина векторных элементов). – James

+0

@James: Тогда я бы предположил, что с вашей функцией должно быть что-то еще. Как вы можете видеть выше, я просто проверил это на векторе с более чем 270 тыс. Записей и получил одинаковые результаты от каждого. Вы можете попытаться предоставить больше своего кода или предоставить некоторые свои данные. – Shane

+0

Кстати, я только что установил версию 0.1.9 в R.2.11.1 и имел аналогичные тайминги, как указано выше. – Shane

 Смежные вопросы

  • Нет связанных вопросов^_^