Answers:
R语言定义可轻松回答以下类型的问题:
R具有三个基本的索引运算符,其语法由以下示例显示
x[i] x[i, j] x[[i]] x[[i, j]] x$a x$"a"
对于向量和矩阵
[[
,尽管它们与形式有一些语义上的区别[
(例如,它删除了任何名称或dimnames属性,并且部分匹配用于字符索引),但很少使用形式。使用单个索引对多维结构进行索引时,x[[i]]
或x[i]
将返回的i
第th个顺序元素x
。对于列表,通常用于
[[
选择任何单个元素,而[
返回所选元素的列表。该
[[
格式仅允许使用整数或字符索引选择单个元素,而[
允许使用向量索引。请注意,尽管对于列表,索引可以是向量,并且向量的每个元素依次应用于列表,所选组件,该组件的所选组件,依此类推。结果仍然是单个元素。
[
总是返回,你得到的结果相同类列表的装置x[v]
,无论长短v
。例如,可能要lapply
遍历列表的子集:lapply(x[v], fun)
。如果[
将长度为1的向量的列表删除,则长度为1的向量将返回错误v
。
两种方法之间的显着区别是,它们在提取时返回的对象的类别,以及它们是否可以接受一定范围的值,或者在赋值期间仅接受单个值。
考虑以下列表中数据提取的情况:
foo <- list( str='R', vec=c(1,2,3), bool=TRUE )
假设我们想从foo中提取bool存储的值,并在if()
语句中使用它。这将说明的返回值[]
与[[]]
用于数据提取的返回值之间的差异。该[]
方法返回类列表的对象(如果foo是data.frame,则[[]]
返回data.frame),而该方法返回其类由其值的类型确定的对象。
因此,使用该[]
方法将得到以下结果:
if( foo[ 'bool' ] ){ print("Hi!") }
Error in if (foo["bool"]) { : argument is not interpretable as logical
class( foo[ 'bool' ] )
[1] "list"
这是因为该[]
方法返回了一个列表,并且列表不是直接传递给if()
语句的无效对象。在这种情况下,我们需要使用,[[]]
因为它将返回存储在“ bool”中的“ bare”对象,该对象具有适当的类:
if( foo[[ 'bool' ]] ){ print("Hi!") }
[1] "Hi!"
class( foo[[ 'bool' ]] )
[1] "logical"
第二个区别是,[]
操作员可以用来访问数据帧列表或列中的一系列插槽,而[[]]
操作员只能访问单个插槽或列。考虑使用第二个列表进行值分配的情况bar()
:
bar <- list( mat=matrix(0,nrow=2,ncol=2), rand=rnorm(1) )
假设我们想用bar中包含的数据覆盖foo的最后两个插槽。如果我们尝试使用[[]]
运算符,则会发生以下情况:
foo[[ 2:3 ]] <- bar
Error in foo[[2:3]] <- bar :
more elements supplied than there are to replace
这是因为[[]]
仅限于访问单个元素。我们需要使用[]
:
foo[ 2:3 ] <- bar
print( foo )
$str
[1] "R"
$vec
[,1] [,2]
[1,] 0 0
[2,] 0 0
$bool
[1] -0.6291121
请注意,分配成功后,foo中的插槽保留其原始名称。
只需在此处添加[[
也可用于递归索引。
@JijoMatthew的答案暗示了这一点,但并未进行探讨。
如中所述?"[["
,类似的语法x[[y]]
,其中length(y) > 1
,被解释为:
x[[ y[1] ]][[ y[2] ]][[ y[3] ]] ... [[ y[length(y)] ]]
请注意,这不会改变您对[
和[[
- 之间区别的主要理解,即前者用于子集,后者用于提取单个列表元素。
例如,
x <- list(list(list(1), 2), list(list(list(3), 4), 5), 6)
x
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [1] 1
#
# [[1]][[2]]
# [1] 2
#
# [[2]]
# [[2]][[1]]
# [[2]][[1]][[1]]
# [[2]][[1]][[1]][[1]]
# [1] 3
#
# [[2]][[1]][[2]]
# [1] 4
#
# [[2]][[2]]
# [1] 5
#
# [[3]]
# [1] 6
要获得值3,我们可以这样做:
x[[c(2, 1, 1, 1)]]
# [1] 3
回到上面的@JijoMatthew的答案,回想一下r
:
r <- list(1:10, foo=1, far=2)
特别是,这解释了我们在滥用时容易出现的错误[[
,即:
r[[1:3]]
错误
r[[1:3]]
:递归索引在级别2失败
由于此代码实际上尝试评估r[[1]][[2]][[3]]
,并且嵌套r
在一级停止,因此通过递归索引进行提取的尝试[[2]]
在一级(即二级)失败。
错误
r[[c("foo", "far")]]
:下标超出范围
在这里,R正在寻找r[["foo"]][["far"]]
,它不存在,因此我们得到下标超出范围错误。
如果这两个错误都给出相同的消息,可能会更有帮助/一致。
两者都是子集化的方式。单括号将返回列表的子集,列表本身就是列表。即:它可以包含或可以不包含多个元素。另一方面,双括号将仅返回列表中的单个元素。
-单括号会给我们一个列表。如果我们希望从列表中返回多个元素,我们也可以使用单括号。考虑以下列表:
>r<-list(c(1:10),foo=1,far=2);
现在,请注意当我尝试显示列表时返回列表的方式。我输入r并按Enter
>r
#the result is:-
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
现在,我们将看到单括号的神奇之处:-
>r[c(1,2,3)]
#the above command will return a list with all three elements of the actual list r as below
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
$foo
[1] 1
$far
[1] 2
这与我们尝试在屏幕上显示r的值完全相同,这意味着使用单括号返回了一个列表,其中在索引1处有一个包含10个元素的向量,然后还有两个名称为foo的元素远。我们也可以选择将单个索引或元素名称作为单个括号的输入。例如:
> r[1]
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
在此示例中,我们给了一个索引“ 1”,然后返回了一个包含一个元素的列表(它是10个数字的数组)
> r[2]
$foo
[1] 1
在上面的示例中,我们给了一个索引“ 2”,然后得到了一个包含一个元素的列表
> r["foo"];
$foo
[1] 1
在此示例中,我们传递了一个元素的名称,然后返回了一个元素的列表。
您也可以传递元素名称的向量,例如:
> x<-c("foo","far")
> r[x];
$foo
[1] 1
$far
[1] 2
在此示例中,我们传递了一个带有两个元素名称“ foo”和“ far”的向量
作为回报,我们得到了一个包含两个元素的列表。
简而言之,单括号将始终为您返回另一个列表,该列表的元素数等于您传递到单括号中的元素数或索引数。
相反,双括号将始终只返回一个元素。在移至双括号之前,请注意一点。
NOTE:THE MAJOR DIFFERENCE BETWEEN THE TWO IS THAT SINGLE BRACKET RETURNS YOU A LIST WITH AS MANY ELEMENTS AS YOU WISH WHILE A DOUBLE BRACKET WILL NEVER RETURN A LIST. RATHER A DOUBLE BRACKET WILL RETURN ONLY A SINGLE ELEMENT FROM THE LIST.
我将举几个例子。请注意以下粗体字,并在完成以下示例后再次使用:
双括号将返回索引处的实际值(它不会返回列表)
> r[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
>r[["foo"]]
[1] 1
对于双括号,如果我们尝试通过传递矢量来查看多个元素,则将导致错误,原因仅在于它不是为满足这种需要而构建的,而是仅返回单个元素。
考虑以下
> r[[c(1:3)]]
Error in r[[c(1:3)]] : recursive indexing failed at level 2
> r[[c(1,2,3)]]
Error in r[[c(1, 2, 3)]] : recursive indexing failed at level 2
> r[[c("foo","far")]]
Error in r[[c("foo", "far")]] : subscript out of bounds
为了帮助新手克服手动雾,将[[ ... ]]
符号表示为折叠功能可能很有帮助,换句话说,就是当您只想从命名矢量,列表或数据框中“获取数据”时。如果要使用这些对象中的数据进行计算,则最好这样做。这些简单的例子将说明。
(x <- c(x=1, y=2)); x[1]; x[[1]]
(x <- list(x=1, y=2, z=3)); x[1]; x[[1]]
(x <- data.frame(x=1, y=2, z=3)); x[1]; x[[1]]
因此,从第三个示例:
> 2 * x[1]
x
1 2
> 2 * x[[1]]
[1] 2
请参考以下详细说明。
我在R中使用了内置数据帧,称为mtcars。
> mtcars
mpg cyl disp hp drat wt ...
Mazda RX4 21.0 6 160 110 3.90 2.62 ...
Mazda RX4 Wag 21.0 6 160 110 3.90 2.88 ...
Datsun 710 22.8 4 108 93 3.85 2.32 ...
............
该表的第一行称为标题,其中包含列名称。后面的每条水平线表示一个数据行,该数据行以该行的名称开头,然后是实际数据。行中的每个数据成员称为单元。
要检索单元格中的数据,我们将在单个方括号“ []”运算符中输入其行和列坐标。两个坐标由逗号分隔。换句话说,坐标以行位置开始,然后以逗号开头,以列位置结束。顺序很重要。
例如1:-这是mtcars第一行第二列的单元格值。
> mtcars[1, 2]
[1] 6
例如2:-此外,我们可以使用行名和列名代替数字坐标。
> mtcars["Mazda RX4", "cyl"]
[1] 6
我们使用双括号“ [[]]”运算符引用数据框列。
例如1:-要检索内置数据集mtcar的第九列向量,我们编写mtcars [[9]]。
mtcars [[9]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...
例如2:-我们可以通过名称检索相同的列向量。
mtcars [[“” am“]] [1] 1 1 1 0 0 0 0 0 0 0 0 ...
此外:
这是一个解决以下问题的小示例:
x[i, j] vs x[[i, j]]
df1 <- data.frame(a = 1:3)
df1$b <- list(4:5, 6:7, 8:9)
df1[[1,2]]
df1[1,2]
str(df1[[1,2]])
str(df1[1,2])