2010-06-16 9 views
10

Al crear funciones que usan strsplit, las entradas de vectores no se comportan como se desee y se debe usar sapply. Esto se debe a la salida de lista que produce strsplit. ¿Hay alguna manera de vectorizar el proceso, es decir, la función produce el elemento correcto en la lista para cada uno de los elementos de la entrada?Cómo vectorizar R strsplit?

Por ejemplo, para contar las longitudes de las palabras en un vector de caracteres:

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 

Idealmente, algo así como length(strsplit(words,"")[[.]])., donde se interpreta como el ser la parte correspondiente del vector de entrada.

Respuesta

19

En general, debe intentar usar una función vectorizada para comenzar. El uso de strsplit requerirá con frecuencia algún tipo de iteración después (que será más lento), así que trate de evitarlo si es posible. En su ejemplo, debe utilizar nchar lugar:

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

De manera más general, aprovechar el hecho de que strsplit devuelve una lista y utilizar lapply:

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

O bien utilizar una función l*ply familia de plyr . Por ejemplo:

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

Editar:

En honor a Bloomsday, decidí probar el rendimiento de estos enfoques utilizando el Ulises de Joyce:

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

Ahora que tengo todas las palabras , podemos hacer nuestro conteo:

> # 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 

La función vectorizada y lapply son considerablemente más rápidas que la versión original sapply. Todas las soluciones devuelven la misma respuesta (como se ve en el resultado del resumen).

Aparentemente, la última versión de plyr es más rápida (esta es una versión ligeramente anterior).

+0

Gracias Shane, pero no obtengo los mismos resultados de lo que estoy haciendo. Es una implementación del esquema de verificación de números de Verhoeff. Modifiqué mi función para que fuera compatible con las implementaciones anteriores, pero con una entrada de un vector de 100.000 de largo, obtengo una lista de 8 elementos del primero y un vector de 8 elementos del segundo (8 es el más longitud probable de los elementos del vector). – James

+0

@James: Entonces me imagino que debe haber algo más con tu función. Como puede ver arriba, acabo de probar esto en un vector con más de 270k registros y obtuve los mismos resultados en cada uno. Puede tratar de proporcionar más de su código o proporcionar algunos de sus datos. – Shane

+0

A propósito, acabo de instalar la versión 0.1.9 de Plyr en R 2.11.1 y tuve tiempos similares a los anteriores. – Shane

Cuestiones relacionadas