第 2 章 数据结构

最基础的数据结构包括:向量、列表、矩阵、数组与数据框,下面分别介绍。

2.1 向量

2.1.1 用数据创建向量

向量中的元素必须是同一种数据类型,简单向量中的元素为原子类型,复杂向量中的元素可以是列表等。

原子类型或原子模式指的是以下6种数据类型中的一种:整数(integer)、浮点数(double)、复数(complex)、字符串(character)、逻辑值(logical)、原生型(raw),。本章仅讲解简单向量,且不讲解原生型。

当需要用已知的数据创建向量时,用函数c,参数为各个元素,如a <- c(1.2,3.5,pi)b <- c("Apple","Baidu","Chrome")d <- c(TRUE,TRUE,FALSE)

当逻辑值和整数放在一起时,逻辑值会转化为整数(TRUE转化为1,FALSE转化为0)。当整数与浮点数放在一起时,整数会转化为浮点数。当浮点数与复数放在一起时,浮点数会转化为复数。当其他类型和字符串放在一起时,其他所有类型都会转化为字符串。(输出优先级可表示为 NULL < raw < logical < integer < double < complex < character < list < expression)。

向量的元素可以具有名字,也可以部分元素有名字,部分元素没有,不同元素可以有相同的名字,如

a <- c(x1=1.2,x2=3.5,x3=pi)
a
##       x1       x2       x3 
## 1.200000 3.500000 3.141593
b <- c(x1=1.2,3.5,x1=pi)
b
##       x1                x1 
## 1.200000 3.500000 3.141593

2.1.2 向量的串联

函数c的参数不仅可以是单个元素,也可以是向量,这时,会将多个向量串联起来组成一个向量。当然,不同向量也需要是相同类型,否则会进行类型转换。如

a <- c(x1=1.2,x2=3.5,x3=pi)
b <- c(x1=1.2,3.5,x3=pi)
d <- c(a,b)
d
##       x1       x2       x3       x1                x3 
## 1.200000 3.500000 3.141593 1.200000 3.500000 3.141593

2.1.3 向量的初始化

当向量中的元素还未知具体数值,需要先创建一个初始化的向量时,创建的方法为vector(mode = "logical", length = 0),其中mode可选的参数包括“logical”, “integer”, “numeric” (等同于“double”), “complex”, “character” 和 “raw”。length参数表示向量长度。

其他方法还有

  • 如果向量类型为整数型:integer(length = 0)
  • 如果向量类型为数值型:double(length = 0)或numeric(length = 0)
  • 如果向量类型为复数型:complex(length.out = 0)
  • 如果向量类型为字符串型:character(length = 0)
  • 如果向量类型为逻辑型:logical(length = 0)
  • 如果向量类型原生型:raw(length = 0)

2.1.4 特殊规律的向量的创建

2.1.4.1 元素全同的向量

用rep函数,如

rep(1L,5)
## [1] 1 1 1 1 1
rep(3.2,5)
## [1] 3.2 3.2 3.2 3.2 3.2
rep(1+5i,5)
## [1] 1+5i 1+5i 1+5i 1+5i 1+5i
rep("app",5)
## [1] "app" "app" "app" "app" "app"

rep函数还有更多更复杂的用法,可以产生更多有规律重复的数据,感兴趣的读者可以查看rep函数的帮助文档。

2.1.4.2 创建等差数列

  • 连续整数:用冒号连接连续整数的首尾,如1:5,2:7,0:8,-3:7,6:-2,可以是正序,也可以是倒序,返回值包含首尾。结尾的数可以不是整数:当开头是整数,结尾不是整数时,返回不超过结尾的序列,如6:pi返回c(6,5,4),1:pi返回c(1,2,3)。

  • 公差为1或-1的实数等差序列:上面的用法,开头也可以不是整数而是一般实数,则序列的首项为开头数值,如
    • 3.14:6返回c(3.14,4.14,5.14)
    • 3.14:6.14返回c(3.14,4.14,5.14,6.14)
    • 3.14:6.1返回c(3.14,4.14,5.14)
    • 3.14:6.2返回c(3.14,4.14,5.14,6.14)
    • 3.14:-6.2返回c(3.14,4.14,5.14,6.14)
  • 任意公差的等差数列:用seq函数,其用法为:seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)),length.out = NULL, along.with = NULL, ...),其中from代表首项,to代表尾项,by代表公差,length.out代表项数。也可以不提供length.out而提供along.with,则项数用赋给along.with的参数的长度,典型的几种用法如下:

    seq(from, to)
    seq(from, to, by= )
    seq(from, to, length.out= )
    seq(from,  by= , length.out= )
    seq(by= , length.out= ,to=)
    • seq(from, to):不指定公差,则公差默认为1或-1,与from:to的效果相同。
    • seq(from, to, by= ):公差由by指定,如果to-from不是by的整数倍,则序列尾项不超过to。如果to-fromby异号,则报错。如seq(from=1, to=6, by=2)返回c(1,3,5)seq(from=0, to=3*pi, by=pi)返回c(0.000000, 3.141593, 6.283185, 9.424778)seq(from=1, to=6, by=-2)报错。
    • seq(from, to, length.out= ):项数由length.out指定,一般其为正整数,如果为负数,会报错,如果是小数,会四舍五入到整数,length.out通常简写为lengthlen。序列在fromto中均匀分布,即公差被定为by = ((to - from)/(length.out - 1)seq(from=0, to=5, length=5)返回c(0.00, 1.25, 2.50, 3.75, 5.00)seq(from=0, to=5, length.out=5.5)返回c(0, 1, 2, 3, 4, 5)
    • seq(from, by= , length.out= ):已知首项、公差和项数,如seq(from=0,by=2,length=5)返回c(0, 2, 4, 6, 8)
    • seq(by= , length.out= ,to=):已知公差、项数和尾项,如seq(by=2,length=5,to=0)返回c(-8, -6, -4, -2, 0)

2.1.4.3 创建等比数列

R语言基础包中没有直接创建等比数列的函数,我们可以用一些函数的组合来实现:

  • 已知首项a1,公比q和项数n,则序列可写为a1*q^(0:(n-1)),如5.2*10^(0:(5-1))返回c(5.2, 52.0, 520.0, 5200.0, 52000.0)
  • 已知尾项an,公比q和项数n,则序列可写为an/(q^((n-1):0)),如1024/(2^((5-1):0))返回c(64, 128, 256, 512, 1024)
  • 已知首项a1,尾项an,公比q,则序列可写为a1*q^(0:floor(log(an/a1,q))),如10*2^(0:floor(log(1000/10,2)))返回c(10, 20, 40, 80, 160, 320, 640)
  • 已知首项a1,尾项an,项数n,则序列可写为a1*((an/a1)^(1/(n-1)))^(0:(n-1)).

注意,上面的floor为向下取整函数,log为对数函数,第二个参数为对数的基。

2.1.5 向量元素的选取

2.1.5.1 单个元素的选取

用中括号加上元素的序号;如果元素有名字,也可以在中括号中放置元素名。如果序号超过了向量长度,则会返回NA值,如果序号为0,则返回长度为0的该类型向量,如果序号为小数,则会被函数as.integer修正为整数,即向0取整。下面是一些例子:

x <- c(1.2,3.5,pi)
x[1]
## [1] 1.2
a <- c(x1=1.2,x2=3.5,x3=pi)
a[1]
##  x1 
## 1.2
b <- c(x1=1.2,3.5,x3=pi)
b["x3"]
##       x3 
## 3.141593

R中最后一个元素的序号没有特别的定义,对向量用length函数得到(矩阵或数据框的最后一行用nrow函数)。 如下面展示了取向量最后一个元素的方法:

x <- c(1.2,3.5,pi)
x[length(x)]
## [1] 3.141593

2.1.5.2 多个元素的选取

在中括号中放置向量,向量元素为要选取的元素序号或名字组成,但序号和名字不能混用,如

x <- c(5.2, 52.0, 520.0, 5200.0, 52000.0)
x[2:4]
## [1]   52  520 5200
x[c(1,3:4)]
## [1]    5.2  520.0 5200.0
a <- c(x1=1.2,x2=3.5,x3=pi)
a[c("x1","x3")]
##       x1       x3 
## 1.200000 3.141593
# a["x1","x3"] #这种写法会报错,一定要放在一个向量内
# a["x1":"x3"]  #这种写法也会报错,"x1":"x3"的写法没有意义

b <- c(x1=1.2,3.5,x3=pi)
# b[c(1,"x3")] #序号和名字放在一个向量中时,整数会转化为字符串,而b中不存在名为"1"的元素,因而返回非数NA

如果要选取开头的n个元素,可用head函数,其用法为head(x, n = 6L, …),如果n没有指定,则默认为选取开头6个。如果要选取末尾n个元素,可用tail函数,其用法为tail(x, n = 6L, …)。下面是例子

x <- seq(1,100,2)
head(x,n=10)
##  [1]  1  3  5  7  9 11 13 15 17 19
tail(x,n=10)
##  [1] 81 83 85 87 89 91 93 95 97 99

如果想选取除了某些元素的其他元素,则可在方括号的向量前加负号表示这些是需要排除的项目,该方法只适合于知道元素序号的情况。如果排除某些名字的元素,则需要用names函数与%in%的组合,下面是例子:

x <- 1:10
x[c(-3,-7:-9)]
## [1]  1  2  4  5  6 10
b <- c(x1=1.2,3.5,x3=pi)
b[!names(b) %in% c("x1")]
##                x3 
## 3.500000 3.141593

2.1.6 向量元素的修改

将新值赋给对应元素即可。如x[1] <- 2,b[2:3] <- c(3,2)。新值个数与被赋值的元素个数可以不相同。当新值个数大于被赋值元素个数时,超出部分不起作用。当新值个数小于被赋值元素个数时,新值会被复制多份,首尾相连,然后赋给被赋值元素。特别地,当新值个数为1时,将该值赋给每一个被赋值的元素。下面是一些例子:

x <- 1:10
x[1:5] <- c(11,12)
## Warning in x[1:5] <- c(11, 12): number of items to replace is not a
## multiple of replacement length
x
##  [1] 11 12 11 12 11  6  7  8  9 10
y <- 1:10
y[6:10] <- 1:6
## Warning in y[6:10] <- 1:6: number of items to replace is not a multiple of
## replacement length
y
##  [1] 1 2 3 4 5 1 2 3 4 5
z <- 1:10
z[6:10] <- 0
z
##  [1] 1 2 3 4 5 0 0 0 0 0

可以给尚不存在的元素赋值,相当于增加了向量的长度,如:

x <- 1:5
x[6] <- 6
x
## [1] 1 2 3 4 5 6
x[10] <- 10
x
##  [1]  1  2  3  4  5  6 NA NA NA 10
x["a"] <- 8
x
##                                a 
##  1  2  3  4  5  6 NA NA NA 10  8

2.1.7 向量元素名的获取与修改

用names函数可获取向量的元素名,返回是各元素名字符串组成的向量,没定义名的为空字符串。如果一个向量,没有一个元素定义了名字,则返回空值NULL。如果需要修改元素名,直接将新元素名赋给names函数定义的位置即可。例如:

b <- c(x1=1.2,3.5,x3=pi)
names(b)
## [1] "x1" ""   "x3"
x <- 1:10
names(x)
## NULL
names(b)[2] <- "x2"
names(b)[1] <- "x1_0"
b
##     x1_0       x2       x3 
## 1.200000 3.500000 3.141593
names(x)[5] <- "x5"
x
## <NA> <NA> <NA> <NA>   x5 <NA> <NA> <NA> <NA> <NA> 
##    1    2    3    4    5    6    7    8    9   10
names(x)
##  [1] NA   NA   NA   NA   "x5" NA   NA   NA   NA   NA

2.1.8 向量元素的增加或插入

用函数c将增加了元素的向量赋给原向量,例如:

x <- 1:10

# 在开头增加一个元素0
x <- c(0,x)
x
##  [1]  0  1  2  3  4  5  6  7  8  9 10
# 在结尾增加一个元素100
x <- c(x,100)
x
##  [1]   0   1   2   3   4   5   6   7   8   9  10 100
# 在原来第4与第5个之间插入一个元素pi
x <- c(x[1:4],pi,x[5:length(x)])
x
##  [1]   0.000000   1.000000   2.000000   3.000000   3.141593   4.000000
##  [7]   5.000000   6.000000   7.000000   8.000000   9.000000  10.000000
## [13] 100.000000

2.1.9 向量元素的删除

用函数c将删除了元素的向量赋给原向量,一般会利用上面讲过的负数序号的用法,例如:

x <- c(0.5,1:10,100)

# 删除第1个元素
x <- x[-1]
x
##  [1]   1   2   3   4   5   6   7   8   9  10 100
# 删除结尾元素
x <- x[-length(x)]
x
##  [1]  1  2  3  4  5  6  7  8  9 10
# 删除第2及4~6个元素
x <- x[-c(2,4:6)]
x
## [1]  1  3  7  8  9 10
# 删除名为"x1"的元素
b <- c(x1=1.2,3.5,x3=pi)
b <- b[!names(b) %in% c("x1")]

2.1.10 条件查询

除了用元素序号和元素名选取向量中的元素外,还可以通过逻辑值来选取向量中的元素,如

x <- 1:10
x[c(T,T,F,F,F,T,F,F,F,T)]
## [1]  1  2  6 10

此时,逻辑值向量的长度必须与原向量长度相同。

利用这种元素选取的方法,我们可以做条件查询。例如:

x <- 1:10
x[x==5]
## [1] 5
x[x>3.1&x<=8]
## [1] 4 5 6 7 8

注意到,当向量与标量进行逻辑运算时,返回向量每个元素分别与该标量进行逻辑运算组成的向量。当向量与向量进行逻辑运算时,需要两个向量长度相等。如果不相等,长度短的会被复制多份,首尾相连。例如:

x <- 1:10
y <- 1:-8
z <- c(1:9,1,1)
x==y
##  [1]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
x==z
## Warning in x == z: longer object length is not a multiple of shorter object
## length
##  [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE

注意&、|、!为向量中各元素分别求“与”、“或”、“非”,而&&、||为向量第一个元素求“与”、“或”。 如果要求向量内部所有元素求“与”,用函数all,如果要求向量内部所有元素求“或”,用函数any

c(T,T,F)&c(T,F,F)
## [1]  TRUE FALSE FALSE
c(T,T,F)&&c(T,F,F)
## [1] TRUE
all(c(T,T,T))
## [1] TRUE
all(T,T,T)
## [1] TRUE
all(T,T,F)
## [1] FALSE
any(c(T,T,T))
## [1] TRUE
any(T,T,T)
## [1] TRUE
any(T,T,F)
## [1] TRUE

对于字符型或其他非数值型,我们可以用运算符%in%,判断运算符前面的对象的各元素是否在后面对象的元素中,返回值的长度总与前面向量的长度相同。如:

c("a","b") %in% c("b","d")
## [1] FALSE  TRUE
c("a","b") %in% c("b","d","a")
## [1] TRUE TRUE
c("a","b","x") %in% c("b","d")
## [1] FALSE  TRUE FALSE

如果我们不仅需要知道满足条件的值是多少,还需要知道他们在原向量中的位置,可以用which函数,其用法为which(x, arr.ind = FALSE, useNames = TRUE),第1个参数为逻辑向量,返回元素为TRUE的位置。如果仅需知道第1个为TRUE的位置,在后面加一个[1]即可,例如:

x <- 1:10
which(x>3.1&x<=8)
## [1] 4 5 6 7 8
which(x>3.1&x<=8)[1]
## [1] 4

2.1.11 向量元素的排序与排名

将数值型向量的元素按大小重新排序的例子如下:

x <- c(5:1,6,pi)
# 从小到大排序
# 用order函数
y1 <- x[order(x)]
y1
## [1] 1.000000 2.000000 3.000000 3.141593 4.000000 5.000000 6.000000
# 用sort函数
y12 <- sort(x)
y12
## [1] 1.000000 2.000000 3.000000 3.141593 4.000000 5.000000 6.000000
# 从大到小排序
# 用order函数
y2 <- x[order(x,decreasing = TRUE)]
y2
## [1] 6.000000 5.000000 4.000000 3.141593 3.000000 2.000000 1.000000
# 用sort函数
y22 <- sort(x,decreasing = TRUE)
y22
## [1] 6.000000 5.000000 4.000000 3.141593 3.000000 2.000000 1.000000

下面更详细说明sort函数和order函数的用法:

sort的用法为sort(x, decreasing = FALSE, na.last = NA, ...),返回排序后的结果。可排序的类型包括实数、复数、字符串和逻辑型向量。

order用法为order(..., na.last = TRUE, decreasing = FALSE,method = c("auto", "shell", "radix")),返回向量的第n个元素为第n名所在原向量的序号。如在上面的例子中,x元素的第1名(最小值)为1,它在原向量中的序号为5,因而order的第1个元素为5。如果希望降序,将decreasing设为TRUE。如果向量中含有NA值,na.last = TRUE则将NA值排在最后,na.last = FALSE则将NA值排在开头,na.last = NA则排除NA值。可排序的类型包括实数、复数、字符串和逻辑型向量,此外,还可对一系列向量组成的矩阵、数据框排序,因而是sort函数不可替代的。

一个相关的函数是rank函数,它返回被排序向量中每个元素的排名,如

x <- c(5:1,6,pi)
y3 <- rank(x)
y3
## [1] 6 5 3 2 1 7 4

以上都是按向量的值排序的方式,如果要求按元素名字排序,则可用下面的方法:

x <- c(d=1,a=2,b=3)
y <- x[order(names(x))]
y
## a b d 
## 2 3 1

最大值函数用max,最小值函数用min,其中的参数既可以是单个向量,也可以是多个向量,返回所有向量中的最大或最小值。如果需要返回不同向量对应比较的结果,用函数pmax或pmin。位置最大、最小值所在的位置用函数which.max和which.min,例如:

x <- c(5:1,6,pi)
max(x)
## [1] 6
min(5:1,6,pi)
## [1] 1
pmax(x)
## [1] 5.000000 4.000000 3.000000 2.000000 1.000000 6.000000 3.141593
pmin(5:1,6,pi)
## [1] 3.141593 3.141593 3.000000 2.000000 1.000000
which.max(x)
## [1] 6
which.min(x)
## [1] 5

2.1.12 向量的翻转

直接将倒序后的向量赋给原向量即可,也可用rev函数,如

x <- seq(2,10,by=2)
x
## [1]  2  4  6  8 10
x <- x[length(x):1]
x
## [1] 10  8  6  4  2
x <- rev(x)
x
## [1]  2  4  6  8 10

2.1.13 向量的集合运算

向量的交集用intersect(x, y),并集用union(x, y),差集用setdiff(x, y),作为集合是否相等,用setequal(x, y),确定元素是否是集合的某个元素用is.element(x, y),它等价于x %in% y.下面为一些例子:

x <- c(5:1,NA)
y <- c(NA,4:8)
union(x, y)
## [1]  5  4  3  2  1 NA  6  7  8
intersect(x, y)
## [1]  5  4 NA
setdiff(x, y)
## [1] 3 2 1
setdiff(y, x)
## [1] 6 7 8
setequal(x, y)
## [1] FALSE
is.element(x, y)
## [1]  TRUE  TRUE FALSE FALSE FALSE  TRUE
is.element(y, x)
## [1]  TRUE  TRUE  TRUE FALSE FALSE FALSE

注意上面的函数只能用于两个向量的集合运算,而不能用于多个向量的集合运算,如果需要对多个向量运算,可以重复使用上面的函数,或者用Reduce函数,例如

a <- c(1,3,5,7,9)
b <- c(3,6,8,9,10)
d <- c(2,3,4,5,7,9)
intersect(intersect(a,b),d)
## [1] 3 9
Reduce(intersect, list(a,b,d))
## [1] 3 9

2.1.14 向量的算数运算

下面的算数运算符均可直接用于两向量的运算,结果返回向量两两对应元素之间运算的结果。当两向量长度不相等时,会将长度短的复制多份首尾相连。但两向量长度不是整数倍关系时,会报错。

  • 加:+
  • 减:-
  • 乘:*
  • 除:/
  • 取模(余数):%%
  • 整除:%/%
  • 乘方:^或**

2.2 简单列表

列表中的元素可以是任何数据结构,包含列表本身,因而整个列表的结构可以非常复杂。这里所说的简单列表,指的是元素中不包含列表的列表。

2.2.1 用数据创建列表

用list函数,如

x <- list(a=1,b="Apple",c=TRUE,d=1:3)
y <- list(x,5+3i)
x
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
length(x)
## [1] 4
y
## [[1]]
## [[1]]$a
## [1] 1
## 
## [[1]]$b
## [1] "Apple"
## 
## [[1]]$c
## [1] TRUE
## 
## [[1]]$d
## [1] 1 2 3
## 
## 
## [[2]]
## [1] 5+3i
length(y)
## [1] 2

2.2.2 列表的串联与元素插入

直接用list函数将两个列表连起来,两个列表本身会作为新列表的元素,而不是列表的元素作为新列表的元素。如果我们需要让两个列表的元素组成新的列表,依然用函数c将多个列表连接起来,返回的是列表而不是向量。

另外也可以用函数append。其用法为append(x, values, after = length(x))(append函数也可用于向量,也是取向量values中的元素)。如果原列表的元素有名字,则新列表的元素继承其名字,如果原列表元素没有名字,在append函数中没有添加名字的功能,可以在之后单独用names函数添加名字。

x1 <- list(a=1,b="Apple",c=TRUE,d=1:3)
x2 <- list(e=5+3i,f=c(1.5,2.6))

# 列表的元素作为新列表的元素
y1 <- append(x1,x2)
y1
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## $e
## [1] 5+3i
## 
## $f
## [1] 1.5 2.6
length(y1)
## [1] 6
y12 <- c(x1,x2)
y12
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## $e
## [1] 5+3i
## 
## $f
## [1] 1.5 2.6
length(y12)
## [1] 6
# 向量的元素作为新向量的元素
y2 <- append(x1,1:5)
y2
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## [[5]]
## [1] 1
## 
## [[6]]
## [1] 2
## 
## [[7]]
## [1] 3
## 
## [[8]]
## [1] 4
## 
## [[9]]
## [1] 5
length(y2)
## [1] 9
y22 <- c(x1,1:5)
y22
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## [[5]]
## [1] 1
## 
## [[6]]
## [1] 2
## 
## [[7]]
## [1] 3
## 
## [[8]]
## [1] 4
## 
## [[9]]
## [1] 5
length(y22)
## [1] 9
# 列表本身作为新列表的元素
y3 <- list(x1,x2)
y3
## [[1]]
## [[1]]$a
## [1] 1
## 
## [[1]]$b
## [1] "Apple"
## 
## [[1]]$c
## [1] TRUE
## 
## [[1]]$d
## [1] 1 2 3
## 
## 
## [[2]]
## [[2]]$e
## [1] 5+3i
## 
## [[2]]$f
## [1] 1.5 2.6
length(y3)
## [1] 2

当在append函数中指定after值的时候,可用于元素的插入,如

x1 <- list(a=1,b="Apple",c=TRUE,d=1:3)
x2 <- list(e=5+3i,f=c(1.5,2.6))

# 在列表x1的第2个元素后插入列表x2的元素
y4 <- append(x1,x2,after = 2)
y4
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $e
## [1] 5+3i
## 
## $f
## [1] 1.5 2.6
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3

values值也可以是一个长度为1的元素,此时就是将单个元素附加在末尾或插入其中,如

x1 <- list(a=1,b="Apple",c=TRUE,d=1:3)
y5 <- append(x1,5)
y5
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## [[5]]
## [1] 5
y6 <- append(x1,5,after = 2)
y6
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## [[3]]
## [1] 5
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3

2.2.3 列表的初始化

空列表的初始化为x<- list(),没有给定长度的列表的初始化的方式。因为列表的元素的类型是任意的,无法预先分配内存。可以直接给尚不存在的列表的元素赋值。

x1 <- list(a=1,b="Apple",c=TRUE,d=1:3)
length(x1)
## [1] 4
x1[5] <- 5
x1
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## [[5]]
## [1] 5
x1[8] <- T
x1
## $a
## [1] 1
## 
## $b
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
## 
## [[5]]
## [1] 5
## 
## [[6]]
## NULL
## 
## [[7]]
## NULL
## 
## [[8]]
## [1] TRUE
length(x1)
## [1] 8

2.2.4 列表元素的选取

2.2.4.1 单个元素的选取

如果列表元素有名字,可以用美元符号$放在列表名和元素名之间,元素名不需要用引号括起来(括起来也没关系)。如果没有名字,则需要用双中括号将元素序号括起来。同时,双中括号也可用于有元素名的情况,此时元素名必须用引号括起来。列表元素没指定名时,其名不是长度为0的字符串。如果用单方括号将序号括起来,则返回一个仅包含该元素的列表:

x1 <- list(a=1,"Apple",c=TRUE,d=1:3)
x1$a
## [1] 1
x1[[2]]
## [1] "Apple"
# x1$2 # 错误的方式
x1[["d"]]
## [1] 1 2 3
x1[[""]] # 无效的方式,未命名的元素不能用""得到
## NULL
x1[2]
## [[1]]
## [1] "Apple"

2.2.4.2 多个元素的选取

方法与向量的多元素的选取相同,得到的结果为多个元素组成的列表。美元符号的方式只能用于单元素选取,不能用于多元素选取。head函数、tail函数、负数序号表示排除等方法,对列表依然适用。

x1 <- list(a=1,e="Apple",c=TRUE,d=1:3)
x1[1:2]
## $a
## [1] 1
## 
## $e
## [1] "Apple"
x1[c(1:2,4)]
## $a
## [1] 1
## 
## $e
## [1] "Apple"
## 
## $d
## [1] 1 2 3
x1[c("a","c")]
## $a
## [1] 1
## 
## $c
## [1] TRUE
x1[-1:-2]
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
# x1[c("a":"c")] # 错误的方式,不能识别"a":"c"
# x1$c("a") # 错误的方式
# x1$c("a","c") # 错误的方式  

2.2.5 简单列表的其他操作

列表元素的修改、列表元素名的获取与修改、列表元素的删除、条件查询、翻转、集合运算与向量类似,不再赘述。列表一般不涉及排序问题(但列表中的元素可能涉及排序问题),这里暂不讨论。需要注意的是,与向量不同,列表元素的类型可以任意改变。

x1 <- list(a=1,"Apple",c=TRUE,d=1:3)
# 元素a原来是数值型,现在改变为逻辑型
x1$a <- FALSE
x1
## $a
## [1] FALSE
## 
## [[2]]
## [1] "Apple"
## 
## $c
## [1] TRUE
## 
## $d
## [1] 1 2 3
x2 <- 1:3
# 对向量来说,原来是数值型,被逻辑型赋值之后,逻辑型会被强制改变为数值型。
x2[1] <- FALSE
x2
## [1] 0 2 3

2.3 矩阵

在R语言中,矩阵是具有维度(dim)属性,且其维度为2的向量。因为其本质上仍是向量,所以所有能对向量进行的操作,对矩阵都适用,对向量的限制,对矩阵也都适用,比如要求所有元素必须是同一类型。下面仅介绍与矩阵特有特点相关的操作。

2.3.1 用数据创建矩阵

创建矩阵的语法为matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL),data参数指定原始向量的数据,nrow参数指定行数,ncol参数指定列数,默认将原有向量按照纵向排成矩阵。下面是一个例子:

x <- matrix(1:8,nrow = 2)
x
##      [,1] [,2] [,3] [,4]
## [1,]    1    3    5    7
## [2,]    2    4    6    8
# 下面的例子说明对向量元素的选取与对矩阵元素的选取得到的结果相同
x[2]
## [1] 2
x[7]
## [1] 7
length(x)
## [1] 8
# 按行排列
x <- matrix(1:8,nrow = 2,byrow = TRUE)
x
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8

2.3.2 矩阵的拼接

矩阵的横向拼接用函数cbind,纵向拼接用函数rbind,被拼接的对象可以是矩阵,也可以是向量。他们在拼接方向需要有相同的维度。当横向拼接向量时,向量被视为行向量;当纵向拼接向量时,其被视为列向量。

m1 <- matrix(1:8,nrow = 2)
m2 <- matrix(1:4,nrow = 2)
m3 <- matrix(1:12,nrow = 3)
v1 <- 1:4
v2 <- 1:3
cbind(m1,m2)
##      [,1] [,2] [,3] [,4] [,5] [,6]
## [1,]    1    3    5    7    1    3
## [2,]    2    4    6    8    2    4
rbind(m1,m3)
##      [,1] [,2] [,3] [,4]
## [1,]    1    3    5    7
## [2,]    2    4    6    8
## [3,]    1    4    7   10
## [4,]    2    5    8   11
## [5,]    3    6    9   12
rbind(m1,v1)
##    [,1] [,2] [,3] [,4]
##       1    3    5    7
##       2    4    6    8
## v1    1    2    3    4
cbind(m3,v2)
##               v2
## [1,] 1 4 7 10  1
## [2,] 2 5 8 11  2
## [3,] 3 6 9 12  3

2.3.3 矩阵的初始化

在语句matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)中,将data做对应的向量的初始化即可。如:

matrix(data = 0, nrow = 2, ncol = 2)
##      [,1] [,2]
## [1,]    0    0
## [2,]    0    0
matrix(data = complex(6), nrow = 2, ncol = 3)
##      [,1] [,2] [,3]
## [1,] 0+0i 0+0i 0+0i
## [2,] 0+0i 0+0i 0+0i

2.3.4 特殊规律的矩阵的创建

2.3.4.1 全同矩阵(包括全0矩阵)

# 元素全为5的矩阵
matrix(data = 5, nrow = 2, ncol = 3)
##      [,1] [,2] [,3]
## [1,]    5    5    5
## [2,]    5    5    5
# 可简写为
matrix(5,2,3)
##      [,1] [,2] [,3]
## [1,]    5    5    5
## [2,]    5    5    5
# 全0矩阵
matrix(0,2,3)
##      [,1] [,2] [,3]
## [1,]    0    0    0
## [2,]    0    0    0
# 全1矩阵
matrix(1,3,2)
##      [,1] [,2]
## [1,]    1    1
## [2,]    1    1
## [3,]    1    1

2.3.4.2 对角矩阵(包括单位矩阵)

对角矩阵的创建用函数diag,diag用法比较复杂,为diag(x = 1, nrow, ncol, names = TRUE),当仅包含两个参数,第1个参数为单元素,第2个参数为整数n时,表示创建n维对角矩阵,每个对角元素都为第1个参数。第1个元素的类型可以是整数型、实数型、复数型或逻辑型,不能是字符串型。对逻辑型来说,非对角的地方为FALSE。

也可以创建对角线上元素不同的矩阵,此时将对角线上的元素以向量的方式作为diag的第一个参数,矩阵的维度与该向量的长度相同。如果指定维度,若对角线上元素长度小于行数与列数中的最小值,指定对角线向量会复制多份首尾相连。

# 对角线上元素全同的用法
diag(5.2,3)
##      [,1] [,2] [,3]
## [1,]  5.2  0.0  0.0
## [2,]  0.0  5.2  0.0
## [3,]  0.0  0.0  5.2
diag(1,4)
##      [,1] [,2] [,3] [,4]
## [1,]    1    0    0    0
## [2,]    0    1    0    0
## [3,]    0    0    1    0
## [4,]    0    0    0    1
diag(TRUE,3)
##       [,1]  [,2]  [,3]
## [1,]  TRUE FALSE FALSE
## [2,] FALSE  TRUE FALSE
## [3,] FALSE FALSE  TRUE
diag(F,2)
##       [,1]  [,2]
## [1,] FALSE FALSE
## [2,] FALSE FALSE
diag(3+5i,3)
##      [,1] [,2] [,3]
## [1,] 3+5i 0+0i 0+0i
## [2,] 0+0i 3+5i 0+0i
## [3,] 0+0i 0+0i 3+5i
# 非方阵
diag(1,3,5)
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    0    0    0    0
## [2,]    0    1    0    0    0
## [3,]    0    0    1    0    0
diag(1.2,5,2)
##      [,1] [,2]
## [1,]  1.2  0.0
## [2,]  0.0  1.2
## [3,]  0.0  0.0
## [4,]  0.0  0.0
## [5,]  0.0  0.0
# 对角线上元素不同的用法
diag(1:5)
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    0    0    0    0
## [2,]    0    2    0    0    0
## [3,]    0    0    3    0    0
## [4,]    0    0    0    4    0
## [5,]    0    0    0    0    5
diag(c(1.1,2.3))
##      [,1] [,2]
## [1,]  1.1  0.0
## [2,]  0.0  2.3
diag(c(T,F))
##       [,1]  [,2]
## [1,]  TRUE FALSE
## [2,] FALSE FALSE
diag(c(1.5,3i))
##        [,1] [,2]
## [1,] 1.5+0i 0+0i
## [2,] 0.0+0i 0+3i
# 指定元素仅作为部分对角线上元素
diag(c(1.1,2.3),3,5)
##      [,1] [,2] [,3] [,4] [,5]
## [1,]  1.1  0.0  0.0    0    0
## [2,]  0.0  2.3  0.0    0    0
## [3,]  0.0  0.0  1.1    0    0

2.3.5 矩阵行名、列名的创建、获取与修改

可以在创建矩阵时指定行名与列名,也可以创建后再创建。行名的创建、获取与修改用函数rownames或row.names,二者效果相同,习惯上推荐使用row.names。列名的创建、获取与修改用函数colnames。names函数返回的是矩阵作为原始向量的元素的名,而不能返回和行名、列名相关的信息。下面是一些例子:

m1 <- matrix(1:8,nrow = 2, dimnames = list(c("r1","r2"),c("c1","c2","c3","c4")))
m1
##    c1 c2 c3 c4
## r1  1  3  5  7
## r2  2  4  6  8
m2 <- matrix(8:1,nrow = 2)
rownames(m2) <- c("r1","r2")
m2
##    [,1] [,2] [,3] [,4]
## r1    8    6    4    2
## r2    7    5    3    1
row.names(m2) <- c("R1","R2")
m2
##    [,1] [,2] [,3] [,4]
## R1    8    6    4    2
## R2    7    5    3    1
colnames(m2) <- c("c1","c2","c3","c4")
m2
##    c1 c2 c3 c4
## R1  8  6  4  2
## R2  7  5  3  1
dimnames(m2)[[1]][2] <- "row2"
m2
##      c1 c2 c3 c4
## R1    8  6  4  2
## row2  7  5  3  1
dimnames(m2) <- list(c("R1","R2"),c("C1","C2","C3","C4"))
m2
##    C1 C2 C3 C4
## R1  8  6  4  2
## R2  7  5  3  1
names(m2)
## NULL

2.3.6 矩阵元素的选取与修改

2.3.6.1 单个元素的选取与修改

除了作为普通向量的元素选取方式,我们更希望采用行序号(或行名)与列序号(或列名)的方式选取矩阵中的元素。这是可以实现的,将行序号(或带引号的行名)、列序号(或带引号的行名)放在方括号中,用逗号分隔即可。可以在行的维度用序号,列的维度用名字,反之也可以。下面是一些例子:

m1 <- matrix(1:8,nrow = 2, dimnames = list(c("r1","r2"),c("c1","c2","c3","c4")))
m1[2,3]
## [1] 6
m1["r1","c2"]
## [1] 3
m1[1,"c2"]
## [1] 3

2.3.6.2 多个元素的选取与修改

可以指定同时处于某些行,且处于某些列的元素,将上面逗号前的单个元素用向量代替,逗号后的元素用向量代替即可。如果选取所有行或所有列,将逗号前或后的参数空缺即可,如:

m <- matrix(1:16,nrow = 4, dimnames = list(c("r1","r2","r3","r4"),c("c1","c2","c3","c4")))
m[c(1,3:4),c("c2","c3")]
##    c2 c3
## r1  5  9
## r3  7 11
## r4  8 12
m[1,] # 第1行,所有列
## c1 c2 c3 c4 
##  1  5  9 13
m[,] # 所有行,所有列,和m[]等价
##    c1 c2 c3 c4
## r1  1  5  9 13
## r2  2  6 10 14
## r3  3  7 11 15
## r4  4  8 12 16

注意,如果选取的为单行或单列矩阵,返回结果会转化为向量,如果依然希望保持为矩阵,可增加参数drop = FALSE,例如

m <- matrix(1:16,nrow = 4)
v <- m[1,]
v # 在上面一行,v被转化为了向量,下一行v[1,4]会报错
## [1]  1  5  9 13
# v[1,4] 

m2 <- m[1,,drop = FALSE]
m2 # 在上面一行,m2为单行矩阵,下一行m2[1,4]不会报错
##      [,1] [,2] [,3] [,4]
## [1,]    1    5    9   13
m2[1,4]
## [1] 13

2.3.6.3 矩阵对角线元素的选取与修改

用diag函数获取矩阵的对角线上的元素,返回向量,可利用向量元素获取方法对其元素进行进一步获取与修改。

m1 <- matrix(1:8,nrow = 2, dimnames = list(c("r1","r2"),c("c1","c2","c3","c4")))
m1
##    c1 c2 c3 c4
## r1  1  3  5  7
## r2  2  4  6  8
diag(m1)
## [1] 1 4
diag(m1)[2] <- 3
m1
##    c1 c2 c3 c4
## r1  1  3  5  7
## r2  2  3  6  8

2.3.7 矩阵的转置、翻转、旋转

矩阵的转置用函数t,矩阵翻转对应行列翻转即可。R没有现成的矩阵旋转的函数,需要自己编写。

m1 <- matrix(1:8,nrow = 2, dimnames = list(c("r1","r2"),c("c1","c2","c3","c4")))
m1
##    c1 c2 c3 c4
## r1  1  3  5  7
## r2  2  4  6  8
# 转置
t(m1)
##    r1 r2
## c1  1  2
## c2  3  4
## c3  5  6
## c4  7  8
# 上下翻转
m1[nrow(m1):1,]
##    c1 c2 c3 c4
## r2  2  4  6  8
## r1  1  3  5  7
# 左右翻转
m1[,ncol(m1):1]
##    c4 c3 c2 c1
## r1  7  5  3  1
## r2  8  6  4  2
# 顺时针90度旋转
t(apply(m1, 2, rev))
##    r2 r1
## c1  2  1
## c2  4  3
## c3  6  5
## c4  8  7
# 逆时针90度旋转
apply(t(m1), 2, rev)
##    r1 r2
## c4  7  8
## c3  5  6
## c2  3  4
## c1  1  2

2.3.8 元素级算数运算

直接对矩阵的算数运算即为每个元素分别运算,包括乘法。

2.3.9 矩阵级运算

R语言常用矩阵运算1

R语言常用矩阵运算1

R语言常用矩阵运算2

R语言常用矩阵运算2

2.3.10 矩阵的其他操作

行列的增加可参考矩阵的拼接。行列的删除、用逻辑值选取、集合运算与向量的操作类似。矩阵的条件查询、排序一般按单行或单列分别查询,也可以利用apply函数。

2.4 高维数组

在R语言中,数组是具有维度(dim)属性的向量。矩阵是一种特殊的数组,这里以3维数组为例,说明高维数组的操作。

2.4.1 用数据创建高维数据

创建矩阵的语法为array(data = NA, dim = length(data), dimnames = NULL)

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

2.4.2 高维数组的拼接

高维数组拼接需要用到abind包中的abind函数,请读者提前安装并加载该包。其基本用法为abind(..., along=N),下面是一些例子

library(abind)
x <- array(1:24, c(3,4,2))
y <- x+100
x
## , , 1
## 
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4]
## [1,]   13   16   19   22
## [2,]   14   17   20   23
## [3,]   15   18   21   24
y
## , , 1
## 
##      [,1] [,2] [,3] [,4]
## [1,]  101  104  107  110
## [2,]  102  105  108  111
## [3,]  103  106  109  112
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4]
## [1,]  113  116  119  122
## [2,]  114  117  120  123
## [3,]  115  118  121  124
# 行方向拼接
abind(x,y, along=1)
## , , 1
## 
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
## [4,]  101  104  107  110
## [5,]  102  105  108  111
## [6,]  103  106  109  112
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4]
## [1,]   13   16   19   22
## [2,]   14   17   20   23
## [3,]   15   18   21   24
## [4,]  113  116  119  122
## [5,]  114  117  120  123
## [6,]  115  118  121  124
# 列方向拼接
abind(x,y, along=2)
## , , 1
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]    1    4    7   10  101  104  107  110
## [2,]    2    5    8   11  102  105  108  111
## [3,]    3    6    9   12  103  106  109  112
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,]   13   16   19   22  113  116  119  122
## [2,]   14   17   20   23  114  117  120  123
## [3,]   15   18   21   24  115  118  121  124
# 页方向拼接
abind(x,y, along=3)
## , , 1
## 
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
## 
## , , 2
## 
##      [,1] [,2] [,3] [,4]
## [1,]   13   16   19   22
## [2,]   14   17   20   23
## [3,]   15   18   21   24
## 
## , , 3
## 
##      [,1] [,2] [,3] [,4]
## [1,]  101  104  107  110
## [2,]  102  105  108  111
## [3,]  103  106  109  112
## 
## , , 4
## 
##      [,1] [,2] [,3] [,4]
## [1,]  113  116  119  122
## [2,]  114  117  120  123
## [3,]  115  118  121  124
# 新的维度上拼接,新维度作为第1维,之前的维度顺次后移
abind(x,y, along=0)
## , , 1, 1
## 
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]  101  102  103
## 
## , , 2, 1
## 
##      [,1] [,2] [,3]
## [1,]    4    5    6
## [2,]  104  105  106
## 
## , , 3, 1
## 
##      [,1] [,2] [,3]
## [1,]    7    8    9
## [2,]  107  108  109
## 
## , , 4, 1
## 
##      [,1] [,2] [,3]
## [1,]   10   11   12
## [2,]  110  111  112
## 
## , , 1, 2
## 
##      [,1] [,2] [,3]
## [1,]   13   14   15
## [2,]  113  114  115
## 
## , , 2, 2
## 
##      [,1] [,2] [,3]
## [1,]   16   17   18
## [2,]  116  117  118
## 
## , , 3, 2
## 
##      [,1] [,2] [,3]
## [1,]   19   20   21
## [2,]  119  120  121
## 
## , , 4, 2
## 
##      [,1] [,2] [,3]
## [1,]   22   23   24
## [2,]  122  123  124

2.4.3 高维数组的其他操作

数组其他操作与矩阵操作类似,特别地,数据选取通过方括号中三个序号来确定,序号之间用逗号分隔。

2.5 数据框

数据框是一种特殊的列表,列表中每个元素都是一个有名字的向量,且这些向量的长度相同。不同的向量可以是不同类型的。数据框是统计分析中最常用的数据结构。

2.5.1 用数据创建数据框

2.5.1.1 用各列向量创建数据框

用data.frame函数。

name <- c("Alice","Bob","Christien")
age <- c(10,20,14)
gender <- c("F","M","F")
weight <- c(25,50,30)
df <- data.frame(name,age,gender,weight)
df
##        name age gender weight
## 1     Alice  10      F     25
## 2       Bob  20      M     50
## 3 Christien  14      F     30
# 抑制字符串向因子型的转换
df2 <- data.frame(name,age,gender,weight,stringsAsFactors=FALSE)
df2
##        name age gender weight
## 1     Alice  10      F     25
## 2       Bob  20      M     50
## 3 Christien  14      F     30

字符串会默认转化为因子型(factor)。如果需要抑制这种转换,则需要在data.frame函数中,加入stringsAsFactors=FALSE的参数。所谓因子型,简单来说是包含顺序的字符串,顺序可由用户指定。在建模时,会被作为整数处理。

x <- factor(c("single", "married", "married", "single"), levels = c("single", "married", "divorced"))

2.5.1.2 用矩阵创建数据框

用as.data.frame函数。

m1 <- matrix(1:8,nrow = 2, dimnames = list(c("r1","r2"),c("c1","c2","c3","c4")))
df1 <- as.data.frame(m1)
df1
##    c1 c2 c3 c4
## r1  1  3  5  7
## r2  2  4  6  8
# 如果矩阵没有指定列名,则列名默认为"V1"、"V2"...
m2 <- matrix(1:8,nrow = 2)
df2 <- as.data.frame(m2)
df2
##   V1 V2 V3 V4
## 1  1  3  5  7
## 2  2  4  6  8

2.5.2 数据框的初始化

在实际数据分析中,数据框一般由导入数据、其他数据框、其他向量创建较为常见,新建指定行列数的数据框并不常见,但是这依然是可以做到的。

建立指定行列数的空数据框,同用矩阵创建数据框的方法。在不确定每列数据类型的情况下,将矩阵中的数据定为raw()即可。由于原生型的优先级最低,它被赋为其他类型时,会被强制转换为该类型。如果确定数据框中所有列均为数值型,也可以将元素初始化为NA或0。当某一整行或整列被赋为NULL,该行或列会被删除,因而不能将初始值赋为NULL。如果知道每一列的数据类型,可以用向量创建数据框的方法。

as.data.frame(matrix(raw(),5,3))
##   V1 V2 V3
## 1 00 00 00
## 2 00 00 00
## 3 00 00 00
## 4 00 00 00
## 5 00 00 00
as.data.frame(matrix(0,5,3))
##   V1 V2 V3
## 1  0  0  0
## 2  0  0  0
## 3  0  0  0
## 4  0  0  0
## 5  0  0  0
as.data.frame(matrix(NA,5,3))
##   V1 V2 V3
## 1 NA NA NA
## 2 NA NA NA
## 3 NA NA NA
## 4 NA NA NA
## 5 NA NA NA
# as.data.frame(matrix(NULL,5,3)) # 报错,矩阵的data参数中不能被赋为NULL
data.frame(character(5),integer(5),double(5),logical(5))
##   character.5. integer.5. double.5. logical.5.
## 1                       0         0      FALSE
## 2                       0         0      FALSE
## 3                       0         0      FALSE
## 4                       0         0      FALSE
## 5                       0         0      FALSE

可以创建仅含列名的0行的数据框。这在未知数量的数据框的纵向拼接时有用,如:

df <- as.data.frame(matrix(0,0,3,dimnames = list(c(),c("x1","x2","x3"))))
df
## [1] x1 x2 x3
## <0 rows> (or 0-length row.names)
for (i in 1:3) {
  n_row <- sample(1:4,1)
  df_tmp <- as.data.frame(matrix(n_row,n_row,3,dimnames = list(c(),c("x1","x2","x3"))))
  df <- rbind(df,df_tmp)
}
df
##   x1 x2 x3
## 1  3  3  3
## 2  3  3  3
## 3  3  3  3
## 4  3  3  3
## 5  3  3  3
## 6  3  3  3
## 7  3  3  3
## 8  3  3  3
## 9  3  3  3

2.5.3 数据框行名、列名的创建、选取与修改

当用向量建立数据框时,向量名被默认作为数据框的列名。当用矩阵建立数据框时,矩阵列名也会默认作为数据框列名。如果数据框没有指定列名,则列名默认为“V1”、“V2”…,如果数据框没有指定行名,则行名默认为“1”、“2”、“3”…。获取数据框列名,可用names函数或colnames函数。获取数据框行名,可用row.names函数或rownames函数,对数据框来说,row.names函数效率更高,因而推荐使用row.names函数。

在实际数据分析中,修改行名的做法很少见,更多的是增加一个ID列,然后做相应的修改。

df <- data.frame(character(5),integer(5),double(5),logical(5))
names(df)
## [1] "character.5." "integer.5."   "double.5."    "logical.5."
newcol <- complex(5)
df[5] <- newcol
names(df)
## [1] "character.5." "integer.5."   "double.5."    "logical.5."  
## [5] "V5"
df$newname <- newcol
names(df)
## [1] "character.5." "integer.5."   "double.5."    "logical.5."  
## [5] "V5"           "newname"
df["newname2"] <- newcol
names(df)
## [1] "character.5." "integer.5."   "double.5."    "logical.5."  
## [5] "V5"           "newname"      "newname2"
colnames(df)
## [1] "character.5." "integer.5."   "double.5."    "logical.5."  
## [5] "V5"           "newname"      "newname2"
row.names(df)
## [1] "1" "2" "3" "4" "5"
rownames(df)
## [1] "1" "2" "3" "4" "5"

2.5.4 数据框元素的选取与修改

所有对矩阵元素选取与修改的方法,除对角线元素的选取与修改,对数据框均可使用。如将行序号(或带引号的行名)、列序号(或带引号的行名)放在方括号中,用逗号分隔。行序号与列序号均可以是向量。如果选取的是单列,则会返回向量值,如果需要保持为单列的数据框,则添加drop = FALSE参数。但如果选取的是单行,会直接返回为数据框,这与矩阵不同,因为同一行的不同列可能是不同类型的数据。

由于数据框是一种特殊的列表,对列表元素的选取与修改,对数据框都适用,如美元符号表示对列中元素的选取。下面是一些例子:

m <- matrix(1:16,4,4, dimnames = list(c("r1","r2","r3","r4"),c("c1","c2","c3","c4")))
df <- as.data.frame(m)
df
##    c1 c2 c3 c4
## r1  1  5  9 13
## r2  2  6 10 14
## r3  3  7 11 15
## r4  4  8 12 16
df[2,3]
## [1] 10
df["r1","c3"]
## [1] 9
df[2,"c3"]
## [1] 10
df["r2",2]
## [1] 6
df[c(1,3:4),c("c2","c3")]
##    c2 c3
## r1  5  9
## r3  7 11
## r4  8 12
df[,2]
## [1] 5 6 7 8
df[,2,drop = FALSE]
##    c2
## r1  5
## r2  6
## r3  7
## r4  8
df[3,]
##    c1 c2 c3 c4
## r3  3  7 11 15
# 第1列的3种选取方式
df[[1]]
## [1] 1 2 3 4
df[["c1"]]
## [1] 1 2 3 4
df$c1
## [1] 1 2 3 4
df[[1]][2] # 第1列,第2行,等价于df[1,2]
## [1] 2
df$c2[1:3]
## [1] 5 6 7
df$c2[c(1,3:4)]
## [1] 5 7 8

2.5.5 增加、删除行、列

数据框增加一列,直接将数据赋给新列即可。删除数据框的某一列,可以将NULL赋给该列,或者用负数序号排除该列,将其他列赋给原始数据框:

m <- matrix(1:16,4,4, dimnames = list(c("r1","r2","r3","r4"),c("c1","c2","c3","c4")))
df <- as.data.frame(m)
df
##    c1 c2 c3 c4
## r1  1  5  9 13
## r2  2  6 10 14
## r3  3  7 11 15
## r4  4  8 12 16
# 给一个尚不存在的列赋值,即增加该列
df$new <- 1:4
df
##    c1 c2 c3 c4 new
## r1  1  5  9 13   1
## r2  2  6 10 14   2
## r3  3  7 11 15   3
## r4  4  8 12 16   4
df[5] <- NULL # 删除该列
# 可以通过序号的方式增加1列
df[5] <- c(T,F,F,T)
df
##    c1 c2 c3 c4    V5
## r1  1  5  9 13  TRUE
## r2  2  6 10 14 FALSE
## r3  3  7 11 15 FALSE
## r4  4  8 12 16  TRUE
df <- df[-5] # 删除该列
df
##    c1 c2 c3 c4
## r1  1  5  9 13
## r2  2  6 10 14
## r3  3  7 11 15
## r4  4  8 12 16
# 用序号的方式增加列,中间不能有空列,即序号必须为数据框列数加一,下面会报错
# df[6] <- c(T,F,F,T)

删除行的方式可用NULL赋给该行或用负数序号的方式。

增加行的方式用函数rbind,例如

name <- c("Alice","Bob","Christien")
age <- c(10,20,14)
gender <- c("F","M","F")
weight <- c(25,50,30)
df <- data.frame(name,age,gender,weight) # 注意此处字符串会转化为因子型

new <- list("Dora",24,"F",55) # 此处的字符串不是因子型
# df[nrow(df)+1,] <- new # 出错,将字符串赋给了因子型
# df2 <- rbind(df,new) # 出错,将字符串赋给了因子型

# 将原数据框中的因子型转化为字符型,增加行之后,如果需要,再转回来
df$name <- as.character(df$name)
df$gender <- as.character(df$gender)
df2 <- rbind(df,new)
df$name <- as.factor(df$name)
df$gender <- as.factor(df$gender)

# 如果新行的字符串类型,全部都已经在原数据框中出现过了,则可以直接合并
new2 <- list("Alice",12,"F",30)
df3 <- rbind(df,new2)

2.5.6 数据框的拼接与合并

2.5.6.1 纵向拼接与合并

两个数据框,如果有相同的列名(且对应数据类型相同),可以直接纵向合并,用rbind函数。如果列名顺序不同,按列名对应合并,不按原位置合并。如果一个数据框比另一个数据框有更多的列,则需要首先将有差异的列补齐(如赋予NA值),然后再合并。

name <- c("Alice","Bob","Christien")
age <- c(10,20,14)
gender <- c("F","M","F")
weight <- c(25,50,30)
df1 <- data.frame(name,age,gender,weight) # 注意此处字符串会转化为因子型
df2 <- data.frame(age=24,gender="F",name="Dora",weight=55)
df3 <- rbind(df1,df2)
df4 <- data.frame(age=25,name="Einstein",weight=54)
# df5 <- rbind(df3,df4) # 列数不一致,报错
df4$gender <- NA
df5 <- rbind(df3,df4)

2.5.6.2 横向拼接与合并

数据框的横向拼接用函数cbind,它要求数据框具有相同的行数。无论数据框是否具有共同列,cbind函数都会直接将两数据框拼接,而不考虑共同列的合并问题。

如果需要将相同的列合并,需要用merge函数。如果相同 列中元素的顺序相同,直接将重复的列删除。如果相同列中元素的顺序不同,会根据元素值对应的方式,对数据框进行重排序。相同的列会提到数据框的最前面,且以排序后的形式展示出来。关于merge函数更复杂的用法,可参考其帮助文档。

name <- c("Alice","Bob","Christien")
age <- c(10,20,14)
gender <- c("F","M","F")
weight <- c(25,50,30)
df1 <- data.frame(name,age,gender,weight) # 注意此处字符串会转化为因子型
nationality <- c("USA","UK","China")
height <- c(1.2,1.65,1.43)
df2 <- data.frame(nationality,height)
df3 <- cbind(df1,df2)
df4 <- data.frame(nationality,height,age=age[c(3,2,1)])
df5 <- cbind(df1,df4)
df6 <- data.frame(nationality,height,age)
df7 <- cbind(df1,df6)

df8 <- merge(df1,df6)
df9 <- merge(df1,df4)

2.5.7 条件查询与子集选取

多行多列子集选取的方式可以按照一般的序号、名字或逻辑向量的方式选取,也可以用subset函数,其用法为subset(x, subset, select, drop = FALSE, ...)。x为数据框,subset为行方面满足条件的逻辑向量,select为列方面选取的范围。例如:

name <- c("Alice","Bob","Christien","Einstein")
age <- c(10,20,14,25)
gender <- c("F","M","F","M")
weight <- c(25,50,30,54)
nationality <- c("USA","UK","China","German")
height <- c(1.2,1.65,1.43,1.75)
df <- data.frame(name,age,gender,weight,nationality,height)
df2 <- subset(df,subset = name!="Einstein"&age>10,select = c(age:weight,height))
df2
##   age gender weight height
## 2  20      M     50   1.65
## 3  14      F     30   1.43
# 下面为原始的不使用subset的做法
df3 <- df[df$name!="Einstein"&df$age>10,
          c(which(names(df)=="age"):which(names(df)=="weight"),which(names(df)=="height"))]
# 如果已知列名所在的序号,可简写为
df3 <- df[df$name!="Einstein"&df$age>10,c(2:4,6)]
df3
##   age gender weight height
## 2  20      M     50   1.65
## 3  14      F     30   1.43

用subset函数有下面一些优点:

  • 行方面的逻辑表达式:可以省略掉原始数据与美元符号,直接写列名即可。
  • 列方面的选取:对于一系列连续的列,不方便知道列的序号,但方便知道列名时,可以用列序列的首尾列名中间用冒号连接的方式选取。
  • 含义更加直观。

2.5.8 数据框的排序

如果需要根据一列或多列的顺序,对其他列做相应的调整,可以使用order函数。如果需要将所有列都按降序排序,则增加decreasing = TRUE参数。如果大多数升序,某些降序,在降序的列前加-rank函数(如果是数值型,仅加负号就可以)。如果大多数降序,某些升序,在升序前加-rank函数函数,且添加decreasing = TRUE参数。下面是一些列子:

name <- c("Alice","Bob","Christien","Einstein")
age <- c(10,20,14,25)
gender <- c("F","M","F","M")
weight <- c(25,50,30,54)
nationality <- c("USA","UK","China","German")
height <- c(1.2,1.65,1.43,1.75)
df <- data.frame(name,age,gender,weight,nationality,height)
df2 <- df[order(df$gender,df$weight,decreasing = TRUE),]
df3 <- df[order(-rank(df$gender),df$weight),]
df4 <- df[order(df$gender,-df$weight),]