Comment vectoriser R StrSplit?
-
27-09-2019 - |
Question
Lors de la création des fonctions que l'utilisation strsplit
, les entrées de vecteur ne se comportent pas comme on le souhaite, et les besoins de sapply
à utiliser. Cela est dû à la sortie de liste qui strsplit
produit. Est-il possible de vectoriser le processus - qui est la fonction produit l'élément correct dans la liste pour chacun des éléments de l'entrée
Par exemple, pour compter les longueurs de mots dans un vecteur de caractères:
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
Idéalement, quelque chose comme length(strsplit(words,"")[[.]])
où .
est interprété comme étant la partie pertinente du vecteur d'entrée.
La solution
En général, vous devriez essayer d'utiliser une fonction vectorisée pour commencer. L'utilisation strsplit
nécessitera souvent une sorte d'itération après (qui sera plus lent), alors essayez de l'éviter si possible. Dans votre exemple, vous devez utiliser à la place nchar
:
> nchar(words)
[1] 1 5 5 3
De manière plus générale, tirer profit du fait que strsplit
retourne une liste et de l'utilisation lapply
:
> as.numeric(lapply(strsplit(words,""), length))
[1] 1 5 5 3
Ou utilisez bien une fonction de la famille l*ply
de plyr
. Par exemple:
> laply(strsplit(words,""), length)
[1] 1 5 5 3
Edit:
En l'honneur de Bloomsday , j'ai décidé de tester les performances de ces approches utilisant Ulysse de Joyce:
joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt")
joyce <- unlist(strsplit(joyce, " "))
Maintenant que j'ai tous les mots, nous pouvons faire nos comptes:
> # 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 fonction vectorisé et lapply
sont considérablement plus rapide que la version originale sapply
. Toutes les solutions renvoient la même réponse (comme on le voit par la sortie de synthèse).
Apparemment, la dernière version de plyr
est plus rapide (ce qui utilise une version légèrement plus).