Как создать список из 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