How to Correctly Use Lists in R?
简要背景:很多(大多数?)当前广泛使用的编程语言至少有一些共同的ADT(抽象数据类型),特别是,
字符串(由字符组成的序列)
列表(值的有序集合),以及
基于映射的类型(将键映射到值的无序数组)
在R编程语言中,前两个分别实现为
当我开始学习r时,有两件事几乎从一开始就很明显了:
首先,在我看来,R的
例如,通过将键值对传递给构造函数(在python中是
1 | x = list("ev1"=10,"ev2"=15,"rv"="Group 1") |
您可以像访问Python字典一样访问r列表中的项目,例如,
1 2 3 4 5 6 7 8 9 10 11 | 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
r
首先,R中的
其次,即使在调用函数时从未传入过
1 2 3 4 | 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's
1 2 3 4 | x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE) class(x) # [1] list |
。
我在这里的目的不是批评语言或者它是如何被记录下来的;同样,我也不是说
以下是我想更好地理解的事情:
决定函数调用何时返回
list 表达式的规则是什么(如上文所述的strsplit 表达式)?如果我没有给
list 显式地指定名称(例如,list(10,20,30,40) ),默认名称只是以1开头的顺序整数吗?(我假设,但我还不能确定答案是肯定的,否则我们就不能强制这种类型的list 使用一个向量w/a调用unlist 。)为什么这两个不同的操作符
[] 和[[]] 返回相同的结果?江户十一〔31〕号
两个表达式都返回"1":
埃多克斯1〔32〕
埃多克斯1〔33〕
为什么这两个表达式不返回相同的结果?
江户十一〔31〕号
埃多克斯1〔35〕
请不要把我指给R文档(
(最后,我最近了解并开始使用一个名为
只是为了解决问题的最后一部分,因为这确实指出了R中的
Why do these two expressions not return the same result?
x = list(1, 2, 3, 4); x2 = list(1:4)
号
列表可以包含任何其他类作为每个元素。因此,您可以有一个列表,其中第一个元素是字符向量,第二个元素是数据帧,等等。在本例中,您创建了两个不同的列表。
1 2 3 4 | > length(x[[1]]) [1] 1 > length(x2[[1]]) [1] 4 |
所以这些是完全不同的列表。
r列表非常类似于散列映射数据结构,因为每个索引值都可以与任何对象相关联。下面是包含3个不同类(包括函数)的列表的简单示例:
1 2 3 4 5 6 7 8 9 10 | > 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" |
。
最后一个元素是搜索函数,我可以这样调用它:
1 2 | > complicated.list[["d"]]() [1]".GlobalEnv" ... |
。
最后一点意见是:应注意,
A data frame is a list of variables of the same number of rows with unique row names, given class ‘"data.frame"’
号
这就是为什么
1 2 3 4 5 6 7 8 9 10 11 12 13 | > 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" |
请注意,我无法将第一列中的数据类型更改为数字,因为第二列包含字符:
1 2 3 | > d[,1] <- as.numeric(d[,1]) > class(d[,1]) [1]"character" |
。
关于你的问题,让我按顺序回答并举例说明:
1)当RETURN语句添加一个列表时,将返回一个列表。考虑
1 2 3 4 5 | 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)不设置名称:
1 2 3 | R> retList <- function() return(list(1,2,3,4)); names(retList()) NULL R> |
号
3)它们不返回相同的内容。您的示例给出
1 2 3 4 5 6 | R> x <- list(1,2,3,4) R> x[1] [[1]] [1] 1 R> x[[1]] [1] 1 |
其中
4)最后,这两种方法不同,它们分别创建了一个包含四个标量的列表和一个包含单个元素的列表(正好是四个元素的向量)。
只需回答一部分问题:
本文就索引问题讨论了
简而言之,[]从列表中选择单个项目,
1 2 3 4 5 6 7 | > x = list(1, 2, 3, 4) > x[1] [[1]] [1] 1 > x[[1]] [1] 1 |
。
一个原因是列表按它们的方式工作(按顺序排列)是为了满足对有序容器的需求,该容器可以包含任何节点上的任何类型,而向量则不需要。列表在R中可用于多种用途,包括形成
为什么这两个表达式不返回相同的结果?
1 | x = list(1, 2, 3, 4); x2 = list(1:4) |
要添加到@shane的答案中,如果您想获得相同的结果,请尝试:
1 | x3 = as.list(1:4) |
。
将载体
只需再加一点:
r的数据结构与
1 2 3 4 5 6 | > 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 |
号
在可用性方面,
你说:
For another, lists can be returned
from functions even though you never
passed in a List when you called the
function, and even though the function
doesn't contain a List constructor,
e.g.,
号
1 2 3 | x = strsplit(LETTERS[1:10],"") # passing in an object of type 'character' class(x) # => 'list' |
号
我想你认为这是个问题(?)我是来告诉你为什么这不是问题的。您的示例有点简单,因为当您进行字符串拆分时,您有一个元素长度为1个元素的列表,因此您知道
例如:
1 2 3 4 | 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,",")) |
号
在第一种情况下(
1 2 3 | x = list(1, 2, 3, 4) x2 = list(1:4) all.equal(x,x2) |
不相同,因为1:4与C(1,2,3,4)相同。如果您希望它们相同,那么:
1 2 3 | x = list(c(1,2,3,4)) x2 = list(1:4) all.equal(x,x2) |
号
尽管这是一个很古老的问题,但我必须说,它确实触及了我在R中第一步所缺少的知识,即如何将手中的数据表示为R中的对象,或者如何从现有对象中进行选择。对于新手来说,从一开始就想"在R盒子里"是不容易的。
所以我自己开始使用下面的拐杖,这有助于我找到用于什么数据的对象,并基本上想象真实世界的使用情况。
虽然我没有给出问题的确切答案,下面的短文可能有助于读者谁刚刚开始与R,并提出西米拉尔问题。
- 原子矢量…我自己称之为"序列",没有方向,只是相同类型的序列。
[ 子集。 - 矢量。。。从2d到
[ 子集的单向序列。 - 矩阵。。。一组长度相同的向量,按行和列或按序列组成
[ 子集。 - 数组…形成三维分层矩阵
- 数据帧…类似于Excel的二维表格,我可以在其中对行或列进行排序、添加或删除,或者生成算术。使用它们进行操作后,我才真正认识到,数据帧是
list 的巧妙实现,在这里我可以使用[ 按行和列进行子集,甚至使用[[ 。 - 列表…为了方便自己,我考虑了一个清单,比如
tree structure ,其中[i] 选择并返回整个分支机构,[[i]] 从分支机构返回项目。因为它是tree like structure ,你甚至可以用index sequence 来处理非常复杂的list 上的每一片叶子,使用[[index_vector]] 。列表可以是简单的,也可以是非常复杂的,可以将各种类型的对象混合在一起。
因此,对于
1 2 3 4 | 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 |
这种思维方式对我有很大帮助。
为什么这两个不同的操作符
1 | x = list(1, 2, 3, 4) |
1 2 3 4 5 | x[1:2] [[1]] [1] 1 [[2]] [1] 2 |
。
1 2 3 | x[[2]] [1] 2 > x[[2:3]] Error in x[[2:3]] : subscript out of bounds |
。
如果有帮助,我倾向于将r中的"list"想象成其他面向对象前语言中的"records":
- 它们不会对总体类型做出任何假设(或者更确切地说,任何arity和字段名的所有可能记录的类型都是可用的)。
- 它们的字段可以是匿名的(然后通过严格的定义顺序访问它们)。
名称"record"将与数据库术语中"records"(即行)的标准含义相冲突,这可能就是为什么它们的名称建议自己是列表(字段)。
关于向量和其他语言的散列/数组概念:
矢量是r的原子,例如,
列表或向量都可以有
1 2 3 4 5 6 7 8 9 | > 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 |
向量要求所有内容都是相同的数据类型。注意:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | > 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++中的"数组"必须是相同的数据类型。我认为这是一个速度/效率的问题:如果你有一个
当类型得到保证时,某些标准的R操作也更有意义。例如,
关于第二个问题:
Lists can be returned from functions even though you never passed in a List when you called the function
号
函数返回的数据类型与它们一直输入的数据类型不同。
(至于