web-dev-qa-db-fra.com

trouver des valeurs uniques dans une liste

Supposons que vous ayez une liste de valeurs

x <- list(a=c(1,2,3), b = c(2,3,4), c=c(4,5,6))

Je voudrais trouver des valeurs uniques à partir de tous les éléments de liste combinés. Jusqu'à présent, le code suivant a fait l'affaire

unique(unlist(x))

Quelqu'un connaît-il un moyen plus efficace? J'ai une liste volumineuse avec beaucoup de valeurs et j'apprécierais toute accélération.

27
Roman Luštrik

Cette solution suggérée par Marek est la meilleure réponse au Q. original. Voir ci-dessous pour une discussion sur d'autres approches et pourquoi Marek est la plus utile.

> unique(unlist(x, use.names = FALSE))
[1] 1 2 3 4 5 6

Discussion

Une solution plus rapide consiste à calculer unique() sur les composants de votre x d'abord, puis à faire une unique() finale sur ces résultats. Cela ne fonctionnera que si les composants de la liste ont le même nombre de valeurs uniques, comme ils le font dans les deux exemples ci-dessous. Par exemple.:

D'abord votre version, puis ma double approche unique:

> unique(unlist(x))
[1] 1 2 3 4 5 6
> unique.default(sapply(x, unique))
[1] 1 2 3 4 5 6

Nous devons appeler unique.default Car il existe une méthode matrix pour unique qui maintient une marge fixe; c'est très bien car une matrice peut être traitée comme un vecteur.

Marek, dans les commentaires de cette réponse, note que la vitesse lente de l'approche unlist est potentiellement due au names sur la liste. La solution de Marek consiste à utiliser l'argument use.names Pour unlist, ce qui, s'il est utilisé, se traduit par une solution plus rapide que la version unique double ci-dessus. Pour le simple x du post de Roman, nous obtenons

> unique(unlist(x, use.names = FALSE))
[1] 1 2 3 4 5 6

La solution de Marek fonctionnera même lorsque le nombre d'éléments uniques diffère entre les composants.

Voici un exemple plus grand avec quelques timings des trois méthodes:

## Create a large list (1000 components of length 100 each)
DF <- as.list(data.frame(matrix(sample(1:10, 1000*1000, replace = TRUE), 
                                ncol = 1000)))

Voici les résultats des deux approches utilisant DF:

> ## Do the three approaches give the same result:
> all.equal(unique.default(sapply(DF, unique)), unique(unlist(DF)))
[1] TRUE
> all.equal(unique(unlist(DF, use.names = FALSE)), unique(unlist(DF)))
[1] TRUE
> ## Timing Roman's original:
> system.time(replicate(10, unique(unlist(DF))))
   user  system elapsed 
  12.884   0.077  12.966
> ## Timing double unique version:
> system.time(replicate(10, unique.default(sapply(DF, unique))))
   user  system elapsed 
  0.648   0.000   0.653
> ## timing of Marek's solution:
> system.time(replicate(10, unique(unlist(DF, use.names = FALSE))))
   user  system elapsed 
  0.510   0.000   0.512

Ce qui montre que le double unique est beaucoup plus rapide pour appliquer unique() aux composants individuels puis unique() ces petits ensembles de valeurs uniques, mais cette accélération est uniquement à cause du names sur la liste DF. Si nous disons à unlist de ne pas utiliser le names, la solution de Marek est légèrement plus rapide que le double unique pour ce problème. Comme la solution de Marek utilise correctement l'outil approprié, et c'est plus rapide que la solution de contournement, c'est la solution préférée.

Le gros problème avec l'approche double unique est qu'il ne fonctionnera que si, comme dans les deux exemples ici, chaque composant de la liste d'entrée (DF ou x) a le même nombre de valeurs uniques. Dans de tels cas, sapply simplifie le résultat en une matrice qui nous permet d'appliquer unique.default. Si les composants de la liste d'entrée ont des nombres différents de valeurs uniques, la solution unique double échouera.

41
Gavin Simpson