Как создать список из R из двух векторов (один из них будет ключ, а другой - значения)?
У меня есть два вектора, и я хочу создать список в R, где один вектор - это ключи, а другие значения. Я думал, что я легко найду ответ в своих книгах или googleing, и я ожидал найти решение, например, при добавлении имен в вектор (имена (v) < - names_vector), но я потерпел неудачу.
Я пришел с двумя возможными решениями, но ни один из них не кажется мне элегантным. 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)
Кажется, что у меня есть решение, но у моих векторов есть 1 миллион или более элементов, и меня беспокоит не только стиль кодирования (важный для меня), но и эффективность (но я не знаю, как сделать профилирование в R). Есть ли более подходящий способ сделать это? Является ли хорошей практикой использование названного векторного ярлыка?
[[UPDATE]] мои слова, вероятно, я упрощаю вопрос, чтобы сделать его воспроизводимым. Я хотел дать имена элементам списка. Сначала я попробовал имена(), но, похоже, что я сделал что-то неправильно и не работал. Поэтому я понял, что имена() не работают со списками. Но они действительно делают, как показано принятым ответом
Ответы
Ответ 1
Если ваши значения - все скаляры, то нет ничего плохого в том, что у него есть "хранилище ключей", которое представляет собой только вектор.
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>)
на самом деле не сделает лишних копий всего.
Изменить: я только что проверил, и R будет копировать все, если вы сделаете lst <- list(....)
. Идите фигуру. Поэтому, если вы уже близко к пределу памяти на вашем компьютере, это не сработает. С другой стороны, если вы сделаете names(lst) <- ....
, он не сделает другую копию lst
. Повторите рисунок.
Ответ 2
Это можно сделать в одном выражении с помощью 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
Ответ 3
Другим серьезным вариантом здесь является использование data.table
. Который использует ключ для сортировки вашей структуры, и очень быстро обращаться к элементам особенно, когда у вас есть большие числа. Вот пример:
library(data.table)
DT <- data.table(xx = 1:1e6,
k = paste0("key", 1:1e6),key="k")
Dt - таблица данных с двумя столбцами, где я устанавливаю столбец 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
Теперь я могу получить доступ к моей таблице данных с помощью этого ключа:
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
Ответ 4
Вы хотите сделать это?...
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
, потому что обычно используются векторизованные методы для выполнения большинства задач, которые являются более эффективными и быстрыми.
Ответ 5
Выход Hong неверен.
Следует использовать vals [[ "key42" ]]
> vals[["key42"]]
[1] 42
vals <- 1:1000000
keys <- paste0("key", 1:1000000)
names(vals) <- keys
vals["key42"]
key42
42