Travailler avec des dictionnaires / listes dans R

89

J'ai une question triviale: je n'ai pas pu trouver une structure de données de dictionnaire dans R, donc j'ai utilisé la liste à la place (comme "mot" -> nombre) Donc, en ce moment, j'ai un problème pour obtenir la liste des clés. Quelqu'un le sait?

Ivri
la source

Réponses:

118

Oui, le listtype est une bonne approximation. Vous pouvez utiliser names()sur votre liste pour définir et récupérer les 'clés':

> foo <- vector(mode="list", length=3)
> names(foo) <- c("tic", "tac", "toe")
> foo[[1]] <- 12; foo[[2]] <- 22; foo[[3]] <- 33
> foo
$tic
[1] 12

$tac
[1] 22

$toe
[1] 33

> names(foo)
[1] "tic" "tac" "toe"
> 
Dirk Eddelbuettel
la source
18
+1 pour répondre à la question sans un mot sur l'approche inefficace de l'OP.
Marek
3
En fonction de l'utilisation prévue d'une liste comme proxy pour un dictionnaire, il peut être prudent de garder à l'esprit que la recherche «clé» pour les listes est O (n) plutôt que O (1), ce à quoi vous vous attendez un dictionnaire (qui hache les clés).
egnha
4
Oui, le environmenttype est utilisé pour cela dans R, mais il est moins courant / moins connu.
Dirk Eddelbuettel
56

Vous n'avez même pas besoin de listes si vos valeurs «numériques» sont toutes du même mode. Si je prends l'exemple de Dirk Eddelbuettel:

> foo <- c(12, 22, 33)
> names(foo) <- c("tic", "tac", "toe")
> foo
tic tac toe
 12  22  33
> names(foo)
[1] "tic" "tac" "toe"

Les listes ne sont requises que si vos valeurs sont soit en mode mixte (par exemple, caractères et nombres), soit en vecteurs.

Pour les listes et les vecteurs, un élément individuel peut être sous-défini par son nom:

> foo["tac"]
tac 
 22 

Ou pour une liste:

> foo[["tac"]]
[1] 22
Calimo
la source
1
Comment pouvez-vous obtenir la liste c(12,22,33)de cette structure R de style dictionnaire foo? unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))est très gênant. Une fonction prête pour cela? Déplacement de la question ici
hhh
18

Pour étendre un peu la réponse de Calimo, je présente quelques autres choses que vous pourriez trouver utiles lors de la création de ce quasi dictionnaires en R:

a) comment retourner toutes les VALEURS du dictionnaire:

>as.numeric(foo)
[1] 12 22 33

b) vérifier si le dictionnaire CONTIENT UNE CLÉ:

>'tic' %in% names(foo)
[1] TRUE

c) comment ajouter une nouvelle clé, une paire de valeurs au dictionnaire:

c (toto, tic2 = 44)

résultats:

tic       tac       toe     tic2
12        22        33        44 

d) comment remplir l'exigence du VRAI DICTIONNAIRE - que les clés NE PEUVENT PAS se répéter (CLÉS UNIQUES)? Vous devez combiner b) et c) et construire une fonction qui valide s'il existe une telle clé, et faire ce que vous voulez: par exemple, ne pas autoriser l'insertion, mettre à jour la valeur si la nouvelle est différente de l'ancienne, ou reconstruire d'une manière ou d'une autre la clé (par exemple ajoute un certain nombre pour qu'il soit unique)

e) comment SUPPRIMER la paire PAR CLÉ du dictionnaire:

foo <-foo [qui (foo! = foo [["tac"]])]

andilabs
la source
Puis-je ajouter une clé qui contient des espaces, quelque chose comme «clé étrange»?
user1700890
Aussi quelque chose comme ça ne fonctionne pas c(foo, tic2=NULL). Un travail autour?
user1700890
15

La raison de l'utilisation des dictionnaires en premier lieu est la performance. Bien qu'il soit correct que vous puissiez utiliser des vecteurs et des listes nommés pour la tâche, le problème est qu'ils deviennent assez lents et gourmands en mémoire avec plus de données.

Pourtant, ce que beaucoup de gens ne savent pas, c'est que R a en effet une structure de données de dictionnaire intégrée: des environnements avec l'optionhash = TRUE

Consultez l'exemple suivant pour savoir comment le faire fonctionner:

# vectorize assign, get and exists for convenience
assign_hash <- Vectorize(assign, vectorize.args = c("x", "value"))
get_hash <- Vectorize(get, vectorize.args = "x")
exists_hash <- Vectorize(exists, vectorize.args = "x")

# keys and values
key<- c("tic", "tac", "toe")
value <- c(1, 22, 333)

# initialize hash
hash = new.env(hash = TRUE, parent = emptyenv(), size = 100L)
# assign values to keys
assign_hash(key, value, hash)
## tic tac toe 
##   1  22 333
# get values for keys
get_hash(c("toe", "tic"), hash)
## toe tic 
## 333   1
# alternatively:
mget(c("toe", "tic"), hash)
## $toe
## [1] 333
## 
## $tic
## [1] 1
# show all keys
ls(hash)
## [1] "tac" "tic" "toe"
# show all keys with values
get_hash(ls(hash), hash)
## tac tic toe 
##  22   1 333
# remove key-value pairs
rm(list = c("toe", "tic"), envir = hash)
get_hash(ls(hash), hash)
## tac 
##  22
# check if keys are in hash
exists_hash(c("tac", "nothere"), hash)
##     tac nothere 
##    TRUE   FALSE
# for single keys this is also possible:
# show value for single key
hash[["tac"]]
## [1] 22
# create new key-value pair
hash[["test"]] <- 1234
get_hash(ls(hash), hash)
##  tac test 
##   22 1234
# update single value
hash[["test"]] <- 54321
get_hash(ls(hash), hash)
##   tac  test 
##    22 54321

Edit : Sur la base de cette réponse, j'ai écrit un article de blog avec un peu plus de contexte: http://blog.ephorie.de/hash-me-if-you-can

vonjd
la source
Cela fonctionne-t-il pour les relations à plusieurs valeurs? Par exemple tic = 1 et tic = 17
skan
@skan: Pourquoi ne pas l'essayer?
vonjd
L'utilisation de cette approche au lieu d'utiliser des listes avec des noms a réduit mon temps de fonctionnement de 6 minutes à 1 seconde! Je comprends bien les hachages, mais est-ce que quelqu'un peut confirmer lors de la recherche d'un nom dans une liste quel type d'algo de recherche est utilisé? Est-ce juste une itération dans la liste sous le nom correspond? J'aimerais comprendre exactement pourquoi les listes sont si lentes, ainsi que pourquoi les hachages sont si rapides pour un grand nombre de clés?
Phil
@vonjd J'essaie d'utiliser le dictionnaire en R et j'ai trouvé cette implémentation. Cependant, cela fonctionne-t-il également lorsque chaque valeur est associée à une paire de clés? Merci d'avance.
savi
@shana: Pouvez-vous donner un exemple de ce que vous entendez exactement?
vonjd
9

Le hachage du package est maintenant disponible: https://cran.r-project.org/web/packages/hash/hash.pdf

Exemples

h <- hash( keys=letters, values=1:26 )
h <- hash( letters, 1:26 )
h$a
# [1] 1
h$foo <- "bar"
h[ "foo" ]
# <hash> containing 1 key-value pair(s).
#   foo : bar
h[[ "foo" ]]
# [1] "bar"
Ngọc Linh Vũ
la source
Comment pouvez-vous ajouter plusieurs valeurs? J'ai essayé de répéter la clé, mais elle ne stocke que la dernière valeur. J'ai aussi essayé d'attribuer des listes mais cela ne fonctionne pas
skan
Les dictionnaires ne stockent jamais plusieurs valeurs par clé. Vous pouvez affecter une liste à une clé si vous le souhaitez.
BallpointBen
7

Variation plus courte de la réponse de Dirk:

# Create a Color Palette Dictionary 
> color <- c('navy.blue', 'gold', 'dark.gray')
> hex <- c('#336A91', '#F3C117', '#7F7F7F')

> # Create List
> color_palette <- as.list(hex)
> # Name List Items
> names(color_palette) <- color
> 
> color_palette
$navy.blue
[1] "#336A91"

$gold
[1] "#F3C117"

$dark.gray
[1] "#7F7F7F"
Ortie
la source
4

Je vais juste commenter que vous pouvez gagner beaucoup de temps tablelorsque vous essayez de "falsifier" un dictionnaire également, par exemple

> x <- c("a","a","b","b","b","c")
> (t <- table(x))
x
a b c 
2 3 1 
> names(t)
[1] "a" "b" "c"
> o <- order(as.numeric(t))
> names(t[o])
[1] "c" "a" "b"

etc.

Gabriel Perdue
la source
Je ne pense pas que ce as.numeric()soit nécessaire. Le tableau est déjà numérique. Vous pouvez obtenir le même résultat avecnames(t[order(t)])
Rich Scriven