如何正确使用R中的列表?
简要背景:广泛使用的许多(大多数)当代编程语言至less有less数ADT [抽象数据types]是相同的,特别是,
-
string (由字符组成的序列)
-
列表 (一个有序的值集合)和
-
基于地图的types (将键映射到值的无序数组)
在R编程语言中,前两个分别作为character
和vector
来实现。
当我开始学习R时,几乎从一开始就有两件事是明显的: list
是R中最重要的数据types(因为它是R data.frame
的父类),其次,我不明白它们是如何工作,至less不够好,在我的代码中正确使用它们。
首先,在我看来,R的list
数据types是ADT(Python中的dictionary
,Objective C中的NSMutableDictionary
,Perl和Ruby中的hash
,Javascript中的object literal
量等等)的直接实现。
例如,通过将键值对传递给构造函数(在Python中不是list
),可以像创buildPython字典一样创build它们:
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
你可以像访问Python字典那样访问R列表的项目,例如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
也不像其他地图types的ADT(从我学习的语言中)。 我的猜测是,这是S的初始规范的结果,也就是从头开始devise数据/统计数字DSL [领域特定语言]的意图。
R list
和其他语言中广泛使用的映射types(例如,Python,Perl,JavaScript)之间的三个显着差异:
首先 ,R中的list
s是一个有序集合,就像向量一样,即使值是键控的(即键可以是任何可哈希值,而不仅仅是顺序整数)。 几乎总是,其他语言的映射数据types是无序的 。
第二 , 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
我的意图不在于批评语言或者如何logging; 同样,我并不是说list
数据结构有什么问题或者行为如何。 我之后所要纠正的是我对它们如何工作的理解,所以我可以在我的代码中正确使用它们。
以下是我想更好理解的事情:
-
确定何时函数调用将返回一个
list
(例如上面叙述的strsplit
expression式)的规则是什么? -
如果我没有明确指定名称到
list
(例如,list(10,20,30,40)
)是默认名称,只是从1开始的顺序整数? (我假设,但是我不确定答案是肯定的,否则我们将无法将这种types的list
强制为一个带有未list
的调用的向量)。 -
为什么这两个不同的运算符
[]
和[[]]
返回相同的结果?x = list(1, 2, 3, 4)
两个expression式都返回“1”:
x[1]
x[[1]]
-
为什么这两个expression式不能返回相同的结果呢?
x = list(1, 2, 3, 4)
x2 = list(1:4)
请不要将我指向R Documentation( ?list
, R-intro
) – 我已经仔细阅读过,并且不能帮助我回答刚才叙述的问题types。
(最后,我最近学习并开始使用一个名为hash
的R包(可在CRAN上获得),它通过一个S4类来实现传统的 maptypes的行为;我当然可以推荐这个包)。
只是为了解决你的问题的最后一部分,因为它真的指出了R中的list
和vector
之间的区别:
为什么这两个expression式不会返回相同的结果?
x = list(1,2,3,4); x2 = list(1:4)
列表可以包含任何其他类作为每个元素。 所以你可以有一个列表,其中第一个元素是一个字符vector,第二个是数据框等。在这种情况下,你已经创build了两个不同的列表。 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"
鉴于最后一个元素是searchfunction,我可以这样调用它:
> complicated.list[["d"]]() [1] ".GlobalEnv" ...
作为对此的最终评论:应该指出data.frame
是一个列表(来自data.frame
文档):
数据框是具有唯一行名的相同行数的variables列表,给定类“data.frame”'
这就是为什么data.frame
的列可以具有不同的数据types,而matrix中的列则不能。 作为一个例子,在这里我尝试创build一个包含数字和字符的matrix:
> a <- 1:4 > class(a) [1] "integer" > b <- c("a","b","c","d") > d <- cbind(a, b) > d ab [1,] "1" "a" [2,] "2" "b" [3,] "3" "c" [4,] "4" "d" > class(d[,1]) [1] "character"
请注意,我不能将第一列中的数据types更改为数字,因为第二列中包含字符:
> d[,1] <- as.numeric(d[,1]) > class(d[,1]) [1] "character"
关于你的问题,让我按顺序解决它们,举一些例子:
1 )如果返回语句添加一个,则返回一个列表。 考虑
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[1]
的第一个元素 – 它与x
相同。 每个标量都是长度为一的vector。 另一方面, x[[1]]
返回列表的第一个元素。
4 )最后,他们分别创build了一个包含四个标量的列表和一个包含单个元素(恰好是四个元素的向量)的列表。
只是拿你的问题的一个子集:
这篇关于索引的文章解决了[]
和[[]]
之间区别的问题。
总之[[]]从列表中select单个项目, []
返回所选项目的列表。 在你的例子中, x = list(1, 2, 3, 4)'
item 1是一个整数,但是x[[1]]
返回一个单独的1,而x[1]
返回一个只有一个值的列表。
> x = list(1, 2, 3, 4) > x[1] [[1]] [1] 1 > x[[1]] [1] 1
列表工作的一个原因就像他们(订购)一样,是为了解决在任何节点上可以包含任何types的有序容器的需求,哪些vector不能这样做。 在R中,列表被重新用于各种目的,包括形成data.frame
的基础,这是一个任意types(但长度相同)的向量列表。
为什么这两个expression式不会返回相同的结果?
x = list(1, 2, 3, 4); x2 = list(1:4)
要添加到@ Shane的答案,如果你想得到相同的结果,请尝试:
x3 = as.list(1:4)
这将向量1:4
强制到列表中。
只是为了增加一点:
R确实有一个相当于hash
包中的Python字典的数据结构。 您可以从Open Data Group的博客文章中阅读这篇文章 。 这是一个简单的例子:
> 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
类与列表非常相似。 但是对于大型数据集来说性能更好。
你说:
另一种情况是,即使你在调用函数的时候从来没有在列表中传入列表,也可以从函数返回列表,即使该函数没有包含List构造函数,例如,
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character' class(x) # => 'list'
我猜你认为这是一个问题(?)。 我在这里告诉你为什么这不是一个问题:-)。 你的例子有点简单,因为当你进行string拆分时,你有一个元素长度为1的列表,所以你知道x[[1]]
和unlist(x)[1]
。 但是如果strsplit
结果在每个bin中返回不同长度的结果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
:返回一个列表),你可以知道第三个string的第二个“部分”是什么,例如: 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)
关于vector和其他语言的散列/数组概念:
-
向量是R的primefaces。例如,
rpois(1e4,5)
(5个随机数),numeric(55)
(length-55零向量在double上)和character(12)
(12个空string)都是“basic ”。 -
列表或向量可以有
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 ABCDEFGHIJ 0 0 0 0 0 0 0 0 0 0
-
向量要求一切都是相同的数据types。 看这个:
> i = integer(5) > v = c(n,i) > v ABCDEFGHIJ 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 ABCDEFGHIJ 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
-
列表可以包含不同的数据types,如其他答案和OP的问题本身所示。
我已经看到了其中“数组”可能包含可变数据types的语言(ruby,javascript),但是例如在C ++中,“数组”必须是完全相同的数据types。 我相信这是一个速度/效率的事情:如果你有一个numeric(1e6)
你就知道它的大小和每个元素的先验位置; 如果这个东西可能在某个未知的片段中包含"Flying Purple People Eaters"
,那么你必须实际parsing东西来了解它的基本事实。
某些标准的R操作在types得到保证时也更有意义。 例如cumsum(1:9)
是有意义的,而cumsum(list(1,2,3,4,5,'a',6,7,8,9))
没有,保证types是双重的。
至于你的第二个问题:
列表可以从函数返回,即使您在调用函数时从未传入列表
函数返回的数据types总是不一样的。 即使不将情节作为input,情节也会返回情节。 即使它接受了一个complex
Arg
也会返回一个numeric
。 等等。
(至于strsplit
:源代码在这里 。)
如果有帮助,我倾向于将R中的“列表”设想为其他OO前的语言中的“logging”:
- 他们不会对总体types做出任何假设(或者是任何可能的任何forms和字段名称的可能logging的types都是可用的)。
- 他们的领域可以是匿名的(那么你可以通过严格的定义顺序访问他们)。
名称“logging”与数据库术语中的“logging”(又名行)的标准含义相冲突,可能这就是为什么他们的名字build议自己:列表(字段)。
虽然这是一个非常古老的问题,但我必须指出,它正在触及我在R中的第一步所缺less的知识 – 即如何在R中表示数据作为对象,或者如何从现有对象中select。 一个R新手从一开始就想“在R箱子里”是不容易的。
所以我自己开始使用下面的拐杖,帮助我了解哪些对象用于什么数据,基本上是想象真实世界的用法。
虽然我没有给出确切的答案,但下面的短文可能会帮助刚刚开始使用R的读者提出类似的问题。
- primefacesvector…我把这个“序列”称为自己,没有方向,只是序列相同的types。
[
子集。 - vector…顺序从一个方向的2D,
[
子集。 - matrix…具有相同长度的vector形成行或列,
[
按行和列sorting的子集,或按顺序排列。 - 数组…分层matrix形成3D
- Dataframe …像Excel中的2D表格,我可以在其中sorting,添加或删除行或列或创build。 与他们的操作,只有一段时间后,我真正认识到,数据框是一个巧妙的实现
list
,我可以使用[
按行和列的子集,但即使使用[[
。 - 列表…帮助自己我想过
tree structure
列表,其中[i]
select并返回整个分支,[[i]]
从分支返回项目。 而且因为它tree like structure
,所以甚至可以使用index sequence
来使用[[index_vector]]
来处理非常复杂list
每一个叶子。 列表可以是简单的或非常复杂的,并且可以将各种types的对象合并为一个。
所以对于lists
你可以用更多的方法来结束如何根据情况来select一个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)
-
[ ]
提供子设置操作。 通常,任何对象的子集都与原始对象具有相同的types。 因此,x[1]
提供了一个列表。 同样,x[1:2]
是原始列表的一个子集,因此它是一个列表。 防爆。x[1:2] [[1]] [1] 1 [[2]] [1] 2
-
[[ ]]
用于从列表中提取元素。x[[1]]
是有效的,并从列表中提取第一个元素。x[[1:2]]
无效,因为[[ ]]
不提供像[ ]
这样的子设置。x[[2]] [1] 2 > x[[2:3]] Error in x[[2:3]] : subscript out of bounds