Работа со словарями / списками в R

93

У меня тривиальный вопрос: мне не удалось найти структуру данных словаря в R, поэтому я использовал вместо этого список (например, «слово» -> число). Итак, прямо сейчас у меня проблема, как получить список ключей. Кто-нибудь знает?

Иври
источник

Ответы:

121

Да, listтип - хорошее приближение. Вы можете использовать names()в своем списке для установки и получения «ключей»:

> 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"
> 
Дирк Эддельбюттель
источник
18
+1 за безмолвный ответ на вопрос о неэффективном подходе ОП.
Марек
3
В зависимости от предполагаемого использования списка в качестве прокси для словаря, было бы разумно иметь в виду, что "ключевой" поиск для списков - это O (n), а не O (1), что и следовало ожидать. словарь (который хеширует ключи).
egnha
4
Да, этот environmentтип используется в R, но он менее распространен / менее известен.
Dirk Eddelbuettel
58

Вам даже не нужны списки, если все ваши «числовые» значения относятся к одному и тому же режиму. Если я возьму пример Дирка Эддельбюттеля:

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

Списки требуются только в том случае, если ваши значения являются либо смешанными (например, символы и числа), либо векторами.

И для списков, и для векторов отдельный элемент может быть разбит на подмножество по имени:

> foo["tac"]
tac 
 22 

Или для списка:

> foo[["tac"]]
[1] 22
Калимо
источник
1
Как получить список c(12,22,33)этой структуры R в стиле словаря foo? unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))очень неудобно. Есть готовая функция для этого? Переехал вопрос здесь
ххх
18

Чтобы немного расширить ответ Calimo, я представляю еще несколько вещей, которые могут оказаться полезными при создании этих квази-словарей в R:

а) как вернуть все ЗНАЧЕНИЯ словаря:

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

б) проверьте, СОДЕРЖИТ ли словарь КЛЮЧ:

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

в) как ДОБАВИТЬ НОВЫЙ ключ, пару значений в словарь:

c (foo, tic2 = 44)

полученные результаты:

tic       tac       toe     tic2
12        22        33        44 

г) как выполнить требование НАСТОЯЩЕГО СЛОВАРА - что ключи НЕ МОГУТ повторяться (УНИКАЛЬНЫЕ КЛЮЧИ)? Вам нужно объединить b) и c) и функцию сборки, которая проверяет, существует ли такой ключ, и делать то, что вы хотите: например, не разрешать вставку, обновлять значение, если новое отличается от старого, или перестраивать каким-то образом ключ (например, добавляет к нему некоторое число, чтобы оно было уникальным)

д) как УДАЛИТЬ пару ПО КЛЮЧУ из словаря:

foo <-foo [который (foo! = foo [["tac"]])]

Andilabs
источник
Могу ли я добавить ключ, содержащий пробелы, что-то вроде «странного ключа»?
user1700890
Тоже как-то так не получается c(foo, tic2=NULL). Любая работа?
user1700890
15

Причина использования словарей в первую очередь - это производительность. Хотя это правильно, что вы можете использовать именованные векторы и списки для задачи, проблема в том, что они становятся довольно медленными и требуют памяти с большим количеством данных.

Однако многие люди не знают, что R действительно имеет встроенную структуру данных словаря: среды с параметромhash = TRUE

См. Следующий пример, как заставить его работать:

# 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

Изменить : на основе этого ответа я написал сообщение в блоге с дополнительным контекстом: http://blog.ephorie.de/hash-me-if-you-can

vonjd
источник
Работает ли это для многозначных отношений? Например tic = 1 и tic = 17
skan
@skan: Почему бы тебе не попробовать?
vonjd
Использование этого подхода вместо использования списков с именами сократило время работы с 6 минут до 1 секунды! Я прекрасно понимаю хеши, но может ли кто-нибудь подтвердить при поиске имени в списке, какой алгоритм поиска используется? Это просто итерация по списку под совпадениями имен? Я хотел бы точно понять, почему списки такие медленные, а также почему хеши такие быстрые для большого количества ключей?
Фил
@vonjd Я пытаюсь использовать словарь в R и нашел эту реализацию. Однако работает ли это также, когда каждое значение связано с парой ключей? Заранее спасибо.
Сави
@shana: Не могли бы вы привести пример того, что именно вы имеете в виду?
vonjd
9

Теперь доступен хэш пакета : https://cran.r-project.org/web/packages/hash/hash.pdf

Примеры

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ũ
источник
Как можно добавить несколько значений? Я пробовал повторить ключ, но он сохраняет только последнее значение. Я тоже пробовал назначать списки, но это не работает
skan
Словари никогда не хранят несколько значений для каждого ключа. При желании вы можете присвоить клавише список.
BallpointBen
7

Более короткий вариант ответа Дирка:

# 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"
Крапива
источник
4

Я просто прокомментирую, что вы можете сэкономить много времени, tableпытаясь "подделать" словарь, например

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

и т.п.

Габриэль Пердью
источник
Не думаю, что as.numeric()это необходимо. Таблица уже числовая. Вы можете получить тот же результат сnames(t[order(t)])
Rich Scriven