Come vectorize R strsplit?
-
27-09-2019 - |
Domanda
Durante la creazione di funzioni che uso strsplit
, ingressi vettore non si comportano come desiderato, e le esigenze sapply
da utilizzare. Ciò è dovuto all'uscita lista che strsplit
produce. C'è un modo vectorize processo - cioè, la funzione produce l'elemento corretto nell'elenco per ciascuno degli elementi di input
Ad esempio, per contare le lunghezze delle parole in un vettore di carattere:
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, qualcosa come length(strsplit(words,"")[[.]])
dove .
viene interpretato come essendo la parte rilevante del vettore di ingresso.
Soluzione
In generale, si dovrebbe cercare di utilizzare una funzione vettorializzare per cominciare. Utilizzando strsplit
frequentemente richiedono un certo tipo di iterazione successiva (che sarà più lenta), in modo da cercare di evitare se possibile. Nel tuo esempio, si dovrebbe usare nchar
invece:
> nchar(words)
[1] 1 5 5 3
Più in generale, approfittare del fatto che strsplit
restituisce una lista e l'uso lapply
:
> as.numeric(lapply(strsplit(words,""), length))
[1] 1 5 5 3
In alternativa all'uso altro una funzione di famiglia l*ply
da plyr
. Per esempio:
> laply(strsplit(words,""), length)
[1] 1 5 5 3
Modifica:
In onore di Bloomsday , ho deciso di testare le prestazioni di questi approcci con l'Ulisse di Joyce:
joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt")
joyce <- unlist(strsplit(joyce, " "))
Ora che ho tutte le parole, possiamo fare i nostri conteggi:
> # 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 funzione vettorializzare e lapply
sono notevolmente più veloce rispetto alla versione originale sapply
. Tutte le soluzioni restituiscono la stessa risposta (come si vede dalla uscita di sintesi).
A quanto pare l'ultima versione di plyr
è più veloce (questo sta usando una versione leggermente più vecchio).