Trabalhando com dicionários / listas em R

89

Tenho uma pergunta trivial: não consegui encontrar uma estrutura de dados de dicionário em R, então usei lista (como "palavra" -> número). Portanto, agora estou com problemas para obter a lista de chaves. Alguém sabe?

Ivri
fonte

Respostas:

118

Sim, o listtipo é uma boa aproximação. Você pode usar names()em sua lista para definir e recuperar as 'chaves':

> 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
fonte
18
1 para responder a perguntas sem uma palavra sobre a abordagem ineficaz do OP.
Marek de
3
Dependendo do uso pretendido de uma lista como proxy para um dicionário, pode ser prudente ter em mente que a pesquisa de "chave" para listas é O (n) em vez de O (1), que é o que você esperaria um dicionário (que faz hash das chaves).
egnha
4
Sim, o environmenttipo é usado para isso em R, mas é menos comum / menos conhecido.
Dirk Eddelbuettel
56

Você nem mesmo precisa de listas se os seus valores "numéricos" forem todos do mesmo modo. Se eu pegar o exemplo 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"

As listas são necessárias apenas se seus valores forem de modo misto (por exemplo, caracteres e números) ou vetores.

Para listas e vetores, um elemento individual pode ser subdividido por nome:

> foo["tac"]
tac 
 22 

Ou para uma lista:

> foo[["tac"]]
[1] 22
Calimo
fonte
1
Como você pode obter a lista c(12,22,33)dessa estrutura R estilo dicionário foo? unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))é muito inconveniente. Alguma função pronta para isso? A questão foi movida para
hhh
18

Para estender um pouco a resposta de Calimo, apresento mais algumas coisas que você pode achar úteis ao criar este quase dicionários em R:

a) como retornar todos os VALORES do dicionário:

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

b) verifique se o dicionário CONTÉM CHAVE:

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

c) como ADICIONAR NOVA chave, par de valores ao dicionário:

c (foo, tic2 = 44)

resultados:

tic       tac       toe     tic2
12        22        33        44 

d) como cumprir o requisito do DICIONÁRIO REAL - que as teclas NÃO PODEM se repetir (TECLAS ÚNICAS)? Você precisa combinar b) ec) e construir a função que valida se existe tal chave, e fazer o que quiser: por exemplo, não permitir a inserção, atualizar o valor se o novo for diferente do antigo ou reconstruir de alguma forma a chave (por exemplo adiciona algum número para que seja único)

e) como EXCLUIR par POR CHAVE do dicionário:

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

andilabs
fonte
Posso adicionar uma chave que contenha espaços, algo como 'chave estranha'?
user1700890
Além disso, algo assim não funciona c(foo, tic2=NULL). Alguma solução?
user1700890
15

A razão de usar dicionários em primeiro lugar é o desempenho. Embora seja correto usar vetores e listas nomeados para a tarefa, o problema é que eles estão se tornando muito lentos e consomem muita memória com mais dados.

No entanto, o que muitas pessoas não sabem é que R tem de fato uma estrutura de dados de dicionário embutida: ambientes com a opçãohash = TRUE

Veja o exemplo a seguir para saber como fazer isso 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

Edit : Com base nesta resposta, escrevi uma postagem no blog com um pouco mais de contexto: http://blog.ephorie.de/hash-me-if-you-can

Vonjd
fonte
Isso funciona para relações multvalorizadas? Por exemplo tic = 1 e tic = 17
skan
@skan: Por que você não experimenta?
vonjd
Usar essa abordagem em vez de usar listas com nomes reduziu meu tempo de execução de 6 minutos para 1 segundo! Eu entendo hashes bem, mas alguém pode confirmar ao pesquisar um nome em uma lista que tipo de algoritmo de pesquisa é usado? Isso é apenas iteração pela lista sob as correspondências de nome? Eu gostaria de entender exatamente por que as listas são tão lentas e por que os hashes são tão rápidos para um grande número de chaves.
Phil
@vonjd Estou tentando usar o dicionário em R e encontrei esta implementação. No entanto, isso também funciona quando cada valor está associado a um par de chaves? Agradeço antecipadamente.
Savi,
@shana: Você poderia dar um exemplo do que você quis dizer exatamente?
vonjd
9

O hash do pacote já está disponível: https://cran.r-project.org/web/packages/hash/hash.pdf

Exemplos

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ũ
fonte
Como você pode adicionar vários valores? Tentei repetir a chave, mas ela armazena apenas o último valor. Também tentei atribuir listas, mas não funciona
skan
Os dicionários nunca armazenam vários valores por chave. Você pode atribuir uma lista a uma tecla, se desejar.
BallpointBen
7

Variação mais curta da resposta 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"
Urtiga
fonte
4

Vou apenas comentar que você também pode aproveitar muito tableao tentar "falsificar" um dicionário, por exemplo,

> 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
fonte
Não acho que as.numeric()seja necessário. A tabela já é numérica. Você pode obter o mesmo resultado comnames(t[order(t)])
Rich Scriven