Краткая предыстория: Многие (большинство?) Современные языки программирования в широком распространении имеют по крайней мере несколько общих ADT [абстрактных типов данных], в частности,
строка (последовательность, состоящая из символов)
список (упорядоченный набор значений) и
основанный на карте тип (неупорядоченный массив, который отображает ключи на значения)
В языке программирования R первые два реализованы как character
и vector
, соответственно.
Когда я начал изучать R, две вещи были очевидны почти с самого начала: list
это самый важный тип данных в R (потому что это родительский класс для R data.frame
), а во-вторых, я просто не мог понять, как они работали, по крайней мере, недостаточно хорошо, чтобы правильно использовать их в моем коде.
Во-первых, мне показалось, что list
тип данных R является простой реализацией карты ADT ( dictionary
в Python, NSMutableDictionary
в Objective C, hash
в Perl и Ruby, object literal
в Javascript и т. Д.).
Например, вы создаете их так же, как словарь Python, передавая пары ключ-значение в конструктор (чего в Python dict
нет list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
И вы получаете доступ к элементам R List точно так же, как и к словарю Python, например x['ev1']
. Аналогично, вы можете получить только «ключи» или только «значения» :
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
также не похожи на другие ADT типа карты (из всех языков, которые я изучал в любом случае). Я предполагаю, что это является следствием начальной спецификации для S, то есть намерения разработать DSL для данных / статистики с нуля с нуля.
три существенных различия между R list
s и типами отображения в других языках в широком распространении (например, Python, Perl, JavaScript):
list
Во- первых , s в R - это упорядоченная коллекция, как и векторы, даже если значения имеют ключи (т. е. ключи могут быть любыми хешируемыми значениями, а не только последовательными целыми числами). Почти всегда тип данных отображения в других языках неупорядочен .
во-вторых , 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
Третья особенность R - х list
лет: это не кажется , что они могут быть членами другого 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; я, безусловно, могу рекомендовать этот пакет.)
x = list(1, 2, 3, 4)
оба они НЕ возвращают один и тот же результат:,x[1]
иx[[1]]
. Первый возвращает список, а второй возвращает числовой вектор. Прокручивая ниже, мне кажется, что Дирк был единственным респондентом, который правильно ответил на этот вопрос.list
в R не похожи на хэш. У меня есть еще один, который я считаю достойным внимания.list
в R может быть два члена с одинаковыми ссылочными именами. Считайте, чтоobj <- c(list(a=1),list(a=2))
это допустимо и возвращает список с двумя именованными значениями 'a'. В этом случае вызов forobj["a"]
вернет только первый соответствующий элемент списка. Вы можете получить поведение, подобное (может быть, идентичное) хешу только с одним элементом наx <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
Ответы:
Просто для решения последней части вашего вопроса, так как это действительно указывает на разницу между a
list
иvector
R:Список может содержать любой другой класс в качестве каждого элемента. Таким образом, у вас может быть список, в котором первый элемент представляет собой символьный вектор, второй - фрейм данных и т. Д. В этом случае вы создали два разных списка.
x
имеет четыре вектора, каждый длиной 1.x2
имеет 1 вектор длины 4:Так что это совершенно разные списки.
Списки R очень похожи на структуру данных хэш-карты, в которой каждое значение индекса может быть связано с любым объектом. Вот простой пример списка, который содержит 3 разных класса (включая функцию):
Учитывая, что последним элементом является функция поиска, я могу назвать ее так:
В качестве заключительного комментария к этому: следует отметить, что это
data.frame
действительно список (изdata.frame
документации):Вот почему столбцы в
data.frame
могут иметь разные типы данных, а столбцы в матрице - нет. В качестве примера здесь я пытаюсь создать матрицу с числами и символами:Обратите внимание, что я не могу изменить тип данных в первом столбце на числовой, потому что во втором столбце есть символы:
источник
switch
в R есть полезная функция, которую можно использовать для этой цели (см.help(switch)
).Что касается ваших вопросов, позвольте мне рассмотреть их по порядку и привести несколько примеров:
1 ) Список возвращается, если и когда оператор возврата добавляет его. Рассматривать
2 ) Имена просто не заданы:
3 ) Они не возвращают одно и то же. Ваш пример дает
где
x[1]
возвращает первый элементx
- который совпадает сx
. Каждый скаляр - это вектор длины один. С другой стороныx[[1]]
возвращает первый элемент списка.4 ) Наконец, они различаются тем, что они создают, соответственно, список, содержащий четыре скаляра, и список с одним элементом (который является вектором из четырех элементов).
источник
Value
. Как в?strsplit
: «Список такой же длины, как х». Но вы должны учитывать, что может быть функция, возвращающая разные значения в зависимости от аргументов (например, sapply может возвращать список или вектор).Просто чтобы взять подмножество ваших вопросов:
В этой статье по индексации рассматривается вопрос о разнице между
[]
и[[]]
.Вкратце [[]] выбирает один элемент из списка и
[]
возвращает список выбранных элементов. В вашем примереx = list(1, 2, 3, 4)'
элемент 1 представляет собой одно целое число, ноx[[1]]
возвращает одно значение 1 иx[1]
возвращает список только с одним значением.источник
A = array( 11:16, c(2,3) ); A[5]
это 15, в плоском массиве ?!Одна из причин, по которой списки работают так, как они работают (упорядочены), заключается в удовлетворении потребности в упорядоченном контейнере, который может содержать любой тип в любом узле, чего нет у векторов. Списки повторно используются для различных целей в R, включая формирование базы a
data.frame
, которая представляет собой список векторов произвольного типа (но одинаковой длины).Почему эти два выражения не возвращают одинаковый результат?
Чтобы добавить к ответу @ Shane, если вы хотите получить тот же результат, попробуйте:
Который приводит вектор
1:4
в список.источник
Просто чтобы добавить еще один момент к этому:
R имеет структуру данных , эквивалентную Dict Python в в
hash
пакете . Вы можете прочитать об этом в этом блоге от Open Data Group . Вот простой пример:С точки зрения удобства использования
hash
класс очень похож на список. Но производительность лучше для больших наборов данных.источник
Ты говоришь:
И я полагаю, вы предполагаете, что это проблема (?). Я здесь, чтобы рассказать вам, почему это не проблема :-). Ваш пример немного прост в том, что когда вы выполняете разбиение строки, у вас есть список с элементами длиной 1 элемент, поэтому вы знаете, что
x[[1]]
это то же самое, что иunlist(x)[1]
. Но что делать, если результатstrsplit
возвращает результаты различной длины в каждом бине. Простой возврат вектора (по сравнению со списком) не поможет вообще.Например:
В первом случае (
x
: , который возвращает список), вы можете сказать , что вторая «часть» 3 - й строка была, например:x[[3]][2]
. Как вы могли бы сделать то же самое, используяxx
теперь, когда результаты были «распутаны» (unlist
-ed)?источник
не то же самое, потому что 1: 4 совпадает с c (1,2,3,4). Если вы хотите, чтобы они были одинаковыми, то:
источник
Это очень старый вопрос, но я думаю, что новый ответ может добавить некоторую ценность, так как, по моему мнению, никто не обратил внимание на некоторые проблемы в ОП.
Несмотря на то, что подтверждают принятые ответы,
list
объекты в R не являются хэш-картами. Если вы хотите провести параллель с python,list
вы, скорее всего, похожи на pythonlist
s (илиtuple
s).Лучше описать, как большинство объектов R хранятся внутри (тип C объекта R
SEXP
). Они сделаны в основном из трех частей:NULL
если объект не имеет атрибутов).С внутренней точки зрения, есть небольшая разница, например, между a
list
иnumeric
вектором. Значения, которые они хранят, просто разные. Давайте разберем два объекта в парадигме, которую мы описали ранее:Для
x
:numeric
(REALSXP
на стороне C), длина 10 и другие вещи.double
значений.NULL
, так как у объекта их нет.Для
y
:list
(VECSXP
на стороне C), длина 2 и другие вещи.runif(10)
и,runif(3)
соответственно.NULL
, как дляx
.Таким образом, единственное различие между
numeric
вектором и alist
состоит в том, чтоnumeric
часть данных состоит изdouble
значений, в то время как дляlist
части данных является массив указателей на другие R-объекты.Что происходит с именами? Ну, имена - это только некоторые из атрибутов, которые вы можете назначить объекту. Давайте посмотрим на объект ниже:
list
(VECSXP
на стороне C), длина 2 и другие вещи.1:3
и,LETTERS
соответственно.names
компонентом, который являетсяcharacter
объектом R со значениемc("a","b")
.На уровне R вы можете получить атрибуты объекта с помощью
attributes
функции.Значение ключа, типичное для хэш-карты в R, является всего лишь иллюзией. Когда ты говоришь:
вот что происходит:
[[
функция называется подмножество;"a"
) имеет типcharacter
, поэтому метод получает указание искать это значение вnames
атрибуте (если он есть) объектаz
;names
атрибут не существует,NULL
возвращается;"a"
значение ищется в нем. Если"a"
не является именем объекта,NULL
возвращается;z[[1]]
.Поиск по значению ключа довольно косвенный и всегда позиционный. Также полезно иметь в виду:
names
в R должны быть строки (character
векторы);в хэш-картах вы не можете иметь два одинаковых ключа. В R вы можете назначить
names
объект с повторяющимися значениями. Например:совершенно верно в R. При попытке получить
y[["same"]]
первое значение. Вы должны знать, почему на этом этапе.В заключение, способность давать произвольные атрибуты объекту дает вам видимость чего-то отличного от внешней точки зрения. Но R
list
s никак не являются хеш-картами.источник
Относительно векторов и концепции хэш / массива из других языков:
Векторы - это атомы R. Например,
rpois(1e4,5)
(5 случайных чисел),numeric(55)
(нулевой вектор длины 55 над двойными числами) иcharacter(12)
(12 пустых строк) являются "основными".Либо списки или векторы могут иметь
names
.Векторы требуют, чтобы все были одинакового типа данных. Смотри:
Списки могут содержать различные типы данных, как видно из других ответов и самого вопроса ОП.
Я видел языки (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))
нет, без гарантируемого типа типа double.Что касается вашего второго вопроса:
Функции возвращают разные типы данных, чем они вводятся постоянно.
plot
возвращает график, даже если он не принимает график в качестве входных данных.Arg
возвращает,numeric
хотя он принялcomplex
. И т.п.(А что касается
strsplit
: исходный код здесь .)источник
Хотя это довольно старый вопрос, я должен сказать, что он касается именно тех знаний, которые мне не хватало во время моих первых шагов в R - то есть, как выразить данные в моей руке как объект в R или как выбрать из существующих объектов. Новичку не просто мыслить «в коробке» с самого начала.
Таким образом, я сам начал использовать костыли ниже, которые мне очень помогли выяснить, какой объект использовать для каких данных, и в основном представить себе реальное использование.
Хотя я не даю точных ответов на этот вопрос, краткий текст ниже может помочь читателю, который только начал с R и задает простые вопросы.
[
подмножества.[
подмножеств.[
подмножества строк и столбцов или последовательности.list
где я могу использовать поднабор[
по строкам и столбцам, но даже при использовании[[
.tree structure
где[i]
выбираются и возвращаются целые ветви и[[i]]
возвращается элемент из ветви. И потому что это такtree like structure
, вы можете даже использоватьindex sequence
для обращения к каждому листу в очень сложных,list
используя его[[index_vector]]
. Списки могут быть простыми или очень сложными и могут объединять различные типы объектов в один.Таким образом,
lists
вы можете получить больше способов, как выбрать вleaf
зависимости от ситуации, как в следующем примере.Такое мышление мне очень помогло.
источник
Если это помогает, я склонен воспринимать «списки» в R как «записи» на других языках до OO:
Название «запись» противоречило бы стандартному значению «записи» (иначе говоря, строки) на языке базы данных, и, возможно, именно поэтому их имя предлагалось: в виде списков (полей).
источник
почему эти два разных оператора
[ ]
, и[[ ]]
, возвращают один и тот же результат?[ ]
обеспечивает операцию поднабора. В целом подмножество любого объекта будет иметь тот же тип, что и исходный объект. Таким образом,x[1]
предоставляет список. Точно такx[1:2]
же это подмножество исходного списка, поэтому это список. Ex.[[ ]]
для извлечения элемента из списка.x[[1]]
допустимо и извлеките первый элемент из списка.x[[1:2]]
недопустимо, так как[[ ]]
не предоставляет подобласти[ ]
.источник