Как правильно использовать списки в R?
краткая справка: Многие (большинство?) современные языки программирования в широком использовании имеют по крайней мере несколько общих ADTs [абстрактных типов данных], в частности,
строка (последовательность, состоящая из символов)
список (упорядоченная коллекция значений) и
тип на основе карты (неупорядоченный массив, который сопоставляет ключи значения)
в языке программирования R первые два реализованы как character
и vector
, соответственно.
когда я начал изучать R, две вещи были очевидны почти с самого начала:list
является наиболее важным типом данных в R (потому что это родительский класс для R data.frame
), а во-вторых, я просто не мог понять, как они работают, по крайней мере, недостаточно хорошо, чтобы правильно использовать их в моем коде.
во-первых, казалось, что мне, что Р list
тип данных был простой реализацией карты ADT (dictionary
в Python, NSMutableDictionary
в объективе C,hash
в Perl и Ruby, object literal
в Javascript и так далее).
например, вы создаете их так же, как и словарь Python, передавая пары ключ-значение конструктору (который в Python является dict
не list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
и вы получаете доступ к элементам списка R так же, как и к элементам словаря Python, например, x['ev1']
. Аналогично, вы можете получить только '' или просто 'values' by:
names(x) # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"
unlist(x) # fetch just the 'values' of an R list
# ev1 ev2 rv
# "10" "15" "Group 1"
x = list("a"=6, "b"=9, "c"=3)
sum(unlist(x))
# [1] 18
но R list
s также в отличие от другие карты типа ADTs (из числа языков, которые я узнал в любом случае). Я предполагаю, что это является следствием начальной спецификации для S, т. е. намерения разработать DSL данных/статистики [язык домена] с нуля.
три значимые различия Р list
S и типы отображения на других широко используемых языках (e.g,. Python, Perl, JavaScript):
первый, list
s в R являются приказал сбор, как и векторы, хотя значения по ключу (т. е. ключи могут быть hashable не просто последовательные целые числа). Почти всегда тип данных сопоставления на других языках -ненумерованный.
второй, list
s может быть возвращен из функций, даже если вы никогда не проходили в list
когда вы вызвали функцию, и хотя функция, которая вернула list
не содержит (явные) list
конструктор (конечно, вы можете справиться с этим на практике, обернув возвращенный результат в вызов unlist
):
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x) # returns 'list', not a vector of length 2
# [1] list
A третий особенность Р list
s: не похоже, что они могут быть членами другого ADT, и если при попытке сделать это основной контейнер принуждается к list
. Е. Г.,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
мое намерение здесь не критиковать язык или то, как он документирован; аналогичным образом, я не предполагаю, что что-то не так с list
структура данных и как она себя ведет. Все, что мне нужно, - это исправить мое понимание того, как они работают, чтобы я мог правильно использовать их в своем коде.
вот те вещи, которые я хотел бы лучше поймите:
каковы правила, которые определяют, когда вызов функции вернет
list
(например,strsplit
выражение, процитированное выше)?если я явно не назначаю имена
list
(например,list(10,20,30,40)
) являются ли имена по умолчанию только последовательными целыми числами, начинающимися с 1? (Я предполагаю, но я далеко не уверен, что ответ да, иначе мы не смогли бы принудить этот типlist
к вектору с вызовомunlist
.)-
почему эти два разных оператора,
[]
и[[]]
, вернуть то же самое результат?x = list(1, 2, 3, 4)
оба выражения возвращают "1":
x[1]
x[[1]]
-
почему эти два выражения не возвратить то же самое результат?
x = list(1, 2, 3, 4)
x2 = list(1:4)
пожалуйста, не указывайте мне на документацию R (?list
, R-intro
)--я внимательно прочитал его, и это не помогает мне ответить на вопросы, которые я читал выше.
(наконец, я недавно узнал и начал использовать пакет R (доступный на CRAN) под названием hash
, который реализует обычных поведение типа карты через класс S4; я, безусловно, могу рекомендовать этот пакет.)
11 ответов
просто для решения последней части вашего вопроса, так как это действительно указывает на разницу между list
и vector
в R:
почему эти два выражения не возвращают один и тот же результат?
X = список(1, 2, 3, 4); x2 = список (1:4)
список может содержать любой другой класс в качестве каждого элемента. Таким образом, вы можете иметь список, где первый элемент является символьным вектором, второй-фреймом данных и т. д. В этом случае, у вас есть создал два разных списка. x
имеет четыре вектора, каждый длины 1. x2
имеет 1 вектор длины 4:
> length(x[[1]])
[1] 1
> length(x2[[1]])
[1] 4
так это совершенно разные списки.
R списки очень похожи на хэш-карте структура данных в том, что каждое значение индекса может быть связан с любым объектом. Вот простой пример списка, который содержит 3 разных класса (включая функцию):
> complicated.list <- list("a"=1:4, "b"=1:3, "c"=matrix(1:4, nrow=2), "d"=search)
> lapply(complicated.list, class)
$a
[1] "integer"
$b
[1] "integer"
$c
[1] "matrix"
$d
[1] "function"
учитывая, что последним элементом является поиск функция, я могу назвать это так:
> complicated.list[["d"]]()
[1] ".GlobalEnv" ...
в качестве заключительного комментария к этому: следует отметить, что a data.frame
действительно список (из data.frame
документация):
фрейм данных-это список переменных с одинаковым количеством строк с уникальными именами строк, заданными классом "" данные.кадр"'
вот почему столбцы в data.frame
могут иметь разные типы данных, в то время как столбцы в матрице не может. В качестве примера, здесь я пытаюсь создать матрицу с цифры и символы:
> a <- 1:4
> class(a)
[1] "integer"
> b <- c("a","b","c","d")
> d <- cbind(a, b)
> d
a b
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
[4,] "4" "d"
> class(d[,1])
[1] "character"
обратите внимание, как я не могу изменить тип данных в первом столбце цифр, потому что второй столбец символов:
> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"
Что касается ваших вопросов, позвольте мне обратиться к ним по порядку и привести несколько примеров:
1) список возвращается, если и когда оператор return добавляет один. Считать
R> retList <- function() return(list(1,2,3,4)); class(retList())
[1] "list"
R> notList <- function() return(c(1,2,3,4)); class(notList())
[1] "numeric"
R>
2) имена просто не установлен:
R> retList <- function() return(list(1,2,3,4)); names(retList())
NULL
R>
3) они не возвращают то же самое. Ваш пример дает
R> x <- list(1,2,3,4)
R> x[1]
[[1]]
[1] 1
R> x[[1]]
[1] 1
здесь x[1]
возвращает первый элемент x
-- что то же самое, что x
. Каждый скаляр-это вектор длины один. С другой стороны!--6--> возвращает первый элемент списка.
4) наконец, они отличаются друг от друга, они создают, соответственно, список, содержащий четыре скаляра и список с одним элементом (который является вектором из четырех элементов).
просто взять подмножество ваших вопросов:
в этой статье при индексации решается вопрос о разнице между []
и [[]]
.
короче [[]] выбирает один элемент из списка и []
возвращает список выбранных элементов. В вашем примере, x = list(1, 2, 3, 4)'
пункт 1 является одним целым числом, но x[[1]]
возвращает один 1 и x[1]
возвращает список с одним значением.
> x = list(1, 2, 3, 4)
> x[1]
[[1]]
[1] 1
> x[[1]]
[1] 1
одна из причин, по которой списки работают так ,как они (упорядочены), - это необходимость упорядоченного контейнера, который может содержать любой тип на любом узле, чего не делают векторы. Списки повторно используются для различных целей в R, включая формирование базы a data.frame
, который представляет собой список векторов произвольного типа (но одинаковой длины).
Почему эти два выражения не возвращают один и тот же результат?
x = list(1, 2, 3, 4); x2 = list(1:4)
чтобы добавить к ответу @Shane, если вы хотите получить тот же результат, попробуйте:
x3 = as.list(1:4)
который coerces вектор 1:4
в список.
чтобы добавить еще один момент к этому:
R имеет структуру данных, эквивалентную Python dict в на hash
пакета. Вы можете прочитать об этом в это сообщение в блоге из группы открытых данных. Вот простой пример:
> library(hash)
> h <- hash( keys=c('foo','bar','baz'), values=1:3 )
> h[c('foo','bar')]
<hash> containing 2 key-value pairs.
bar : 2
foo : 1
С точки зрения юзабилити,hash
класс очень похож на список. Но производительность лучше для больших наборов данных.
вы говорите:
для другого, списки могут быть возвращены от функций, даже если вы никогда передано в список, когда вы позвонили функция, и даже если функция не содержит конструктор списка, например,
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x)
# => 'list'
и я думаю, вы предполагаете, что это проблема(?). Я здесь, чтобы сказать вам, почему это не проблема :-). Ваш пример немного прост, в том, что когда вы делаете разделение строки, у вас есть список с элементами, которые равны 1 элемент длинный, так что вы знаете, что x[[1]]
это то же самое, что unlist(x)[1]
. Но что, если результат strsplit
возвращенные результаты различной длины в каждом ящике. Просто возврат вектора (против списка) не будет делать вообще.
например:
stuff <- c("You, me, and dupree", "You me, and dupree",
"He ran away, but not very far, and not very fast")
x <- strsplit(stuff, ",")
xx <- unlist(strsplit(stuff, ","))
в первом случае (x
: который возвращает список), вы можете сказать, что 2-ю часть 3-й строки, например: x[[3]][2]
. Как вы могли сделать то же самое, используя xx
теперь, когда результаты были "распутаны" (unlist
- ed)?
x = list(1, 2, 3, 4)
x2 = list(1:4)
all.equal(x,x2)
не то же самое, потому что 1: 4 совпадает с c(1,2,3,4). Если вы хотите, чтобы они были одинаковыми, то:
x = list(c(1,2,3,4))
x2 = list(1:4)
all.equal(x,x2)
относительно векторов и концепции хэша / массива из других языков:
векторы являются атомами R. например,
rpois(1e4,5)
(5 случайных чисел),numeric(55)
(длина-55 нулевой вектор над двойниками), иcharacter(12)
(12 пустых строк), все "основные".-
либо списки, либо векторы могут иметь
names
.> n = numeric(10) > n [1] 0 0 0 0 0 0 0 0 0 0 > names(n) NULL > names(n) = LETTERS[1:10] > n A B C D E F G H I J 0 0 0 0 0 0 0 0 0 0
-
векторы требуют, чтобы все было одного и того же типа данных. Часы это:
> i = integer(5) > v = c(n,i) > v A B C D E F G H I J 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > class(v) [1] "numeric" > i = complex(5) > v = c(n,i) > class(v) [1] "complex" > v A B C D E F G H I J 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i
списки могут содержать различные типы данных, как видно из других ответов и самого вопроса OP.
я видел языки (ruby, javascript), в которых "массивы" могут содержать переменные типы данных, но, например, в C++ "массивы" должны иметь один и тот же тип данных. Я считаю, что это скорость и эффективность: если у вас есть numeric(1e6)
вы знаете свой размер и расположение каждого элемента априори; если дело может содержать "Flying Purple People Eaters"
в каком-то неизвестном срезе, то вы должны фактически разобрать материал, чтобы знать основные факты об этом.
некоторые стандартные операции R также имеют больше смысла, когда тип гарантирован. Например cumsum(1:9)
имеет смысл, в то время как cumsum(list(1,2,3,4,5,'a',6,7,8,9))
не делает, без гарантированного, что тип будет двойным.
Что касается вашего второго вопроса:
списки могут быть возвращены из функций, даже если вы никогда не проходили в списке, когда вы вызывается функция
функции возвращают разные типы данных, чем они все время. plot
возвращает участок, даже если он не принимает участок в качестве входных данных. Arg
возвращает numeric
хотя он принял complex
. Так далее.
(а strsplit
исходный код здесь.)
Если это помогает, я склонен понимать "списки" в R как "записи" на других языках pre-OO:
- они не делают никаких предположений о всеобъемлющем типе (или, скорее, тип всех возможных записей любых имен arity и полей доступен).
- их поля могут быть анонимными (тогда вы получаете к ним доступ по строгому порядку определения).
имя " запись "будет конфликтовать со стандартным значением" записи " (он же строки) на языке базы данных, и может быть, поэтому их название предложило себя: как списки (полей).
хотя это довольно старый вопрос, я должен сказать, что он касается именно знания, которого мне не хватало во время моих первых шагов в R - т. е. как выразить данные в моей руке как объект в R или как выбрать из существующих объектов. Новичку R нелегко думать "в коробке R" с самого начала.
поэтому я сам начал использовать костыли ниже, которые помогли мне много узнать, какой объект использовать для каких данных, и в основном представить реальный мир использование.
хотя я не даю точных ответов на вопрос, короткий текст ниже может помочь читателю, который только что начал с R и задает вопросы simmilar.
- атомный вектор ... Я назвал это "последовательностью" для себя, без направления, просто последовательностью тех же типов.
[
подмножеств. - вектор ... последовательность с одним направлением из 2D,
[
подмножеств. - Матрица ... связка векторов одинаковой длины, образующих строки или столбцы,
[
подмножества по строкам и столбцам, или по последовательности. - массивы ... слоистые матрицы, образующие 3D
- таблицы данных ... 2D-таблица, как в excel, где я могу сортировать, добавлять или удалять строки или столбцы или делать arit. операции с ними, только через некоторое время я действительно признал, что dataframe является умной реализацией
list
где я могу подмножество с помощью[
по строкам и столбцам, но даже используя[[
. - список ... чтобы помочь себе, Я думал о списке как о
tree structure
где[i]
выбирает и возвращает целые ветви и[[i]]
возвращает элемент из ветки. И потому этоtree like structure
, вы даже можете использоватьindex sequence
обратиться к каждому листу на очень сложномlist
используя[[index_vector]]
. Списки могут быть простыми или очень сложными и могут смешивать различные типы объектов в один.
и lists
вы можете получить больше способов, как выбрать leaf
в зависимости от ситуации, как в следующем образец.
l <- list("aaa",5,list(1:3),LETTERS[1:4],matrix(1:9,3,3))
l[[c(5,4)]] # selects 4 from matrix using [[index_vector]] in list
l[[5]][4] # selects 4 from matrix using sequential index in matrix
l[[5]][1,2] # selects 4 from matrix using row and column in matrix
этот способ мышления мне очень помогли.
почему эти два различных операторов [ ]
и [[ ]]
, вернуть тот же результат?
x = list(1, 2, 3, 4)
-
[ ]
обеспечивает sub деятельность установки. В общем sub set любого объекта будет иметь тот же тип, что и исходный объект. Следовательно,x[1]
предоставляет список. Аналогичноx[1:2]
является подмножеством исходного списка, поэтому список. Бывший.x[1:2] [[1]] [1] 1 [[2]] [1] 2
-
[[ ]]
для извлечения элемента из списка.x[[1]]
is действительный и извлечь первый элемент из списка.x[[1:2]]
не является[[ ]]
не предоставляет настройки sub, такие как[ ]
.x[[2]] [1] 2 > x[[2:3]] Error in x[[2:3]] : subscript out of bounds