如何正确使用R中的列表?

简要背景:广泛使用的许多(大多数)当代编程语言至less有less数ADT [抽象数据types]是相同的,特别是,

  • string (由字符组成的序列)

  • 列表 (一个有序的值集合)和

  • 基于地图的types (将键映射到值的无序数组)

在R编程语言中,前两个分别作为charactervector来实现。

当我开始学习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 (例如上面叙述的strsplitexpression式)的规则是什么?

  • 如果我没有明确指定名称到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( ?listR-intro ) – 我已经仔细阅读过,并且不能帮助我回答刚才叙述的问题types。

(最后,我最近学习并开始使用一个名为hash的R包(可在CRAN上获得),它通过一个S4类来实现传统的 maptypes的行为;我当然可以推荐这个包)。

只是为了解决你的问题的最后一部分,因为它真的指出了R中的listvector之间的区别:

为什么这两个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和其他语言的散列/数组概念:

  1. 向量是R的primefaces。例如, rpois(1e4,5) (5个随机数), numeric(55) (length-55零向量在double上)和character(12) (12个空string)都是“basic ”。

  2. 列表或向量可以有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 
  3. 向量要求一切都是相同的数据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 
  4. 列表可以包含不同的数据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) 
  1. [ ]提供子设置操作。 通常,任何对象的子集都与原始对象具有相同的types。 因此, x[1]提供了一个列表。 同样, x[1:2]是原始列表的一个子集,因此它是一个列表。 防爆。

     x[1:2] [[1]] [1] 1 [[2]] [1] 2 
  2. [[ ]]用于从列表中提取元素。 x[[1]]是有效的,并从列表中提取第一个元素。 x[[1:2]]无效,因为[[ ]]不提供像[ ]这样的子设置。

      x[[2]] [1] 2 > x[[2:3]] Error in x[[2:3]] : subscript out of bounds 
Interesting Posts