26

我有两个向量,我想在 R 中创建一个列表,其中一个向量是键,另一个是值。我以为我会很容易地在我的书中找到答案或在谷歌上搜索,我期待找到一个解决方案,比如将名称添加到向量(names(v)<-names_vector),但我失败了。

我自己提出了两种可能的解决方案,但对我来说,它们都不优雅。R 不是我的主要编程语言,但我认为 R 如此务实应该存在更好的解决方案(类似于 list(keys=x, values=y))。

我的解决方案1:经典循环解决方案:

    > xx <- 1:3
    > yy <- letters1:3
    > zz =list()
    >for(i in 1:length(yy)) {zz[[yy[i]]]<-xx[i]}

我的解决方案 2:通过命名向量的间接路径:

    > names(xx) <- letters[1:3]
    > as.list(xx)

似乎我有一个解决方案,但我的向量有 100 万个或更多元素,我不仅担心编码风格(对我很重要),而且担心效率(但我不知道如何在 R 中进行分析)。有没有更合适的方法来做到这一点?使用命名矢量快捷方式是一种好习惯吗?

[[更新]] 我的应用程序,可能我过度简化了问题以使其可重现。我想为列表的元素命名。我首先尝试了 names() ,但似乎我做错了什么并且没有工作。所以我得到了一个错误的想法,即 names() 不适用于列表。但他们确实如接受的答案所示

4

5 回答 5

20

如果您的值都是标量,那么拥有一个只是向量的“键值存储”就没有错。

vals <- 1:1000000
keys <- paste0("key", 1:1000000)
names(vals) <- keys

然后,您可以检索与给定键对应的值

vals["key42"]
[1] 42

IIRC R 将散列用于基于字符的索引,因此无论向量的大小如何,查找都应该很快。

如果您的值可以是任意对象,那么您确实需要一个列表。

vals <- list(1:100, lm(speed ~ dist, data=cars), function(x) x^2)
names(vals) <- c("numbers", "model", "function")

sq <- vals[["function"]]
sq(5)
[1] 25

如果您的问题是关于构建列表,我不会太担心。R 内部是写时复制(对象只有在其内容被修改时才会被复制),所以做类似的事情

vals <- list(1:1000000, 1:1000000, <other big objects>)

实际上不会对所有内容进行额外的复制。

编辑:我刚刚检查过,如果你这样做,Rlst <- list(....)复制所有内容。去搞清楚。因此,如果您已经接近机器上的内存限制,这将不起作用。另一方面,如果你这样做names(lst) <- ....,它不会制作另一个副本lst. 再上图。

于 2013-07-06T15:29:57.980 回答
18

可以使用以下语句在一个语句中完成setNames

xx <- 1:3
yy <- letters[1:3]

创建命名列表:

as.list(setNames(xx, yy))
# $a
# [1] 1
# 
# $b
# [1] 2
# 
# $c
# [1] 3

或命名向量:

setNames(xx, yy)
# a b c 
# 1 2 3

在列表的情况下,这在编程上等同于您的“命名向量”方法,但可能更优雅一些。


以下是一些基准,表明这两种方法同样快。另请注意,操作顺序对于避免不必要且昂贵的数据副本非常重要:

f1 <- function(xx, yy) {
  names(xx) <- yy
  as.list(xx)
}

f2 <- function(xx, yy) {
  out <- as.list(xx)
  names(out) <- yy
  out
}

f3 <- function(xx, yy) as.list(setNames(xx, yy))
f4 <- function(xx, yy) setNames(as.list(xx), yy)

library(microbenchmark)
microbenchmark(
  f1(xx, yy),
  f2(xx, yy),
  f3(xx, yy),
  f4(xx, yy)
)
# Unit: microseconds
#        expr    min      lq  median      uq     max neval
#  f1(xx, yy) 41.207 42.6390 43.2885 45.7340 114.853   100
#  f2(xx, yy) 39.187 40.3525 41.5330 43.7435 107.130   100
#  f3(xx, yy) 39.280 41.2900 42.1450 43.8085 109.017   100
#  f4(xx, yy) 76.278 78.1340 79.1450 80.7525 180.825   100
于 2013-07-06T15:55:27.740 回答
5

这里另一个严肃的选择是使用data.table. 它使用键对结构进行排序,特别是当您有大量数字时,访问元素非常快。这里有一个例子:

library(data.table)   
DT <- data.table(xx = 1:1e6, 
             k = paste0("key", 1:1e6),key="k")

Dt 是具有 2 列的 data.table ,我将列 k 设置为键。DT xx k 1: 1 key1 2: 10 key10 3: 100 key100 4: 1000 key1000 5: 10000 key10000 ---
999996: 999995 key999995 999997: 999996 key999996 999998: 999997 key999997 999999: 999998 key999998 1000000: 999999 key999999

现在我可以使用如下键访问我的 data.table:

DT['key1000']
         k   xx
1: key1000 1000

这是将 data.table 解决方案与命名向量进行比较的基准测试:

vals <- 1:1000000
DT <- data.table(xx = vals ,
                 k = paste0("key", vals),key="k")
keys <- paste0("key", vals)
names(vals) <- keys
library(microbenchmark)
microbenchmark( vals["key42"],DT["key42"],times=100)

Unit: microseconds
          expr        min          lq     median         uq        max neval
 vals["key42"] 111938.692 113207.4945 114924.010 130010.832 361077.210   100
   DT["key42"]    768.753    797.0085   1055.661   1067.987   2058.985   100
于 2013-07-06T15:51:04.103 回答
3

你的意思是这样做吗?...

xx <- 1:3
yy <- letters[1:3]
zz <- list( xx , yy )
names(zz) <- c("keys" , "values")
zz
#$keys
#[1] 1 2 3

#$values
#[1] "a" "b" "c"

AFAIK 这是制作向量列表的规范方法。我很高兴得到纠正。如果您是 R 新手,我建议使用循环通常for是不明智的,因为通常有矢量化方法来完成大多数更高效、更快的任务。

于 2013-07-06T15:19:35.500 回答
0

洪的输出是错误的。

应该使用 vals[["key42"]]

> vals[["key42"]]
[1] 42

vals <- 1:1000000
keys <- paste0("key", 1:1000000)
names(vals) <- keys

vals["key42"]
key42
   42
于 2017-03-30T06:03:55.043 回答