Робота зі словниками / списками в R


93

У мене є тривіальне запитання: я не зміг знайти структуру даних словника в R, тому замість цього використав список (наприклад, "слово" -> число) Отже, зараз у мене проблема, як отримати список ключів. Хтось знає?

Відповіді:


120

Так, 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, але він менш поширений / менш відомий.
Дірк Еддельбюттель

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 [which (foo! = foo [["tac"]])]


Чи можу я додати ключ, який містить пробіли, щось на зразок "дивного ключа"?
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


Чи працює це для багатозначних відносин? Наприклад tic = 1 і tic = 17
skan

@skan: Чому б вам не спробувати?
vonjd

Використовуючи цей підхід замість використання списків з іменами, мій час роботи зменшився з 6 хвилин до 1 секунди! Я добре розумію хеші, але чи може хтось підтвердити, шукаючи ім’я у списку, який тип пошуку algo використовується? Це просто перебирання списку під іменами збігів? Я хотів би зрозуміти, чому списки так повільні, а також чому хеші так швидкі для великої кількості клавіш?
Філ

@vonjd Я намагаюся використовувати словник на R і знайшов цю реалізацію. Однак чи працює це також, коли кожне значення пов'язане з парою ключів? Спасибі заздалегідь.
savi

@shana: Не могли б ви навести приклад того, що ви маєте на увазі?
фондж

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"

Як можна додати кілька значень? Я спробував повторити ключ, але він зберігає лише останнє значення. Я також пробував призначати списки, але це не працює
скан

Словники ніколи не зберігають кілька значень для ключа. Ви можете призначити список клавіші, якщо хочете.
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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.