上篇推文介绍了R语言环境是通过数据结构区分同名函数的,本篇来详细介绍数据结构。

R语言能操作的数据结构有很多种,但基础的有如下5种:

  • 原子向量(atomic vector)
  • 矩阵(matrix)
  • 数组(array)
  • 数据框(data.frame)
  • 列表(list)

其中原子向量又是最基础的一个,共有6种类型:

  • 逻辑型(logical)
  • 整型(integer)
  • 数值型或双精度型(numeric或double)
  • 复数型(complex)
  • 字符串型(character)
  • 原始型(raw)

本篇推文部分内容可见如下链接的视频:

https://www.bilibili.com/video/BV1KS4y1h7R5/

1 原子类型

1.1 各类型简介

逻辑型只有两个取值情况:TRUE(是)和FALSE(否),一般简写作TF;分别对应数字1和0,可以进行数值运算。

T + 1
## [1] 2

整型即整数形式的数字。

as.integer(1) 
## [1] 1

as.integer(2.9)
## [1] 2

另外,上述原子类型不包括因子(factor)类型,学堂君不确定其是否属于原子类型,在vector()函数的帮助文档中提及的原子类型确实没有它。但有一点可以肯定,R语言的因子是以整型的形式存储的,各类别依次对应整数1、2、3、...,“类别名称”不过是给整型数字加的标签。具体见如下推文:

双精度型是实数的默认储存形式,又称数值型。

class(1.1)
## [1] "numeric"

class(2)
## [1] "numeric"

复数型即数学上的复数——实数和虚数的总称。

complex(real = 2, imaginary = 4)
## [1] 2+4i

字符串型即文本,开始和结尾处需加引号(单引号或双引号均可,但前后需要保持一致),引号本身不属于其内容。

"Learn R Language"
## [1] "Learn R Language"

'Learn R Language'
## [1] "Learn R Language"

'Learn R Language"
## Error: Incomplete expression: 
## 'Learn R Language"

原始型用于储存原始字节(raw bytes),学堂君没有用过这个类型,也不太了解。举个例子,语句as.raw(40)输出的是数字40的十六进制形式。

as.raw(40)
## [1] 28

# 不能进行数值运算
as.raw(40) + 2
## Error in as.raw(40) + 2 : non-numeric argument to binary operator

1.2 相关函数

这里介绍与原子类型相关的三类函数。

第一类:函数名同原子类型,一般用于定义一定程度的原子类型

complex()函数外,以下其他函数都只有一个指定向量长度的参数,元素均为各类型的默认值。

logical(length = 3)
## [1] FALSE FALSE FALSE

integer(length = 3)
## [1] 0 0 0

numeric(length = 3)
## [1] 0 0 0

double(length = 3)
## [1] 0 0 0

complex(length.out = 3, real = 1:3, imaginary = 3:1)
## [1] 1+3i 2+2i 3+1i

character(length = 3)
## [1] "" "" ""

raw(length = 3)
## [1] 00 00 00

第二类:函数名为is.原子类型,用于判断对象是否属于某原子类型

这类函数的功能在于判断“对错”,输出结果为逻辑型。

is.logical(T)
## [1] TRUE

is.integer(1)
## [1] FALSE

is.numeric(1)
## [1] TRUE

is.character("Learn R Language")
## [1] TRUE

第三类:函数名为as.原子类型,用于将其他原子类型转换成指定原子类型

比如,数字默认情况下是双精度型:

as.integer(1) 
## [1] 1
class(as.integer(1))  
## [1] "integer"

as.character(1)
## [1] "1"
class(as.character(1))
## [1] "character"

as.logical(1)
## [1] TRUE
class(as.logical(1))
## [1] "logical"

2 数据结构

2.1 各数据结构概述

2.1.1 原子向量

原子向量是由任意一种原子类型组成的向量(以下简称向量),它是单维同质结构的数据形式。使用c()函数进行定义。

c(1:4)
## [1] 1 2 3 4

c("Learn", "R", "Language")
## [1] "Learn"    "R"        "Language"

向量的同质性体现在它的所有元素必须是同一种原子类型。

如下例,由于字符串型元素的存在,R会把数字1也强制转成字符串型:

c(1, "R")
## [1] "1" "R"

向量元素的查询符号为[],下标(或称索引)从1开始计数,即第一个元素的下标就是1,依次类推(很多编程语言的下标是从0开始),它的单维性体现在下标只有1个数字。

v <- c(1:5)
v[1]
## [1] 1

v[5]
## [1] 5

同时查询多个位置的元素:

v[c(1,3,5)]
## [1] 1 3 5

向量元素可通过符号<-=进行更改。

v[5] <- 0
v[5]
## [1] 0

当用户提供的下标超过向量的最大下标时(即下标出界):若是查询会返回NA;若是修改则向量长度会自动扩展至该下标。

v[10]
## [1] NA

v[10] <- 10
v
##  [1]  1  2  3  4  0 NA NA NA NA 10

2.1.2 矩阵

矩阵是二维同质结构的数据形式。使用matrix()函数定义,语法结构如下:

matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
       dimnames = NULL)

示例:

matrix(c(1:12), nrow = 3, byrow = T)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12

矩阵元素的查询符号也是[],它的二维性体现在下标一般有两个数字,其中第一个数字表示行标、第二个数字表示列表。

m <- matrix(c(1:12), nrow = 3, byrow = T)
m[2,3]
## [1] 7

但也可使用单下标查询或修改矩阵元素,编号按先列后行排序。

m[3]
## [1] 9

m[4]
## [1] 2

若要查询整行元素,则省略列标;反之,查询整列元素,则省略行标。

m[2,] 
## [1] 5 6 7 8

m[,3:4]
##      [,1] [,2]
## [1,]    3    4
## [2,]    7    8
## [3,]   11   12

矩阵也具有同质性。如下例,如果有元素被修改成字符串,所有元素都会被强制转为字符串:

m[1] <- "1"
m
##      [,1] [,2] [,3] [,4]
## [1,] "1"  "2"  "3"  "4" 
## [2,] "5"  "6"  "7"  "8" 
## [3,] "9"  "10" "11" "12"

与向量不同的是,矩阵不允许查询或修改的下标出界。

m[4,5]
## Error in `[<-`(`*tmp*`, 4, 5, value = 5) : subscript out of bounds

2.1.3 数组

数组相当于是多维矩阵,是多维(不定维)同质结构的数据形式,不太常用。

如下例,c(1:12)表示组成数组的原子成分(atomic components);c(2,2,3)的长度是3表示数组是三维的,它的元素表示对应维度的长度:

array(c(1:12), c(2,2,3))
## , , 1
## 
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## , , 2
## 
##      [,1] [,2]
## [1,]    5    7
## [2,]    6    8
## 
## , , 3
## 
##      [,1] [,2]
## [1,]    9   11
## [2,]   10   12

数组元素的查询符号也是[],下标数等于维数。

a = array(c(1:12), c(2,2,3))
a[1,2,3]
## [1] 11

a[1,,3]
## [1]  9 11

2.1.4 数据框

数据框是二维异质结构的数据形式,类似于Excel表格,是对用户来说最友好的数据结构。一般来说,数据框的列表示变量,行表示样本。

数据框的每个列相当于一个向量,组成元素的原子类型相同;它的异质性体现在不同列的原子类型可以不同。

# x列为数值,y列是字符串
d <- data.frame(
  x = c(1:5),
  y = c("1", 2:5))
d
##   x y
## 1 1 1
## 2 2 2
## 3 3 3
## 4 4 4
## 5 5 5

可以使用符号[]查询数据框元素。

d[2,1]
## [1] 2

如果使用单下标查询数据框返回的是对应的整列元素(如果省略行标或列表,中间有逗号隔开仍然属于双下标),也就是说矩阵的每列相当于一个元素(每列本身是一个原子向量),这一点和矩阵有很大不同。

d[2]
##   y
## 1 1
## 2 2
## 3 3
## 4 4
## 5 5

此外,数据框必须有列名。可以使用符号$根据列名进行元素查询或修改:

d$y
## [1] "1" "2" "3" "4" "5"

2.1.5 列表

列表是单维异质结构的数据形式。

列表的异质性体现在它可以任意嵌套其他形式的数据结构作为它的元素:

# 把前面定义的数据对象作为列表的元素
list("1", 2, v, m, d)
## [[1]]
## [1] "1"
## 
## [[2]]
## [1] 2
## 
## [[3]]
##  [1]  1  2  3  4  0 NA NA NA NA 10
## 
## [[4]]
##      [,1] [,2] [,3] [,4]
## [1,] "1"  "2"  "3"  "4" 
## [2,] "5"  "6"  "7"  "8" 
## [3,] "9"  "10" "11" "12"
## 
## [[5]]
##   x y
## 1 1 1
## 2 2 2
## 3 3 3
## 4 4 4
## 5 5 5

列表元素的查询符号是[[]]

l = list("1", 2, v, m, d)

l[[3]]
##  [1]  1  2  3  4  0 NA NA NA NA 10

可以使用嵌套的形式查询组成列表元素的成分。如上例中,列表l的第3、4个元素本身又是一个向量和矩阵:

l[[3]][3] 
## [1] 3

l[[4]][1,2]
## [1] "2"

相对于数据框,列表的异质性更彻底,非常便于存储数据(不需要考虑数据是否同质),很多非基础的数据结构就是以列表为基础改进的。

列表不允许查询下标出界,但是修改下标可以出界。

l[[10]]
## Error in l[[10]] : subscript out of bounds

l[[10]] <- mtcars
l[[10]]
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1

2.2 相关函数

2.2.1 三类函数

除原子向量外,与数据结构相关的也有三类函数:

  • 第一类:与数据结构同名,用于定义相应的数据结构;
  • 第二类:is类函数,用于判断对象是否属于某种数据结构;
  • 第三类:as类函数,用于将其他数据结构转换成指定数据结构。
is.matrix()
is.array()
is.data.frame()
is.list()

as.matrix()
as.array()
as.data.frame()
as.list()

与原子向量有关的函数参见上节“原子类型”中的相关函数。

需要注意的是,这些数据结构并不一定能相互转换,如果不能转换则函数报错。一般来说,同质型数据结构之间可以相互转换,同质型数据能够转换成异质型数据结构,所有类型的数据结构都能转换成列表。

as.numeric(d)
## Error: 'list' object cannot be coerced to type 'double'

as.list(m)
## [[1]]
## [1] "1"
## 
## [[2]]
## [1] "5"
## 
## [[3]]
## [1] "9"
## 
## [[4]]
## [1] "2"
## 
## [[5]]
## [1] "6"
## 
## [[6]]
## [1] "10"
## 
## [[7]]
## [1] "3"
## 
## [[8]]
## [1] "7"
## 
## [[9]]
## [1] "11"
## 
## [[10]]
## [1] "4"
## 
## [[11]]
## [1] "8"
## 
## [[12]]
## [1] "12"


as.list(d)
## $x
## [1] 1 2 3 4 5
## 
## $y
## [1] "1" "2" "3" "4" "5"

2.2.2 vector类函数

as.vector()函数为例,它并不一定会把其他数据结构转换成原子向量。这里的vector除指原子向量外,还可指列表和表达式(expression)。

如果原数据结构是同质的(如矩阵、数组),as.vector()函数会将它转成原子向量。

as.vector(m)
##  [1] "1"  "5"  "9"  "2"  "6"  "10" "3"  "7"  "11" "4"  "8"  "12"

class(as.vector(m))
## [1] "character"

as.vector(a)
##  [1]  1  2  3  4  5  6  7  8  9 10 11 12

class(as.vector(a))
## [1] "integer"

如果原数据结构是异质的(如数据框),as.vector()函数会将它转成列表。

as.vector(d)
## $x
## [1] 1 2 3 4 5
## 
## $y
## [1] "1" "2" "3" "4" "5"

class(as.vector(d))
## [1] "list"

2.2.3 unlist()函数

unlist()函数的作用是将列表或其他数据结构转换成原子向量,向量元素由列表的所有原子成分组成。

如果列表本身的元素是异质的,那么函数会将所有原子成分转成字符串型后再输出。

unlist(l)
##  "1"  "2"  "1"  "2"  "3"  "4"  "0"   NA   NA   NA   NA "10"  "1"  "5"  "9"  "2" 
##                                           x1   x2   x3   x4   x5   y1   y2   y3 
##  "6" "10"  "3"  "7" "11"  "4"  "8" "12"  "1"  "2"  "3"  "4"  "5"  "1"  "2"  "3" 
##   y4   y5 
##  "4"  "5"

unlist()函数也可用于把其他数据结构转成原子向量。

unlist(d)
##  x1  x2  x3  x4  x5  y1  y2  y3  y4  y5 
## "1" "2" "3" "4" "5" "1" "2" "3" "4" "5"