Trabajar con diccionarios / listas en R

93

Tengo una pregunta trivial: no pude encontrar una estructura de datos de diccionario en R, así que usé lista en su lugar (como "palabra" -> número) Entonces, ahora mismo tengo un problema sobre cómo obtener la lista de claves. ¿Cualquiera sabe?

Ivri
fuente

Respuestas:

121

Sí, el listtipo es una buena aproximación. Puede utilizar names()en su lista para configurar y recuperar las 'claves':

> 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
fuente
18
+1 por responder la pregunta sin decir una palabra sobre el enfoque ineficaz de OP.
Marek
3
Dependiendo del uso previsto de una lista como proxy de un diccionario, puede ser prudente tener en cuenta que la búsqueda "clave" para las listas es O (n) en lugar de O (1), que es lo que esperaría. un diccionario (que utiliza claves hash).
egnha
4
Sí, el environmenttipo se usa para eso en R, pero es menos común / menos conocido.
Dirk Eddelbuettel
58

Ni siquiera necesita listas si sus valores "numéricos" son todos del mismo modo. Si tomo el ejemplo 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"

Las listas solo son necesarias si sus valores son de modo mixto (por ejemplo, caracteres y números) o vectores.

Tanto para listas como para vectores, un elemento individual se puede subdividir por nombre:

> foo["tac"]
tac 
 22 

O para una lista:

> foo[["tac"]]
[1] 22
Calimo
fuente
1
¿Cómo puede obtener la lista c(12,22,33)de esta estructura R de estilo diccionario foo? unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))es muy inconveniente. ¿Alguna función lista para esto? Movido la pregunta aquí
hhh
18

Para extender un poco la respuesta de Calimo, presento algunas cosas más que pueden resultarle útiles al crear estos cuasi diccionarios en R:

a) cómo devolver todos los VALORES del diccionario:

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

b) compruebe si el diccionario CONTIENE CLAVE:

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

c) cómo AÑADIR NUEVA clave, par de valores al diccionario:

c (foo, tic2 = 44)

resultados:

tic       tac       toe     tic2
12        22        33        44 

d) ¿Cómo cumplir con el requisito del DICCIONARIO REAL - que las teclas NO PUEDEN repetirse (LLAVES ÚNICAS)? Necesita combinar b) yc) y construir la función que valida si existe dicha clave, y hacer lo que quiera: por ejemplo, no permitir la inserción, actualizar el valor si el nuevo difiere del anterior o reconstruir de alguna manera la clave (por ejemplo, agrega un número para que sea único)

e) cómo BORRAR par POR CLAVE del diccionario:

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

andilabs
fuente
¿Puedo agregar una clave que contenga espacios, algo como 'clave extraña'?
user1700890
Además, algo como esto no funciona c(foo, tic2=NULL). ¿Alguna solución?
user1700890
15

La razón para usar diccionarios en primer lugar es el rendimiento. Aunque es correcto que se puedan utilizar listas y vectores con nombre para la tarea, el problema es que se están volviendo bastante lentos y con mucha memoria con más datos.

Sin embargo, lo que mucha gente no sabe es que R tiene una estructura de datos de diccionario incorporada: entornos con la opciónhash = TRUE

Vea el siguiente ejemplo para saber cómo hacerlo funcionar:

# 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

Editar : sobre la base de esta respuesta, escribí una publicación de blog con más contexto: http://blog.ephorie.de/hash-me-if-you-can

vonjd
fuente
¿Funciona para relaciones de valores múltiples? Por ejemplo tic = 1 y tic = 17
skan
@skan: ¿Por qué no lo pruebas?
vonjd
¡Usar este enfoque en lugar de usar listas con nombres redujo mi tiempo de ejecución de 6 minutos a 1 segundo! Entiendo bien los hashes, pero ¿alguien puede confirmar al buscar un nombre en una lista qué tipo de algoritmo de búsqueda se utiliza? ¿Es esto simplemente iterando a través de la lista bajo el nombre de coincidencias? Me gustaría entender exactamente por qué las listas son tan lentas, así como por qué los hash son tan rápidos para una gran cantidad de claves.
Phil
@vonjd Estoy tratando de usar el diccionario en R y encontré esta implementación. Sin embargo, ¿también funciona cuando cada valor está asociado con un par de claves? Gracias de antemano.
sábado
@shana: ¿Podría dar un ejemplo de lo que quiere decir exactamente?
vonjd
9

El hash del paquete ahora está disponible: https://cran.r-project.org/web/packages/hash/hash.pdf

Ejemplos

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ũ
fuente
¿Cómo se pueden agregar varios valores? Intenté repetir la clave pero solo almacena el último valor. También intenté asignar listas pero no funciona
skan
Los diccionarios nunca almacenan varios valores por clave. Puede asignar una lista a una tecla si lo desea.
BallpointBen
7

Variación más corta de la respuesta 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"
Ortiga
fuente
4

Solo comentaré que puedes aprovechar mucho tablecuando intentas "falsificar" un diccionario también, por ejemplo

> 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
fuente
No creo que as.numeric()sea ​​necesario. La tabla ya es numérica. Puede obtener el mismo resultado connames(t[order(t)])
Rich Scriven