Praca ze słownikami / listami w R

93

Mam trywialne pytanie: nie mogłem znaleźć struktury danych słownika w R, więc zamiast tego użyłem listy (np. „Słowo” -> liczba). W tej chwili mam problem ze znalezieniem listy kluczy. Czy ktoś wie?

Ivri
źródło

Odpowiedzi:

121

Tak, listtyp jest dobrym przybliżeniem. Możesz użyć names()na swojej liście, aby ustawić i pobrać `` klucze '':

> 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
źródło
18
+1 za odpowiedź na pytanie bez słowa o nieefektywnym podejściu PO.
Marek
3
W zależności od zamierzonego wykorzystania listy jako serwera proxy dla słownika, rozsądnie byłoby pamiętać, że wyszukiwanie „klucza” dla list to O (n) zamiast O (1), czego można się spodziewać słownik (haszujący klucze).
egnha
4
Tak, environmenttyp jest używany do tego w R, ale jest mniej powszechny / mniej znany.
Dirk Eddelbuettel
58

Nie potrzebujesz nawet list, jeśli wszystkie wartości „liczb” są w tym samym trybie. Jeśli wezmę przykład Dirka Eddelbuettela:

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

Listy są wymagane tylko wtedy, gdy wartości są albo w trybie mieszanym (na przykład znaki i cyfry), albo w wektorach.

Zarówno w przypadku list, jak i wektorów, pojedynczy element można podzielić według nazwy:

> foo["tac"]
tac 
 22 

Lub listę:

> foo[["tac"]]
[1] 22
Calimo
źródło
1
Jak uzyskać listę c(12,22,33)tej struktury słownikowej R foo? unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))jest bardzo niewygodne. Jakaś gotowa funkcja do tego? Przeniosłem pytanie tutaj
hhh,
18

Aby nieco rozszerzyć odpowiedź Calimo, przedstawiam jeszcze kilka rzeczy, które mogą się przydać podczas tworzenia tych quasi słowników w R:

a) jak zwrócić wszystkie WARTOŚCI słownika:

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

b) sprawdź, czy słownik ZAWIERA KLUCZ:

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

c) jak DODAĆ NOWY klucz, parę wartości do słownika:

c (foo, tic2 = 44)

wyniki:

tic       tac       toe     tic2
12        22        33        44 

d) jak spełnić wymóg PRAWDZIWEGO SŁOWNIKA - aby klucze NIE MOGły się powtarzać (UNIKALNE KLUCZE)? Musisz połączyć b) ic) i zbudować funkcję, która sprawdza, czy istnieje taki klucz i robić co chcesz: np. Nie zezwalaj na wstawianie, aktualizuj wartość, jeśli nowy różni się od starego, lub przebudowuj jakoś klucz (np. dodaje do niego pewną liczbę, więc jest unikalna)

e) jak USUNĄĆ parę WEDŁUG KLUCZA ze słownika:

foo <-foo [które (foo! = foo [["tac"]])]

andilabs
źródło
Czy mogę dodać klucz zawierający spacje, coś w rodzaju „dziwnego klucza”?
user1700890
Coś takiego też nie działa c(foo, tic2=NULL). Jakieś obejście?
user1700890
15

Powodem używania słowników w pierwszej kolejności jest wydajność. Chociaż prawdą jest, że możesz używać nazwanych wektorów i list do zadania, problem polega na tym, że stają się one dość powolne i wymagają większej ilości pamięci z większą ilością danych.

Jednak wielu ludzi nie wie, że R ma rzeczywiście wbudowaną strukturę danych słownikowych: środowiska z opcjąhash = TRUE

Zobacz poniższy przykład, jak to działa:

# 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

Edycja : Na podstawie tej odpowiedzi napisałem post na blogu z większym kontekstem: http://blog.ephorie.de/hash-me-if-you-can

vonjd
źródło
Czy to działa w przypadku relacji wielowartościowych? Na przykład tic = 1 i tic = 17
skan
@skan: Dlaczego tego nie wypróbujesz?
vonjd
Zastosowanie tego podejścia zamiast używania list z nazwami skróciło mój czas działania z 6 minut do 1 sekundy! Rozumiem skróty, ale czy ktoś może potwierdzić, wyszukując nazwisko na liście, jakiego rodzaju algo wyszukiwania jest używane? Czy to tylko powtarzanie listy pod zgodnymi nazwami? Chciałbym dokładnie zrozumieć, dlaczego listy są tak wolne, a także dlaczego skróty są tak szybkie dla dużej liczby kluczy?
Phil
@vonjd Próbuję użyć słownika w R i znalazłem tę implementację. Jednak czy działa to również, gdy każda wartość jest skojarzona z parą kluczy? Z góry dziękuję.
savi
@shana: Czy możesz podać przykład, co dokładnie masz na myśli?
vonjd
9

Skrót pakietu jest już dostępny: https://cran.r-project.org/web/packages/hash/hash.pdf

Przykłady

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ũ
źródło
Jak możesz dodać wiele wartości? Próbowałem powtórzyć klucz, ale przechowuje tylko ostatnią wartość. Próbowałem też przypisywać listy, ale to nie działa
skan
Słowniki nigdy nie przechowują wielu wartości na klucz. Jeśli chcesz, możesz przypisać listę do klawisza.
BallpointBen
7

Krótsza odmiana odpowiedzi Dirka:

# 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"
Pokrzywa
źródło
4

Tylko skomentuję, że tablepróbując „sfałszować” słownik również można uzyskać dużo kilometrów , np

> 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"

itp.

Gabriel Perdue
źródło
Myślę, że nie as.numeric()jest to konieczne. Tabela jest już numeryczna. Możesz uzyskać ten sam wynik dziękinames(t[order(t)])
Rich Scriven