使用字典/列表来获取键列表

101

我有一个小问题:在R中没有找到字典数据结构,所以我使用了列表(类似于"单词"->数字)。那么,我如何获取键的列表。

7个回答

130

是的,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"
> 

21
+1 是对回答问题不提及 OP 无效方法的肯定。 - Marek
5
根据列表作为字典的代理时所需用途的不同,需要记住列表中的“键”查找时间复杂度为O(n),而不是像字典一样预期的O(1)(它使用哈希值进行键查找)。请注意这一点可能是明智的。 - egnha
5
是的,在 R 中可以使用 'environment' 类型来实现这个功能,但它不太常见或不太为人知。 - Dirk Eddelbuettel

64

如果你的“number”值都是相同类型的,那么甚至不需要使用列表。如果我拿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"

只有当你的值为混合模式(例如字符和数字)或向量时,才需要使用列表。

对于列表和向量,可以通过名称子集化单个元素:

> foo["tac"]
tac 
 22 

或者针对列表:

> foo[["tac"]]
[1] 22

1
你如何从这个类似字典的R结构foo中获取列表c(12,22,33)unlist(lapply(FUN=function(a){foo[[a]]},X = 1:length(foo)))非常不方便。有没有现成的函数可以实现这个功能?问题已经转移到这里 - hhh

20

在Calimo的回答基础上,我再补充一些关于在R中创建这种准字典的有用信息:

a) 如何返回字典中所有的值:

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

b) 检查字典是否包含关键字:

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

c) 如何向字典中添加新的键值对:

c(foo, tic2=44)

结果:

tic       tac       toe     tic2
12        22        33        44 

d) 如何满足真实字典的要求 - 键不能重复(唯一键)?您需要结合 b) 和 c) 并构建一个功能,该功能验证是否存在这样的键,并执行所需操作:例如不允许插入,如果新值与旧值不同,则更新值,或以某种方式重建键(例如添加一些数字使其唯一)

e) 如何按键从字典中删除键值对:

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


我可以添加包含空格的键吗,比如“奇怪的键”? - user1700890
还有像这样的东西不起作用 c(foo, tic2=NULL)。有什么解决方法吗? - user1700890

20

使用字典的原因在于性能。虽然使用命名向量和列表也可以完成任务,但问题在于随着数据增加,它们变得非常缓慢且占用大量内存。

然而,许多人不知道的是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秒!我理解哈希表没问题,但有人能否确认在列表中查找名称时使用了什么样的搜索算法?这只是在名称匹配下迭代遍历列表吗?我想确切地了解为什么列表如此缓慢,以及为什么哈希对于大量键来说如此快? - Phil
@vonjd 我正在尝试在R中使用字典,并找到了这个实现。但是,如果每个值都与一对键相关联,它是否也适用?提前感谢您。 - savi
@shana:你能举个例子来说明你的意思吗? - vonjd
@vonjd 当然,(name1,name2)->value,(name2,name9)->value。我正在寻找一种可以存储这样的配对(最多200个),并在logn时间内提取最低或最高值的数据结构。 - savi

11

现在可以使用hash包了: 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"

你如何添加多个值?我尝试了重复键,但它只存储最后一个值。我还尝试过分配列表,但它不起作用。 - skan
字典永远不会为一个键存储多个值。如果需要,可以将列表分配给一个键。 - BallpointBen
这是一个非常好的方法!它复制了字典具有的许多功能和设置,并且非常容易实现。 - Peter Maguire

8

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"

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"

etc.


我认为 as.numeric() 不是必要的。表格已经是数字类型了。你可以使用 names(t[order(t)]) 得到相同的结果。 - Rich Scriven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接