R 是一门面向对象的函数化数组编程语言,所有可操作的 R
实体(entity)都是对象(object), 对象在
R 中是一种专门的数据结构。所有 R
对象都拥有一种或者多种属性(attribute),
可以用attributes()或者attr()函数来读取或设置对象的属性。一个重要属性是类(class),
所有 R
对象都拥有类属性,可以用class()函数来读取和设置一个对象的类,常见的类有numeric,logical,character,list,matrix,array,factor和
data.frame。
R
对象一定要以某种类型(type)来存储,可以用typeof()函数来获得一个
R
对象的类型。使用as.类型()函数可以将一种类型的对象转换成另一种类型,使用is.类型()函数可以判断一个对象是否属于指定类型。
此外,R 对象还有一个内在属性(Intrinsic
attribute):模式(mode),它来源于 R 语言的前身
S 语言,
无法通过使用mode()和storage.mode()函数分别可以返回
R 对象的模式和存储模式。在大多数情况下,
typeof()和mode()的返回结果相同,但typeof()会将数值模式numeric细分为整数类型integar和
双精度浮点数类型double,与storage.mode()的返回结果相同。
而数据科学中提到的数据结构显然不是对象这种笼统的概念,而是存储了我们感兴趣的数据的具体数据结构, 也即是对象的某些类型(注意这里的类型与上面说的存储的类型是不同的概念)。想要记住所有 R 数据结构 没有意义,不同的 R 包可能还会补充新的数据结构,只要了解自己需要使用的数据结构即可。下面介绍 R 中常用的数据结构。
首先,根据存储类型是否统一,可以将 R 中数据的存储结构划分为两种基本类型: 原子向量(atomic vector)和泛型向量(generic vector)。
由单一存储类型的数据构成的数据结构,称为原子向量(atomic
vector),也即其所有元素都具有相同的type。
实际上,原子向量只有六种可能的存储类型:logical,integer,double,complex,character和raw。
原子向量除了通常所说的 R 语言中的(简单)向量,还包括矩阵、数组等。
由多种存储类型的数据构成的数据结构,称为泛型向量(generic vector),列表中不但可以包含各种类型的原子向量,还可以嵌套其他泛型向量。常见的泛型向量有数据框(data.frame)和列表(list),不过,由于数据框也可以看作一种特殊的列表,因此很多时候直接用”列表”来指代泛型向量,在不同语境中,要注意”列表”到底指的是泛型向量,还是列表这种具体的数据结构。
向量是一个一维数组,可以用来存储数值型、字符型和逻辑型的数据,也就是对应type为integer,double,complex,character和logical的元素。正如前文提到过的,向量是原子向量的一种,因此一个向量只能存储同一种类型的数据,如果一个向量由不同的数值型数据构成,那么会按照integer,double,complex的顺序把前面的数值存储类型强制转换成最后一种数值存储类型(integer会被转换成double,integer和double会被转换成complex)。
值得一提的是,向量的类(class)就是它的模式(mode)。
R
中没有标量,单个的数据元素也会被当作长度为1的向量。顺带一提,向量的长度可以通过函数length()得到。
c()(x <- c('I','love','U'))
[1] "I" "love" "U"
:运算符(步长为1或者-1时)或者函数seq()x <- 1:5
y <- 5:1
z <- seq(1,7,2)
x;y;z
[1] 1 2 3 4 5
[1] 5 4 3 2 1
[1] 1 3 5 7
rep(),使用参数each表示逐元素复制,使用参数times表示整体复制,
分别对应 Python 的 Numpy
包中的repeat()函数和tile()函数x <- 1:3
rep(x, each = 2)
[1] 1 1 2 2 3 3
rep(x, times = 2)
[1] 1 2 3 1 2 3
参数 times 还可以接受一个和数值向量等长的整数向量,可以依次指定每个分量重复的次数:
rep(x, times = 1:3)
[1] 1 2 2 3 3 3
R 是向量化编程语言,因此对向量的所有运算都直接作用于每一个分量:
x <- 1:3
3*x
[1] 3 6 9
两个向量进行运算时,会按位进行,如果长度不一致,会循环使用较短的一个向量以匹配较长的向量, 当较长的向量的长度不是较短向量的倍数时,循环会进行,但会给出警告信息:
v <- 2:7
x <- 1:6
y <- 1:3
z <- 1:4
v + x
[1] 3 5 7 9 11 13
x + y
[1] 2 4 6 5 7 9
x + z
Warning: longer object length is not a multiple of shorter object length
[1] 2 4 6 8 6 8
这种按位向量运算的规律适用于所有类型的向量。在 Python 的 Numpy 包中,这种特性被称为广播(broadcast)机制。
逻辑向量的分量都是逻辑值,逻辑真为TRUE或者T,逻辑假为FALSE或者F,转换成数值则分别为1和0。
下表列出了 R 最基础的逻辑运算:
| 运算符/函数 | 运算含义 |
&,&& |
逻辑与运算,&&非必要不检查第2位元素 |
|,|| |
逻辑或运算,||非必要不检查第2位元素 |
! |
逻辑非运算 |
xor() |
逻辑异或运算 |
isTRUE() |
判断逻辑值是否为真 |
isFALSE() |
判断逻辑值是否为假 |
any()和all()函数分别检验逻辑向量中是否有TRUE和是否全为TRUE。在
Python 中,同样的功能是通过 Numpy
包中ndarray的any()和all()实例方法实现的。
which()函数接受一个逻辑向量(或者数组),并且返回逻辑值为真的全部下标,在
Python 中,同样的功能是通过 NumPy
包中的where()函数实现的。
如上一部分所述,逻辑向量也满足向量化运算的规律,不再赘述。
字符向量的分量是一个个字符串(character string),对字符串的操作和处理是数据处理中的重要内容,我们在介绍数据处理时再来介绍。
向量的下标是正整数时,返回该位置的分量:
x <- 1:10
x[5]
[1] 5
向量的下标是负整数时,返回去除了该位置的分量的整个向量:
x[-1]
[1] 2 3 4 5 6 7 8 9 10
向量的下标还可以接受一个正整数或者负整数构成的向量,返回所有正整数位置的分量,或者去除所有负整数位置的分量(但要注意不能同时包含正整数和负整数):
x[c(1,1,5)]
[1] 1 1 5
x[-c(2,6)]
[1] 1 3 4 5 7 8 9 10
向量下标还可以接受一个等长的逻辑向量,例如原向量的一个逻辑运算或者操作,来对数据进行筛选:
# 返回 x 中大于5 的全部元素
x[x>5]
[1] 6 7 8 9 10
这等价于使用which()函数:
x[which(x>5)]
[1] 6 7 8 9 10
数组(array)是一种有多个维度的原子向量,和向量相比,它多了一个维度属性dim,此外,还可以指定各维度的名称(标识符)参数dimnames来创建dimnames属性。
创建数组使用array()函数,语法为:
{array(data = NA, dim = NULL, dimnames = NULL)}
参数data接受用于创建数组的数据向量,dim接受指定维数的向量,dimnames接受指定各维度的名称(标识符)的列表。
dname1 <- c('A','B','C')
dname2 <- c('D1','D2','D3')
dname3 <- c('d1','d2','d3')
A = array(1:27, c(3,3,3), list(dname1,dname2,dname3))
A
, , d1
D1 D2 D3
A 1 4 7
B 2 5 8
C 3 6 9
, , d2
D1 D2 D3
A 10 13 16
B 11 14 17
C 12 15 18
, , d3
D1 D2 D3
A 19 22 25
B 20 23 26
C 21 24 27
dim()和dimnames()函数分别用于获取数组的维数和各维度名称,分别返回一个向量和一个列表。
dim(A)
[1] 3 3 3
dimnames(A)
[[1]]
[1] "A" "B" "C"
[[2]]
[1] "D1" "D2" "D3"
[[3]]
[1] "d1" "d2" "d3"
数组的转置:通过函数aperm()实现,不常用到,不在此处展开。
注意:R 的数组与 Python 的 NumPy 包中的
ndarray排列维度的顺序不一样,要区分清楚。
矩阵实际上就是2维的数组,同时具有matrix和array两个类属性。但是矩阵的用途比数组广泛得多,因此
R 有一系列专门的矩阵函数和矩阵操作。
创建矩阵使用matrix()函数,其基本语法为:
{matrix(data = NA, nrow = NULL, ncol = NULL, byrow = FALSE, dimnames = NULL)}
参数data接受用于创建矩阵的数据向量,nrow和ncol分别接受行数和列数(可以省略其中的一个让函数自动匹配另一个),逻辑值参数byrow指定向量按行排列还是按列排列,默认取False,dimnames接受指定各行和各列名称(标识符)的列表。
M <- matrix(1:6, nrow = 2)
M
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
因为矩阵就是数组,所以数组函数自然也适用于矩阵。此外,下表中列出了 R 中一些常用的矩阵函数:
| 函数名 | 功能 |
rbind() |
将两个或多个矩阵或向量按行堆叠成一个矩阵 |
cbind() |
将两个或多个矩阵或向量按列堆叠成一个矩阵 |
rownames() |
返回矩阵各行的名称,可以用来为矩阵指定行名称 |
colnames() |
返回矩阵各列的名称,可以用来为矩阵指定列名称 |
nrow() |
返回矩阵的行数 |
ncol() |
返回矩阵的列数 |
t() |
矩阵转置 |
inverse() |
求矩阵的逆矩阵或者广义逆 |
det() |
求矩阵的行列式 |
diag() |
提取矩阵对角元,或者由向量创建对角矩阵 |
eigen() |
求矩阵的特征值和特征向量 |
solve() |
求线性方程组的解 |
矩阵的基本数学运算符号和函数都与数值型向量相同,但需要注意,矩阵乘法需要使用符号%*%,直接使用*进行的是按位乘法。
矩阵下标的用法和向量下标类似,行索引和列索引用逗号分隔开(逗号不能省略),省略行索引或者列索引则表明包含全部的行或列,下面的例子可能更直观一些。
返回矩阵 M 的第1行第2,3列的元素:
M[1,2:3]
[1] 3 5
返回矩阵 M 的第3列的元素:
M[,3]
[1] 5 6
如果有行名或者列名,还可以用行名或者列名的字符串作为下标:
rownames(M)<- c('r1','r2')
M['r1',]
[1] 1 3 5
和向量一样,矩阵也接受逻辑值下标,但会返回一个向量:
M[M>2]
[1] 3 4 5 6
向量和矩阵的元素都可以通过使用下标来修改,例如,将矩阵 M 的第2列改为0:
M[,2] <- 0
M
[,1] [,2] [,3]
r1 1 0 5
r2 2 0 6
数据框是 R 中最重要的数据结构之一,如果只考虑 base
R,甚至可以去掉之一。作为一种二维的泛型向量数据结构,它相当于矩阵的一种延申,是
R
中最常用的数据结构。数据框相当于一种特殊的、形状规整的矩形列表,所以数据框的类属性是data.frame,但存储类型是list。
数据框的属性和矩阵属性类似,只不过列名属性是names而非colnames,用于得到矩阵维数的函数dim(),nrow(),ncol()也都适用于数据框。
数据框的创建使用data.frame()函数,其基本语法如下:
data.frame(..., row.names = NULL, check.rows = FALSE,
check.names = TRUE, fix.empty.names = TRUE,
stringsAsFactors = FALSE)
注意到没有names参数,...部分可以直接输入数据表或者矩阵,创建数据框后可以用names()函数指定列名names;也可以用tag = value的形式(此处不能使用<-赋值号)输入多组标签-值对,以标签作为列名;或者输入多个向量,以每个向量名作为相应的列名。
A <- matrix(1:6,2,3)
data1 <- data.frame(A)
names(data1) <- c('a','b','c')
data2 <- data.frame('A' = A[,1], 'B' = A[,2], 'C' = A[,3])
col_a <- 1:2
col_b <- 3:4
col_c <- 5:6
data3 <- data.frame(col_a,col_b,col_c)
data1
data2
data3
数据框的每一列都必须是同一种存储类型,不同列之间可以是不同存储类型,这种结构与大多数情况下的截面数据一致,这也是数据框被广泛使用的原因,数据框的一列也就是截面数据中的一个变量,因此列名也常被称为变量名。
数据框与矩阵结构类似,因此矩阵的下标运算也都适用。
data1[1,2]
[1] 3
但需要注意的是,如果只使用一个下标(单个整数或单个向量),数据框与矩阵的行为就不同了:矩阵会(按列)拉直为向量,然后按向量下标来处理;而数据框会把单个的下标理解为对列的索引,也就是数据框[索引值]和数据框[,索引值]调用的元素是一样的(只不过前者是数据框,后者是向量)。当然由矩阵的列名索引方式可以得知,数据框[列名]和数据框[列名]也是一样的(同样地,前者是数据框,后者是向量)。
data1[2]
data1[,2]
[1] 3 4
注意:Python 的 pandas 包中的DataFrame数据框是仿照 R
数据框设计的,但DataFrame会把单个的下标理解为对行的索引,这是由于
Python 的 NumPy 和 pandas 都默认把向量当作行向量,而 R
默认把向量当作列向量。
不过毕竟矩阵的下标运算不是为了数据框设计的,数据框有自己的规范的索引方法。
数据框中的列可以用$操作符来调用,其语法为数据框$变量名(这实际上是列表的语法)。
data2$C
[1] 5 6
如果不想每次调用变量都带上数据框名,可以使用attach()函数,相当于进入了数据框的内部环境,使用过后别忘了使用detach()函数退出,以免引起可能的变量名冲突:
attach(data1)
a
[1] 1 2
detach(data1)
a
Error: object 'a' not found
这两个函数也适用于列表。
或者使用with()函数创建一个使用数据框内部变量的独立环境,使用特殊赋值符<<-还可以将变量保存至外部环境:
with(data1, {
c_double <- 2*c
c_sq <<- c^2
})
c_double
Error: object 'c_double' not found
c_sq
[1] 25 36
而要进行较为复杂的数据选择,可以使用subset()函数,其语法为:
subset(x, subset, select, drop = FALSE, ...)
x接受数据框,subset接受行名或者用于筛选的逻辑表达式,select接受变量名。
data_matrix <- matrix(1:25,5)
data <- data.frame(data_matrix)
names(data) <- c('a','b','c','d','e')
data
subset(data, subset = c > 12, select = c('a','b'))
列表是 R
中最复杂的数据结构,数据框只是列表的一个特例。数据框由不同类型的等长向量按列并排构成,而列表可以由任意
R
对象(向量、矩阵、数组、数据框甚至是其他列表)构成,它会为每一个对象创建一个索引,也就是名称标签,这些标签构成了列表的名称属性names。和数据框的变量名属性类似,列表的names属性可以通过names()函数来查看和修改。
列表的创建使用list()函数,其接受对象和names属性的的方式与data.frame()函数类似。
obj1 <- 1:5
obj2 <- 'Hello world!'
obj3 <- matrix(1:4,2)
obj4 <- array(1:24,c(2,3,4))
obj5 <- data.frame(name=c('张三','李四','王五'), age=c(23,24,25))
obj6 <- list(day <- c(1,3), month <- c('Feb','May','Aug'))
LIST <- list(vec=obj1, char=obj2, mat=obj3, arr=obj4, df=obj5, ls=obj6)
LIST
$vec
[1] 1 2 3 4 5
$char
[1] "Hello world!"
$mat
[,1] [,2]
[1,] 1 3
[2,] 2 4
$arr
, , 1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
, , 2
[,1] [,2] [,3]
[1,] 7 9 11
[2,] 8 10 12
, , 3
[,1] [,2] [,3]
[1,] 13 15 17
[2,] 14 16 18
, , 4
[,1] [,2] [,3]
[1,] 19 21 23
[2,] 20 22 24
$df
$ls
$ls[[1]]
[1] 1 3
$ls[[2]]
[1] "Feb" "May" "Aug"
列表中对象的调用可以使用$操作符的形式列表$对象,也可以使用双层方括号的形式列表[[对象]]:
LIST$char
[1] "Hello world!"
LIST[["mat"]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
如果不显式地指定names属性,列表就会用数字索引来标识每个对象,此时只能用双层方括号+数字索引来调用对象(有names属性时这种方法也可用):
l = list(1:3,c('a','b'))
l
[[1]]
[1] 1 2 3
[[2]]
[1] "a" "b"
l[[2]][1]
[1] "a"
从数据是否可以定量地进行数学运算来划分,数据分为和定量数据和定性数据。不能简单地以是否表示为数值来区分定量数据和定性数据,因为定性数据也可以使用数值来表示,甚至数字的相对大小可以有意义:例如对一家饭店的评价分为三档:差、一般、好,就可以分别用 0-2 的整数来表示,较大的数字对应较高的评分,但对这种数字进行数学运算显然是没有意义的。对数据类型的分类不属于本篇范畴,这里仅给出一张分类示意图,更多的内容将在数据处理部分进行介绍。
回到 R 数据结构中来,因子就是 R
中专门用来表示定性变量(定性数据构成的变量)的一种数据结构,它是一种特殊的向量,其类属性为factor,其中定性变量每一个可能的取值称为一个水平(level),所有水平的向量构成了因子的水平属性level,用level()函数可以查看一个因子的水平。注意:因子只是一个单独定义的类,而不是数据的储存类型,因为定性数据也往往是以数字或者字符表示的。因子向量的存储类型为integar,而因子内部的水平向量的存储类型为character,这与因子水平在形式上是以数字还是字符表示无关,通过一个内部函数,因子可以将
\(1\sim n\) 的正整数与 \(n\) 个表示水平取值的字符关联起来。
factor()函数创建因子因子的创建可以使用factor()函数,其语法为:
factor(x, levels, labels = levels,
exclude = NA, ordered = is.ordered(x), nmax = NA)
x接受数据向量:
a <- factor(c('b','a','c'))
a
[1] b a c
Levels: a b c
可选参数level接受指定的因子水平向量及顺序,长度不超过数据向量中不同值的个数(如果长度小于数据向量中不同值的个数,匹配不上的位置会转换成缺失值NA),默认值为as.character(x),也即将x的所有不同取值转化为字符。默认情况下因子水平按照首字母顺序和数字顺序排序。
b <- factor(1:5,levels = 1:2)
b
[1] 1 2 <NA> <NA> <NA>
Levels: 1 2
可选参数label用于因子水平的命名以及精简,它接受一个与level等长的向量(level显式指定或者缺省均如此),不同值按照位置对应level向量中不同的水平,可以使用重复的值来让多个因子水平对应一个水平名,从而把多个因子水平整合成一个;或者,也可以使用单个字符串,factor()函数会自动在字符串后面加上正整数来匹配level向量的长度。
说起来可能比较复杂,还是看例子直观一些:
f1 <- factor(c(1,2,1,2,1), levels = c(2,1), labels = 'level')
f1
[1] level2 level1 level2 level1 level2
Levels: level1 level2
f2 <- factor(c(1,2,3,2,1), levels = c(3,2,1),labels = c('female','female','male'))
f2
[1] male female female female male
Levels: female male
f3 <- factor(c(1,2,3,2,1), labels = c('female','female','male'))
f3
[1] female female male female female
Levels: female male
可选参数exclude给出了需要从输入向量中去除的值,默认值为缺失值NA,也即默认去除缺失值;如果要保留缺失值作为一个水平,可以将其设为NULL。
f4 <- factor(c(1,2,1,2,3), labels = c('boys','girls'), exclude = 3)
f4
[1] boys girls boys girls <NA>
Levels: boys girls
f5 <- factor(c(1,2,1,2,NA), labels = c('boys','girls'))
f5
[1] boys girls boys girls <NA>
Levels: boys girls
f6 <- factor(c(1,2,1,2,NA), labels = c('boys','girls','missing data'), exclude = NULL)
f6
[1] boys girls boys girls missing data
Levels: boys girls missing data
可选参数ordered用来指定是否创建定序变量,它接受一个逻辑值,TRUE和FALSE分别对应因子水平是否是有序的,默认取FALSE创建无序因子。
f7 <- factor(c('a','b','c'), ordered = T)
f7
[1] a b c
Levels: a < b < c
f8 <- factor(c('a','b','c'), levels = c('c','a','b'), ordered = T)
f8
[1] a b c
Levels: c < a < b
最后,可选参数nmax指定最大水平数,在数据向量的不同值的数量过多时,可以用来节省内存。
cut()函数创建因子当数据向量是数值向量时,可以使用cut()函数将数据的范围划分为多个区间来创建一个因子向量,每个区间对应一个因子水平。其基本语法为:
cut(x, breaks, labels = NULL,
include.lowest = FALSE, right = TRUE, dig.lab = 3,
ordered_result = FALSE, ...)
参数x用于接受数据向量;
参数breaks用于接受全部区间的端点构成的递增数值向量,或者要划分的区间数(大于等于2);
参数labels的作用和在factor()函数中相同,用于指定水平名称,默认以区间作为水平名称;
逻辑值参数right表示区间是左开右闭还是左闭右开,默认取TRUE(左开右闭);
参数right取TRUE时,逻辑值参数include.lowest用于表示是否包含最小区间的下限,参数right取FALSE时,逻辑值参数include.highest用于表示是否包含最大区间的上限;
逻辑值参数ordered_result表示是否为有序因子,默认取FALSE。
right的不同设置,cut()函数划分的区间是左开右闭或是左闭右开的,所以参数breaks接受区间的端点时,下限要低于数据中的最小值,或者上限要高于数据中的最大值,以包含全部数据;breaks接受划分的区间数时,默认会在数据最小值和最大值之间进行等分,并且会自动将下限调低一点,上限调高一点,此时如果不指定参数labels,微调的数值会作为区间下限和上限显示在因子水平里,参数dig.lab用来调整水平中显示的微调精度,默认值为3,同时也是最大精度。下面来看例子:
c1 <- cut(1:5, c(1,3,5))
c1
[1] <NA> (1,3] (1,3] (3,5] (3,5]
Levels: (1,3] (3,5]
c2 <- cut(1:5, c(1,3,5), right = F)
c2
[1] [1,3) [1,3) [3,5) [3,5) <NA>
Levels: [1,3) [3,5)
c3 <- cut(1:5, c(1,3,5), include.lowest = T)
c3
[1] [1,3] [1,3] [1,3] (3,5] (3,5]
Levels: [1,3] (3,5]
c4 <- cut(1:5, 4)
c4
[1] (0.996,2] (0.996,2] (2,3] (3,4] (4,5]
Levels: (0.996,2] (2,3] (3,4] (4,5]
c5 <- cut(1:5, 4, labels = c('a','b','c','d'), ordered_result = T)
c5
[1] a a b c d
Levels: a < b < c < d
gl()函数创建因子想要从零开始快速地创建一个各水平重复次数相同的因子,可以使用gl()函数。尽管局限性较强,但在没有数据,又需要临时使用一种结构简单的因子时,gl()函数是一个不错的选择。其语法为:
gl(n, k, length = n*k, labels = seq_len(n), ordered = FALSE)
参数n和k分别指定了因子的水平数和每个水平重复的次数;参数length指定了因子向量的长度,默认取n*k;参数labels指定了水平名称,默认为
\(1\sim n\)
的正整数;逻辑值参数ordered用于指定因子是否有序。
gl(3,2, labels = c('a','b','c'), ordered = T)
[1] a a b b c c
Levels: a < b < c
tibble数据框tibble数据框是tibble包中的一种数据结构,tibble包是现代化R数据科学包集合——tidyverse的核心包之一。tibble数据框是对
R
base包自带的data.frame数据框的改进和简化,使其更符合现代数据科学工作者的思维。tibble数据框同时具有三种类属性:tbl_df,tbl和data.frame。
tibble数据框的创建tibble数据框的创建使用tibble()函数,其基本语法如下:
tibble(
...,
.rows = NULL,
.name_repair = c("check_unique", "unique", "universal", "minimal")
)
与data.frame()函数不同,tibble()函数的数据输入只接受tag = value的形式,即使没有指定tag,tibble()函数也会认为是tag缺失而自动填充一个tag,而不会认为只传递了一个对象作为value。
data.frame数据框的区别tibble数据框与data.frame数据框的一些区别如下:
tibble数据框的打印格式更紧凑,数据集较大时便于展示
tibble数据框不能把字符型变量自动转换为因子,而data.frame数据框可以根据需要使用逻辑值参数stringsAsFactors来指定,默认取FALSE(R
4.0.0 的改动)
当变量名不符合 R
变量命名规则(比如含有空格)时,data.frame数据框会自动进行修饰使之符合命名规则(比如将空格替换成点),而tibble数据框会保留原变量名,并且支持使用反引号来指定不符合变量命名规则的变量名
names(data.frame(`crazy name` = 1))
[1] "crazy.name"
names(tibble(`crazy name` = 1))
[1] "crazy name"
tibble数据框没有行名属性,也不支持给行命名
tibble数据框使用下标索引,返回结果的类型仍然是tibble数据框,即使索引到单个元素也不例外
tibble数据框的列是按顺序惰性生成的,因此在创建后面的列时可以引用前面的列
想了解更多tibble数据框的特性,可以使用vignette("tibble")可以查看tibble包自带的简介文档。