R语言教程-R数据类型与运算2
11. R矩阵和数组
矩阵用matrix函数定义,实际存储成一个向量,根据保存的行数和列数对应到矩阵的元素, 存储次序为按列存储。
A <- matrix(11:16,nrow=3, ncol=2) A
A[c(1,3), 1:2]
A[1:3,2]
|
A[c(1,3),c(1,2)]
A[c(1,2,3),c(2)]
|
用colnames()函数可以给矩阵每列命名, 也可以访问矩阵列名, 用rownames()函数可以给矩阵每行命名, 也可以访问矩阵行名。
colnames(A) <- c('第一列','第二列') rownames(A) <- c("第一行","第二行","第三行") A
|
attributes(A)
dim(A) dimnames(A)
|
注意在对矩阵取子集时, 如果取出的子集仅有一行或仅有一列, 结果就不再是矩阵而是变成了R向量, R向量既不是行向量也不是列向量。 如果想避免这样的规则起作用, 需要在方括号下标中加选项drop=FALSE
A[,1]>=12 A[A[,1]>=12,1:2]
|
矩阵本质上是一个向量添加了dim属性, 实际保存还是保存成一个向量, 其中元素的保存次序是按列填入, 所以, 也可以向对一个向量取子集那样, 仅用一个正整数对向量的矩阵取子集。
matrix(c(1,1, 2,2, 3,2), ncol=2,byrow = T)
|
为了挑选矩阵的任意元素组成的子集而不是子矩阵, 可以用一个两列的矩阵作为下标, 矩阵的每行的两个元素分别指定一个元素的行号和列号。
rod <- matrix(c(1,1, 2,2, 3,2), ncol=2,byrow = T) A
A[rod]
|
- 用c(A)或A[]返回矩阵A的所有元素。
- 如果要修改矩阵A的所有元素, 可以对A[]赋值。
- diag(A)访问A的主对角线元素组成的向量。
- 若x为长度大于1的向量, diag(x)返回以x的元素为主对角线元素的对角矩阵。
- 若x为正整数值标量,diag(x)返回x阶单位阵
c(A) A[] diag(A) diag(6) diag(c(1,2,3,4,5,6))
|
cbind()和rbind()函数
若x
是向量,cbind(x)
把x
变成列向量, 即列数为1的矩阵, rbind(x)
把x
变成行向量。
若x1, x2, x3是等长的向量, cbind(x1, x2, x3)把它们看成列向量并在一起组成一个矩阵。 cbind()的自变量可以同时包含向量与矩阵,向量的长度必须与矩阵行数相等。
cbind(c(1,2), c(3,4), c(5,6))
|
A A1 <- cbind(A,c(-3,6,-9)) colnames(A1) <- c("第一列","第二列","新增列") A1
|
rbind()同理,向量的长度必须与矩阵列数相同(因为次函数下数字是按row来排列矩阵的)
rbind(c(1,2,3), c(4,5,6))
|
四则运算
- 矩阵可以与标量作四则运算,结果为每个元素进行相应运算
A C1 <- A + 2; C1
c2 <- A - 3; c2
c3 <- A * 2; c3
c4 <- A / 2; c4
|
矩阵乘法
用%*%
表示矩阵乘法而不是用*
表示, 注意矩阵乘法要求左边的矩阵的列数等于右边的矩阵的行数。
12. 数据框
数据框类似于一个矩阵,有行、列, 但各列允许有不同类型:数值型向量、因子、字符型向量、日期时间向量。 同一列的数据类型相同。
d <- data.frame( 姓名=c("李明", "张聪", "王建"), 年龄=c(30, 35, 28), 身高=c(180, 162, 175), stringsAsFactors=FALSE) d
|
data.frame()
函数会将字符型列转换成因子, 加选项stringsAsFactors=FALSE可以避免这样的转换。
数据框每列叫做一个变量, 每列都有名字,称为列名或变量名, 可以用names()函数和colnames()函数访问。
names(d) colnames(d)
names(d)[1] <- "names" names(d)
|
用as.data.frame(x)
可以把x
转换成数据框。 如果x
是一个向量, 转换结果是以x
为唯一一列的数据框。 如果x
是一个列表并且列表元素都是长度相同的向量, 转换结果中每个列表变成数据框的一列。 如果x
是一个矩阵,转换结果把矩阵的每列变成数据框的一列。
数据框是一个随着R语言前身S语言继承下来的概念, 现在已经有一些不足之处, tibble包提供了tibble类, 这是数据框的一个改进版本。
数据框内容访问
d[["names"]] d[,"names"] d$names
|
因为数据框的一行不一定是相同数据类型, 所以数据框的一行作为子集, 结果还是数据框,而不是向量。
d[1,] is.data.frame(d[1,])
d[1:2,"names"] d[1:2, c('names', '年龄')]
|
与矩阵类似地是,用如d[,‘age’], d[,2]这样的方法取出的数据框的单个列是向量而不再是数据框。但是,如果取出两列或者两列以上, 结果则是数据框。
对一般的数据框, 可以在取子集的方括号内加上drop=FALSE选项, 确保取列子集的结果总是数据框。 数据框的改进类型tibble在取出列子集时保持为tibble格式。
t1 <- d[,"names"] is.data.frame(t1) t2 <- d[,"names",drop=F] is.data.frame(t2)
|
数据框每一行可以有行名, 这在原始的S语言和传统的R语言中是重要的技术, 但是在改进类型tibble中则取消了行名, 需要用列名实现功能一般改用left_join()函数实现。
可以用数据中的某一列的每一行作为行名,如d数据康中可以用names
(没有重复值)作为行名。
输出结果
names 年龄 身高 李明 李明 30 180 张聪 张聪 35 162 王建 王建 28 175
|
rownames(d) <- d$names d$names <- NULL d
|
输出结果
年龄 身高 李明 30 180 张聪 35 162 王建 28 175
|
用数据框的行名可以建立一个值到多个值的对应表。
dm <- data.frame( "年级"=1:6, '出游'=c(0, 2, 2, 2, 2, 1), '疫苗'=c(T, F, F, F, T, F) )
|
把年级变成行名,可以建立年级到出游次数与疫苗注射的对应表:
rownames(dm) <- dm[['年级']] dm[["年级"]] <- NULL
|
假设某个社区的小学中抽取的4个班的年级为 c(2,1,1,3), 其对应的出游和疫苗注射信息可查询如下:
dm[as.character(c(2,1,1,3)),]
|
输出结果包含了两个1
出游 疫苗 2 2 FALSE 1 0 TRUE 1.1 0 TRUE 3 2 FALSE
|
可以去掉,以上程序改成:
x <- dm[as.character(c(2,1,1,3)),] rownames(x) <- NULL x
|
输出结果:
出游 疫苗 1 2 FALSE 2 0 TRUE 3 0 TRUE 4 2 FALSE
|
对于代替数据框的tibble类型, 如果要实现行名的功能, 可以将行名作为单独的一列
数据框与矩阵的区别
数据框不能作为矩阵参加矩阵运算。 需要时,可以用as.matrix()函数转换数据框或数据框的子集为矩阵。
d2 <- as.matrix(d[,c("年龄", "身高")])
d3 <- crossprod(d2); d3 d4 <- c(1,2,3) %*% d2;d4
|
gl()函数
d4 <- data.frame( group=gl(3, 10, length=30), subgroup=gl(5,2,length=30), obs=gl(2,1,length=30)) print(d4)
|
结果的数据框d有三个变量: group是大组,共分3个大组,每组10个观测; subgroup是子组,在每个大组内分为5个子组,每个子组2个观测。 共有3x5x2=30个观测(行)。
gl()函数第一个参数是因子水平个数, 第二个参数是同一因子水平连续重复次数, 第三个参数是总共需要的元素个数, 所有水平都出现后则重复整个模式直到长度满足要求。
tibble
类型
tibble类型是一种改进的数据框。 readr包的read_csv()函数是read.csv()函数的一个改进版本, 它将CSV文件读入为tibble类型,如文件class.csv的读入:
library(tibble) library(readr) t.class <- read_csv('class.csv')
|
用as_tibble()可以将一个数据框转换为tibble.
可以用tibble()函数生成小的tibble,和生成data.frame的格式一样。如
t.b <- tibble( "序号"=c(1,3,5,4,6,7), `收缩压`=c(145, 110, "未测", 150, "拒绝", 115) ) t.b
|
用tribble可以按类似于CSV格式输入一个tibble,
t.bp <- tribble( ~`序号`,~`收缩压`, 1,145, 5,110, 6,"未测", 9,150, 10,"拒绝", 15,115 ) t.bp
|
注意tribble()中数据每行末尾也需要有逗号, 最后一行末尾没有逗号。 这比较适用于在程序中输入小的数据集。
tibble与数据框的一大区别是在显示时不自动显示所有内容, 这样可以避免显示很大的数据框将命令行的所有显示都充满。 可以在print()用n=和width=选项指定要显示的行数和列数。
另外,用单重的方括号取列子集时, 即使仅取一列, 从tibble取出的一列结果仍是tibble而不是向量, 这时应使用双方括号格式或$格式。 因为这个原因有些原来的程序输入tibble会出错, 这时可以用as.data.frame()转换成数据框。
t.bp[,"收缩压"]
t.bp[["收缩压"]] t.bp$"收缩压"
as.data.frame(t.bp$'收缩压')
|
tibble不使用行名, 需要行名时, 将其保存为tibble的一列。
练习
假设class.csv已经读入为R数据框d.class, 其中的sex列已经自动转换为因子。
library(tibble) library(readr) d.class <- read_csv('class.csv')
|
- 显示d.class中年龄至少为15的行子集;
x1 <- d.class[d.class["age"]>=15,] x1
|
- 显示女生且年龄至少为15的学生姓名和年龄;
注意:此处取sex=F时,表达式为x1$sex==“F”,两个等于号
x2 <- x1[x1$sex=="F",c("name","age")] x2
|
- 取出数据框中的age变量赋给变量x。