写在前面
计算机语言的学习并不困难,关键是一定要由浅入深的实际操作练习。也许最开始的比较简单,学习者一带而过没有实际操作,之后的进一步学习很可能会陷入不知所云的困境,实际操作所带来的感觉是无法替代的,其价值也是非常重要的。
1.对象与函数
R(https://cran.r-project.org/)是一个很好的数据分析以及可视化工具。在R中对象(object)是指可以赋值给变量(variable)的任何事物,在R语言中使用对象来存储数据也即储存变量,对象类型有标量、向量、矩阵、数组、数据框、列表。R语言通过函数(function)来提取对象属性、变量运算,函数可以来自R平台,也可以来自各种软件包(package)、自定义函数。
R语言不用事先声明对象或变量,对象在赋值时同步创建。对象或变量名以字母开头,可由字母、数字、“.”、“_”组成。对象赋值一般使用赋值符号“<-”,而在很多情况下也可以用传值符号“=”代替,也即“=”具有二义性,区别在于在函数内部时“=”只具有参数传递作用,举例如下:
在函数的括号内部使用“=”则将一个值传递给函数的内置参数(这个参数必须是函数具有的),而使用“<-”则将一个值赋值给变量,这时候a2作为一个对象而不是参数存在。可以使用objects()函数来查看当前对象目录,使用rm(objectname)来移除遗留的对象。
推荐在安装R后安装RStudio(https://www.rstudio.com/),在RStudio中编辑、运行R脚本。
1.1标量与向量
⑴赋值及运算符
标量是存储数据的最基本结构,可以是数值型、字符型或逻辑型(TRUE/FALSE)。在R语言里数值型变量运算的加、减、乘、除、幂指数分别为“ ”、“-”、“*”、“/”、“^”,一定要注意乘号不可省略;科学计数法可以用e来表示,也即“12300”记为“1.23e 4”。在不用变量赋值的情况下R平台里也可以直接进行数学运算,其运算符优先级与数学中一致。赋值为字符串时字符串需要添加引号。具体示例如下:
向量(vector)是存储数据的一维数组,标量可以理解为只含有一个元素的向量。向量可以使用执行组合的函数c()来创建向量,其数据来源可以是数值型、字符型、逻辑型数据(单个向量其数据类型必须相同),也可以来自标量,其参数可以是变量名,具体如下所示:
连续的整数可以使用“:”来表示也即“1:4”等效于“c(1,2, 3, 4)”。向量可以通过“[]”来进行索引,方括号内为元素的位置,可以是大于1的整数或者向量,位置前加负号“-”则表示删除这个位置的元素,但是使用向量索引时只能全是正整数或者负整数,不能混杂,如下所示:
R语言中判断符号有大于“>”、大于等于“>=”、小于“<”、小于等于“<=”、完全等于“==”、不等于“!=”、存在于“%in%”,如下所示:
在向量的索引[]里也可以加入判断语句,例如a[a>5]。
函数vector()可以来产生一个一定长度、一定类型的空向量,函数numeric()可以用来产生一个一定长度的数值型向量,函数character()可以用来产生一个一定长度的字符型向量,函数as.vector()可以用来将其他类型的数据转换为一定类型的向量,函数is.vector()可以用来判断数据是否为一定类型的向量,具体使用格式如下:
代码语言:javascript复制vector(mode="logical", length=0)
numeric(length=0)
character(length=0)
as.vector(x, mode="any")
is.vector(x,mode="any")
其中x为作用对象;length为向量长度(正整数);mode为向量类型,有任意"any"、数值型"numeric"、字符型"character"、逻辑型"logical"、整数"integer"、复数"complex"。
另外还有几种产生向量的方式:
rep(x, times=0) #产生重复,x可以是任意标量、向量,times为重复次数
seq(1.5, 6, by=0.5) #产生序列数,前面两个数为起始范围,by为步长
sample(x, size=3, replace=FALSE) #在x(任意向量)中随机抽样,size抽样次数,replace是否放回抽样
pretty(c(a, b), n) #将区间(a, b)插入n个等间距的间隔点,从而将区间分成n 1个相等区域,在画图中常用
⑵函数
R可以非常灵活的处理数值与文本数据,并且有很好的面向对象的编程方式,对于标量与向量,常用内置基本函数如下所示(其中绿色部分为数值处理;蓝色部分为字符串处理,R支持正则表达式;红色部分为对象属性及操作):
函数 | 含义 |
---|---|
round() | round(x, 2)将数值对象x四舍五入法保留小数点后2位 |
trunc() | 四舍五入去整,floor()向下取整,ceiling()向上取整 |
signif() | 取有效数字 |
sqrt() | 返回标量或向量元素的平方根 |
log() | log(x, y)返回以y为底x的对数,y默认值自然常数e |
exp() | 返回自然常数e的指数 |
sin() | 返回正弦值,其余还有cos()、tan()等详细查看?sin |
min() | 返回最小值,此外which.min()返回最小值id |
max() | 返回最大值,此外which.max()返回最小值id |
abs() | 返回数值对象绝对值 |
sum() | 返回对象元素的和 |
prod() | 返回对象元素的乘积 |
mean() | 返回对象元素的均值 |
var() | 返回对象元素的方差 |
sd() | 返回对象元素的标准差 |
median() | 返回对象元素的中位数 |
nchar() | 返回标量或向量元素的字符长度(包含空格) |
paste() | paste(a,b,c,sep=" ")将a、b、c粘贴为一个字符串,空格分割 |
tolower() | 转换为小写,toupper()转换为大写 |
substring() | substring(a,1,3)返回字符对象a中第1到第3个字符 |
strsplit() | strsplit(x, split=" ", fixed=FALSE, perl=FALSE)根据split将字符串对象x分割,默认split为正则表达式, fixed=TRUE则做精确匹配,当perl=TRUE时,使用perl的正则表达式规则,当分隔符为?, , {, |, (, )时,要使用'\'来消除特殊含义 |
grep() | grep("x", a),返回a中包含有字符“x”的元素id,可以使用正则表达式匹配,与strsplit()类似。此外还有grepl()返回匹配逻辑值、sub()替换、gsub()全局替换等 |
length() | 返回对象的长度也即元素个数 |
mode() | 查看对象数据类型(也即数值型、字符型等) |
names() | 返回向量元素名字 |
order() | 对向量元素排序,decreasing=TRUE则为降序,na.last=TRUE将缺失值排在最后,返回值为元素排名 |
sort() | 对对象元素排序(不限于向量),返回排序后的对象 |
union() | union(a, b)求两个向量并集 |
intersect() | 求两个向量的交集 |
setdiff() | setdiff(a, b)求在a中而不在b中的部分 |
setequal() | setequal(a, b)检验ab是否完全相同,此外is.element(12, a)检验元素12是否属于a,all(c%in%a)检验集合a是否包含c |
此外,缺失数据用大写NA表示,数据不确定用NaN表示,数据是无穷用Inf表示,判断是否为空数据用函数is.na(),判断是否不确定用函数is.nan(),数据是否有限用is.finite(),数据是否为无穷用函数is.infinite()。当向量含有缺失值时,若是计算向量的均值、方差等,需要在函数内设置参数na.rm=TRUE来去除缺失值。对于函数的使用方法可以使用?function来查询。
⑶内置常量
R语言有一些内置常量,方便使用的时候调取:
LETTERS #26个大写字母
letters #26个小写字母
month.abb #12个月份的三字母缩写
month.name #12个月份的全称
pi #3.14...
colors() #语言颜色表
上面这些内置常量都可以像其他向量一样取用,例:letters[c(1:8)]。
1.2矩阵与数组
矩阵(matrix)是一个二维数组,矩阵内所有元素必须具有相同的模式(数值型、字符型、逻辑型),矩阵可以使用向量、数据框等数据赋值转换,方法如下所示:
matrix(vector, nrow=m, ncol=n) #使用向量生成m行n列的矩阵
matrix(NA, nrow=m, ncol=n) #生成一个m行n列的空矩阵
as.matrix(x) #将对象转换为矩阵
is.matrix(x) #判断对象是否为矩阵
具体示例如下:
矩阵通过行、列id或者行列name对元素进行索引,也可以使用向量,id前加负号“-”则表示删除改行、列的元素,索引值也可以引入逻辑判断,如下所示:
注意,R中的判断符号有“<”、“<=”、“>”、“>=”、“==”、“!=”,逻辑连接符有与“&”、或“|”、非“!”。
索引里面也可以使用order()等函数:
对于矩阵,也适用上一小节的基本函数,对于二维数据增添的的属性函数如下所示:
ncol() | 返回矩阵、数组、数据框的列数目 |
---|---|
nrow() | 返回行数目 |
colnames() | 返回列名字 |
rownames() | 返回行名字 |
t() | 矩阵转置 |
数组(array)与矩阵相似似,但是维度可以大于2,类似的具有array()、as.array()、is.array()函数,创建方式如下所示:
array(vector, dimensions=c(3,3,3), dimnames=list(dim1,dim2,dim3)) #使用向量生成3*3*3的数组,dimnames为行名字、列名字、维度名字
示例如下:
数组可以通过三元id进行索引,如下所示:
1.3数据框与因子
有时候通过实验、调查获得的数据不只有一种模式,也即字符型、数值型等混杂在一起(但是每一列必须同一模式),需要一种简单的数据集来存储变量数据,即数据框(dataframe)。数据框可以使用data.frame()来创建,数据来源可以是任何类型的矩阵、向量等,其实用方法如下所示:
data.frame(matrix, row.names=NULL, check.names=FALSE) data.frame(col1, col2, col3...)
数据框必须有列名字,若没有则默认为X1、X2……。
数据框元素索引有三种方法,第一种为通过列的序号索引,第二种通过列名字索引,第三种通过$变量名索引,如下所示:
可以使用attach()函数来将数据框添加到当前平台,这样就可以直接使用列名字或变量名来调用数据框中的数据,使用完后用detach()来移除这些变量名(而不是移除数据框),如下所示:
变量(variable)可以分为名义型、有序型、连续型。名义型变量例如不同膳食类型、不同糖尿病类型,一般为字符型;有序型变量表示一种顺序关系,例如癌症的早、中、晚期,虽然也可以用数字表示,但不是数值关系,没有比较的意义,也无法衡量不同阶段间的差别大小;连续性变量可以为两个值之间的任何值,例如温度,DO,年龄等,可以进行数值运算。类别(名义型)变量和有序变量在R中称为因子(factor)。
函数factor()或者as.factor()以一个正整数向量的形式存储类别值,如下所示:
这时会自动按照字母和数字的顺序映射类别与数字,并存储为(1,2, 4, 3, 2)。若是存储有序变量,也即水平的顺序是有意义的,这时候需指定参数ordered=TRUE:
可以看到这时候不同level之间不再是并列的,有了顺序的意义。有时候按照字母顺序排序的因子向量不能满足现实需要,需要指定顺序,则可以通过设定levels参数来实现:
可以看到这时的顺序发生了变化。接下来我们通过一个小练习练熟悉数据框内不同模式变量差别:
函数str()和summary()可以很方便的查看总结数据信息。可以看到对于数值变量age会计算最大值、最小值、平均值等,但是对于因子变量,只会计算频数。变量类型不同,在统计中其处理方法也不同(例如RDA、CCA等),结果也不相同。
由于因子的存在,数据分组信息等都可以转换为一个变量,从而使得数据框可以存储远多于矩阵的数据。
1.4列表
列表(list)是R中最复杂的一种数据类型。列表是一些对象的有序集合,这些对象可以是向量、矩阵、数据框,甚至其他列表。列表可以使用list()函数进行创建,如下所示:
代码语言:javascript复制list(object1, object2, ...) #默认名字为编号
list(name1=object1, name2=object2, ...) #为每个对象命名
举例如下:
列表的索引可以使用双括号[[]]加编号或者名字,也可以使用$加名字提取,如下所示:
列表是一种简单的数据组织和调用方式,很多函数的计算结果也是列表(例如lapply()函数),因此列表在R中非常重要。
1.5软件包
R语言提供了大量的功能,而且大部分功能是通过可选模块进行下载安装,这些模块被称为包(package)。这些包即有用来分析作图的函数包,也有用来作为例子的数据包。包即可以在线安装,也可以下载后本地安装。对于大部分包,均可以通过函数install.packages()来进行安装,如下所示:
初次安装需要选择国内CRAN(ComprehensiveR Archive Network,R语言综合典藏网)接口,如安装失败可尝试不同的CRAN。
有些专项的包,有独特的安装方式,例如生物学数据分析最常用的BioconductorProject(http://www.bioconductor.org/),其安装方法如下所示:
代码语言:javascript复制source("https://bioconductor.org/biocLite.R")
biocLite("packagename")
此外,很多软件包并不包含在R的CRAN内,而在一些托管平台,最常见的为GitHub(https://github.com/),安装托管软件可以使用devtools软件包,安装方法一般为:
代码语言:javascript复制devtools::install_github("class/packagename")
其中class也即该软件在GitHub上存放的类别。查看当前环境中已经安装的软件包,可以使用如下命令:
代码语言:javascript复制.packages(all.available=T)
更为详细地,查看已经安装的软件包的版本、安装路径等信息可以使用如下命令:
代码语言:javascript复制installed.packages()[,c('Package','Version','LibPath')]
查看当前版本的R可以安装的软件包,可以使用如下命令:
代码语言:javascript复制available.packages()
利用该命令,我们可以查询想要安装的软件包是否在R的仓库里:
代码语言:javascript复制ap=available.packages()
grep('packagename', rownames(ap))
安装好的软件包,在使用之前需要library()函数加载,才能调用其中的函数。require()函数同样可以调用软件包,区别在于require会返回一个布尔值(True或False)来表示被加载的包是不是可用,而library函数会根据调用方式不同而有不同返回结果。
使用命令:
代码语言:javascript复制(.packages())
可以查看当前工作环境加载的R包,使用命令:
代码语言:javascript复制detach("package:packagename")
可以从当前工作环境移除R包。
关于软件包的使用说明,可以使用help()或者直接??packagename来查询,使用命令help(package='packagename')可以查看某个软件包所包含的全部函数。此外,一个很重要的软件包是installr,其中的updateR()函数能将R更新到最新,并将已安装的兼容最新版本的程序包整合到新版本R中,如下所示:
代码语言:javascript复制library(installr)
updateR()
1.6输入与输出
R可以通过键盘输入数据,也可以导入其他数据框软件生成的数据,常用的一般为文本文件、Excel文件、Web文件等。
⑴键盘输入数据
函数edit()会自动调用一个允许手动输入数据的文本编辑器,来为对象(向量、矩阵、数据框)赋值,如下所示:
代码语言:javascript复制mydata=data.frame(a1=numeric(0), a2=numeric(0), a3=character(0))
mydata=edit(mydata)
在这个编辑器里点击变量名可以更改变量类型(数值型、字符型),还可以点击未命名的变量(var4...)添加新变量。可以直接从其他地方复制数据并粘贴进去。关闭编辑器后,输入的数据即被保存赋值。
⑵从带分隔符的文本文件导入数据
函数read.table()可以从带分隔符的文本文件导入数据,此函数读入一个表格格式的文件并保存为数据框,使用方法如下:
代码语言:javascript复制read.table("file", header=FALSE, sep = "",quote = ""'",
row.names, col.names,na.strings = "NA",
colClasses =NA, check.names = TRUE
stringsAsFactors = default.stringsAsFactors())
其中file为文件名,header=FALSE第一行不是变量名(R会添加默认变量名),为TRUE则会使用第一行作为变量名;row.names、col.names设置那一列为行名字,哪一行为列名字;sep设置分隔符,默认是一个或多个空格、制表符tab;设置stringsAsFactors=FALSE则不会把字符型自动转化为因子;colClasses可以设置每一列的模式(logical、numeric、character);check.names是否检查变量名(合不合语法要求);quote=""'"表示单双引号内部为完整字符串的一部分,这对于字符串内含有与分隔符相同字符时很有用,需要与sep搭配设置。注意文件名前需要添加完整的目录(路径不同层级之间使用/或\)。
一般情况下,我们把文件都复制到工作路径方便引用,查询当前路径使用getwd(),更改路径使用setwd(),如下所示:
除了read.table()外,还有专门读取逗号分隔的csv文件的read.csv()等,如下所示:
⑶保存导出数据
R输出文件包括数据的输出、图片的输出。数据输出可以使用write.table(),其使用方法与read.table()类似,如下所示:
代码语言:javascript复制write.table(object, file="filename", quote=F,row.names=F, col.names=T, sep='t')
其中quote设置字符型元素是否用引号引起来。
对于图片,我们更推荐直接使用Rstudio的图形界面输出图片。在脚本中可以使用pdf()函数直接保存图片,如下所示:
代码语言:javascript复制pdf("practice.pdf", width=9, height=9)
x=1:10
y=1:10
plot(x, y, type="p")
dev.off()
图片会自动保存在当前工作路径。