目录
一、R语言的介绍
R语言的下载与按照
Rstudio
R包的安装
工作空间管理
基本运算
二、R的数据结构
1. 数据类型
2. 数据类型的转换和判断
3.数据结构
三、导入/导出数据
获取内置数据集
获取其他格式的数据
四、数据框的常用操作
1.处理数据框的基本函数
2. 筛选行和列
3. arrange()排列行
4.添加新的变量
5. 拆分数据框
6. 数据框的合并
7. %>%的传递操作
8. 数据框长宽格式转换
9. 缺失值处理
10. 条件循环语句
11. 分割列数据和合并列数据
12. 字符串常用函数
13. 大型数据集的处理策略
R语言的下载网址:R: The R Project for Statistical Computing。
建议下载最新版本,避免有些R包无法运行。 此外选择镜像下载,下载速度会快很多。
国内的镜像比较多,所以可以根据自己的所在位置选择合适的镜像。因为笔者在北京,就选择了北大镜像下载。安装的话建议直接默认,点击下一步,装在C盘的位置。
Rstudio下载网站:Posit | The Open-Source Data Science Company
Rstudio界面介绍:
1.脚本编辑器:这个区域主要是用来撰写代码,编辑器写的代码不会直接执行,并且可以保存成为.R格式的文件,保存既往的代码。可以直接打开,修改,运行。
2.控制台:是R的运算执行区域,可以直接写代码,但是由于不利于保存,较少在控制台写代码。二、控制台还可以显示运算的结果。
3.环境:该区域主要是存储目前的数据内容,包括任何数据格式
4.其他菜单栏:file默认是当前的工作路径,plot输出的图片结果展示,package是R中已经安装的R包。
R包:可以理解成为多个函数的打包存放,包含函数、数据、帮助文档、描述文档等
R包分为3类:1.基础包,在R启动的时候自动调入内存,以满足基础的数据处理和统计分析需要。2.备用包,随着R安装而安装,但是需要使用函数library()或require()调入内存后才能使用。3.捐赠包,由统计专家发布,用户需要单独下载,然后用函数library()或require()调入内存后才能使用。
1. 查看当前内存中调用的R包。
search() #目前工作状态下有哪些R包被加载
require("strings")#strings包是否安装,输出为逻辑值T或者F
2. R包的安装
包使用的顺序:安装——加载——调用函数
R包是否安装成功评价:library加载时是否会报错
方法一:install.packages("R包名称" )
install.packages("epiDisplay")
install.packages("BiocManager")
上面的命令安装了名为“epiDisplay”的包,凡是CRAN的R包,都统一使用install.package()
方法二:bioconductor中的R包,统一使用BiocManager::install()安装
BiocManager::install("limma")
因为在安装limma包时会同时安装一些其他包,如果这些包某些已经被安装,则会提示是否更新这些包?一般情况选择none,只需要在控制台回复n。
方法三:github上的R包,使用devtools::install_github()
install.packages("devtools")#第一次需要先安装devtools包
devtools::install_github("jmzeng1314/idmap1")
发布在github上的包,需要在包的名字前面包含作者名字。
已经安装的包,可以用::快速调用里面的函数
!!!!!更新R包,重新安装,先删除后重新安装。建议不用update()
常见的报错:
- 关键词connection,internet,url、404、http,download往往是网络限制,或者镜像没有选择合适
- not writable/permission denied 权限问题:管理员方式重新打开Rstudio,重新安装
- ????中文用户名惹的祸,修改环境变量
- package not available 四种问题:1.包的名字拼写错误;2.命令用错;3.R包过时;4.版本不匹配
- 是否更新,是否:先拒绝no,不可以在更新
- 依赖包报错或者版本就,单独装依赖包/更新
3. 条件语句来安装R包
if(FALSE) 则后面的代码被跳过
if(TRUE) 则后面的代码执行
if(!require(strings))install.packages("strings")
if(!require(feather))install.packages("feather")
4. 其他R包知识(不重要)
R包的如何使用:1、快速查看函数帮助文档?包的名字;2、浏览器搜索:R包名字 package,找到bioconductor官网的介绍;3、加载Vignettes,利用函数browseVignettes("stringr")
文件名称在代码中出现必然以字符串形式存在,并存在于实际参数的位置上
工作空间是R语言的工作环境,所有创建的对象都被临时保存在工作空间中,可以用函数ls()列出当前工作空间中的所有对象:
也即是Rstudio中资源环境中的所有对象。在退出R时,如果选择保存工作空间,R将会在工作空间所在的文件夹中创建两个新的文件。之前在R中输入的任何命令都将保存到一个名为.Rhistory的文件中,而当前的工作空间中的所有对象将保存在.Rdata的文件中。两个文件都没有前缀。
工作目录:是R用来读取文件和保存结果的一个文件夹。
getwd()能展示目前的工作目录。
setwd()设定当前的工作目录
getwd()
想要把当前的工作空间保存到一个指定的文件:
save("myfile.Rdata")
myfile.Rdata将会保存在当前的工作目录下。下次调用只需要直接load(“文件名.Rdata”)即可。
1.数值型 numeric 1,2,3
2.字符型 character “a",'mm'
3.逻辑值 logical TRUE;FALSE;NA
NA表示存在但未知,NULL表示不存在
class()判断数据类型的函数:
常见的错误:
class(a)
输出为没有object:a,表示没有定义数据a是什么。
calss("a")
class写错
class(true)
系统默认的逻辑值TRUE,一旦改变任何一个大小写,都会被认为是对象object,因此输出为找不到对象true
class(3)
使用了中文括号
R语言常见的报错:
function在R中表示函数;object表示对象;TRUE必须大写,R中严格区分大小写;括号必须用英文,unexpected input in "class("表示问题出现在(这里
#unepected出现表示代码写错了;找不到对象往往是没有引号或者没有定义对象
#is.numeric() 是否是数值型 #is.logical() 是否是逻辑型
#is.character() 是否是字符型
#as.numeric() 转化为数值型
#as.character() 转化为字符型
#as.logical() 转化为逻辑型
is.numeric(3)
is.numeric("4")
as.numeric("jimmy")
class(as.numeric("jimmy")) #s说明NA这里是数值型
数据结构: 向量;数据框;矩阵;列表
3.1 向量
用于存储数值型、字符型、逻辑型数据的一维数组。函数c()可以用来创建向量。每一个向量中数据的类型必须一致,仅为一种数据类型。
修改向量中的元素 ,R语言中任何修改都要赋值,没有赋值就没有发生过
3.1.1 向量的生成
方法一:用 c() 结合到一起
c(2,5,6,2,9)
c("a","f","md","b")
方法二:连续的数字用冒号“:”
x <- 1:5
方法三:有重复的用rep(),有规律的序列用seq(),随机数用rnorm()
rep("x",times=3)
seq(from=3,to=21,by=3) #从3开始到21,每3个数取一个数
rnorm(n=3) #生成3个随机数
方法四:通过组合,产生更为复杂的向量
paste0(rep("x",times=3),1:3)
#表示将向量1与向量2联合在一起,如向量1是xxx,向量2是1,2,3,组合在一起为X1,X2,X3
3.1.2 向量的计算
max(x) #最大值
min(x) #最小值
mean(x) #均值
median(x) #中位数
var(x) #方差
sd(x) #标准差
sum(x) #总和
scale(x) #将x标准化
range(x) #求x的全距
quantile(x) #求x的分位数
length(x) #长度 向量中元素的个数
unique(x) #去重复,向量从左到右第二次或多次出现的取值去除。
duplicated(x) #对应元素是否重复,判断值为逻辑值,如果需要将重复的值结果为FALSE,需要在前面加入!函数
table(x) #重复值统计,计算每个取值的重复个数
sort(x) #排序,默认从小到大
sort(x,decreasing = F)
sort(x,decreasing = T)
3.1.3 向量的比较
x = c(1,3,5,1)
y = c(3,2,5,6)
(1)比较运算,生成等长的逻辑向量,对应位置的数据进行比较
x == y
y == x
不同长度的向量比较:
x = c(1,3,5,6,2) y = c(3,2,5) x == y #循环补齐,输出结果为最长的元素个数逻辑值。
利用循环补齐简化代码:
paste0(rep("x",3),1:3) paste0("x",1:3)
(2)数学计算
x + y
(3)连接
paste(x,y,sep=",") #paste表示将两个元素连接在一起,seq表示分隔
paste与paste0的区别:
paste(x,y) paste0(x,y) #paste0两个元素之间无缝连接,paste默认有空格连接,或者设置相应空格 paste(x,y,sep = "") paste(x,y,sep = ",")
paste可以连接两个或者多个对象,输出为字符型结果:
paste(rep("student",time=3),4:6,rep("class",time=3),sep="a") paste(rep("student",time=3),4:6,rep("class",time=3),sep=",") paste(rep("student",time=3),4:6,rep("class",time=3)) paste0(rep("student",time=3),4:6,rep("class",time=3))
(4)向量之间的交集、并集、差集
intersect(x,y) #交集
union(x,y) #并集
setdiff(x,y) #差集 在x中有y中没有的
setdiff(y,x) #差集 在y中有x中没有的
setequal(x,y) #判断两个向量是否相等,输出为逻辑值
x %in% y #x的每个元素在y中存在吗
y %in% x #y的每个元素在x中存在吗
(5)向量取子集
核心思想:[ ]将TRUE对应的值挑选出来,FALSE丢弃
x <- 8:12
根据逻辑值取子集,[ ]内为逻辑值相关的运算,括号内只能是逻辑值
x[x==10] #第一个x表示从什么向量中挑选,括号内为挑选的条件,需要与x等长的逻辑值
x[x<12]
x[x %in% c(9,13)]
根据位置取子集,只支持数据,不支持逻辑值
x[4]
x[2:4]
x[c(1,5)]
x[-4] #-4不选第四个,保留剩下的,-表示反选
x[-(2:4)]
x[1,5] #错误:因为不是一个向量,2:4是向量,c(1,5)也是向量,所以[]内必须是X的下标组成的向量
补充:元素的名字
scores = c(100,59,73,95,45) names(scores) = c("jimmy","nicker","Damon","Sophie","tony") #元素的名字,本质还是向量 scores scores["jimmy"] scores[c("jimmy","nicker")] names(scores)[scores>60]
虽然score是向量,但是可以给向量赋予名字,再去取子集。
3.2 矩阵
矩阵是一个由行和列组成的二维数组。矩阵中每个元素具有相同的模式(数值型、字符型或逻辑型)。函数matrix()常用于创建矩阵。nrow指定行数,并自动计算出列数。byrow默认为FALSE,即是按列将数值进行排列,如果需要按行进行排列,着byrow=TRUE。
m <- matrix(1:9, nrow = 3) #ncol是列,只需要写一个,另一个就默认了
colnames(m) <- c("a","b","c") #加列名
3.2.1 矩阵取子集:矩阵取子集不支持$,$只能用于数据框格式
m
m[2,] #不支持$的使用
m[,1]
m[2,3]
m[2:3,1:2]
m
3.2.2 矩阵转置:把矩阵的行和列变换
t(m)
3.2.3 矩阵乘法:运算符号为%*%,需要第一个矩阵的列数等于第二个矩阵的行数
mat1 <- matrix(1:6,nrow=3)
mat2 <- matrix(5:10,nrow=2)
dim(mat1)#计算矩阵的维度,也即是行和列数
dim(mat2)
mat1%*%mat2
得到一个3x3的矩阵
3.2.4 矩阵的行列式和逆矩阵,可以使用函数det()和solve()实现
mat3 <- matrix(1:4,nrow=2)
det(mat3) #方阵的行列式
solve(mat3) #逆矩阵
3.2.5 对矩阵行列进行计算
rowSums(mat1)#对矩阵行进行求和
colSums(mat2)#对矩阵列进行求和
rowMeans(mat1)#对矩阵行进行求均值
colMeans(mat2)#对矩阵列进行求均值
3.3 数组
数组是多维数组,与矩阵相似,但是维度大于2。数组有一个特殊的维度属性dim。一个向量,加上维度属性后定义了一个数组。
A <- 1:24
dim(A) <- c(3,4,2)#3行4列,两个维度
A
除此之外还可以使用函数array()创建
3.4 列表
可以由不同类型的对象混合组成。
l <- list(m1 = matrix(1:9, nrow = 3),
m2 = matrix(2:9, nrow = 2))
l
l[[2]] #提取出的是一个矩阵,l[2]则提取出的是列表
l$m1
3.5 数据框(重点)
数据框是一个由行和列构成的二维结构,行表示观测或记录,列表示变量或指标。与矩阵不同的是,数据框的不同列可以是不同的数据类型。使用data.frame()创建。
df1 <- data.frame(gene = paste0("gene",1:4),
change = rep(c("up","down"),each = 2),
score = c(5,3,-2,-4))
df1 #每一列是一个向量,列名=列的内容
3.5.1 数据框的属性
dim(df1) #显示数据框的维度,几行几列
nrow(df1) #行数
ncol(df1) #列数
rownames(df1) #提取df1行名
colnames(df1) #提取df1列名
3.5.2 数据框取子集
按$列名提取
df1$gene
按坐标
df1[2,2] #第2行,第2列
df1[2,] #第2行,所有列
df1[,2] #所有行,第2列
df1[c(1,3),1:2] #第1,3行,第1,2列
按名字
df1[,"gene"]
df1[,c('gene','change')] #$只能提取一列,这种麻烦的方式可以同时取出多列
按条件(逻辑值)
df1[df1$score>0,]
df1[df1$change=="up",] #up是字符型,必须打上引号,不然会报错
如何取数据框的最后一列?
df1[,3] #只能取第三列,如果数据框存在多列,则不能用3取最后一列 df1[,ncol(df1)] #当不知道数据框最后一列时,只能用ncol()取数据框的最后一列
如何取数据框除了最后一列以外的其他列?
df1[,-ncol(df1)] #减号只能用在数字,感叹号用在逻辑值
筛选score > 0的基因
df1[df1$score > 0,1] df1$gene[df1$score > 0] ##取子集的逻辑值向量,只需要与x对应,不必由x产生
3.5.3 数据框的修改
值的修改:
#改一个格
df1[3,3] <- 5
df1
#改一整列
df1$score <- c(12,23,50,2)
df1
#新增一列p value
df1$p.value <- c(0.01,0.02,0.07,0.05)
df1
行列名称的修改:
#改行名和列名,本质就是修改向量
rownames(df1) <- c("r1","r2","r3","r4")
#只修改某一行/列的名
colnames(df1)[2] <- "CHANGE" #对列名取子集第二列出来,改为“CHANGE”
数据框的连接:
test1 <- data.frame(name = c('jimmy','nicker','Damon','Sophie'),
blood_type = c("A","B","O","AB"))
test1
test2 <- data.frame(name = c('Damon','jimmy','nicker','tony'),
group = c("group1","group1","group2","group2"),
vision = c(4.2,4.3,4.9,4.5))
test2
test3 <- data.frame(NAME = c('Damon','jimmy','nicker','tony'),
weight = c(140,145,110,138))
test3
merge(test1,test2,by="name")
merge(test1,test3,by.x = "name",by.y = "NAME")
#merge默认是交集,其他需要查看帮助菜单?merge
library(MASS)#载入R包
data(bacteria)#使用data函数调出数据集
CSV格式(本质是纯文本格式)的打开方式:1.excel打开;2.记事本打开;3.sublime;4.R语言读取:read.csv()
分隔符:逗号,空格,制表符(\t)
表格文件读入R语言中,就得到一个数据框,对数据框进行修改不会同步到表格中
1. 导入数据
read.csv() 读取CSV格式文件
read.table() 读取txt格式文件
(1)行名和列名的正确识别
ex1 <- read.table("ex1.txt")
#R语言不能识别到列名,重新分配了列名V1-V5,会将数字转化为了字符型
ex1 <- read.table("ex1.txt",header = T)
当原始表格存在列名时需要设置参数header=T,让系统识别列名
(2)行列名中特殊符号的处理
ex2 <- read.csv("ex2.csv") #1.行名不能正确识别;2.列名将-改为了,;
R语言中原始默认不存在特殊字符,就将-改为了.
ex2 <- read.csv("ex2.csv",row.names = 1,check.names = F)
#row.name是一个行名向量,定义行名的名字;check.names=T会自动识别特殊字符并改为规范符号,F则默认
(3)重复行名
rod <- read.csv("rod.csv",row.names = 1) #数据框不允许存在重复行名
(4)缺失值的处理
soft <- read.table("soft.txt")
在第二行之后只存在四个值,第五列的值缺失,系统不能识别,因此不能读取
soft <- read.table("soft.txt",header = T,fill = T)
一般情况fill=T,默认将空格内容填充为NA,但是不能识别几个空格,可能出现串行没比如两个空格建,R语言就会认为只有一个空格,出现错误
soft <- read.table("soft.txt",header = T,sep = "\t")
制表符\t的设置表示一个制表符就是一个空格
为了避免空格错位的问题:
read.delim()#读取txt文件,能避免上述问题 soft1 <- read.delim("sotf.txt")
其他格式:
read.xlsx( )读取Excel格式的文件,需要加载第三方包(例如openxlsx包,readx1包和gdata包);第二种方法将excel文件另存为csv格式,使用read.csv( )读取。
#如果没有安装请先安装
#install.packages("openxlsx")
#install.packages("readxl")
#install.packages("gdata")
library(openxlsx)
patients.data <- read.xlsx("patients.xlsx",sheet=1)
#sheet=表示读取的excel表格中第几个sheet
read.spss( )读取SPSS文件,read.xport( )读取SAS文件,read.dta( )读取Stata文件,但需要使用借助拓展包foreign包。foreign包主要功能读写其他格式的统计软件数据。
library(foreign)
patients.data <- read.spss("patients.sav",to.data.frame = TRUE)
To.data.frame参数的默认值为FALSE,如果不设置为TURE,返回的将是一个列表形式的数据。
2. 导出数据
数据框导出:
write.csv(test(数据框名称),file=“example.csv(输出的文件名称)”)
txt格式:write.table() eg:write(test,file="example.csv")
write.csv(soft,file="soft.csv")#一定要加后缀名.csv
Rdata格式:是R语言特有的数据储存形式,保存的是变量,不是表格
save()保存,load()加载;save(test,file="example.csv"),load("example.Rdata")
save(soft,file="soft.Rdata")#一定要加后缀名.Rdata
rm(list=ls())
load(file="soft.Rdata")
易犯错误:
save(soft,file="soft2.csv")
系统不报错,但是决定文件性质的是函数不是后缀。所以用excel/read.csv打开就是乱码,但可以使用Rdata语言打开,使用load函数。
3. 其他包导入/导出数据
(1)用rio包导入和导出数据
rio包提供一个类似于万能工具的包为目标,用统一的import()和export()函数简化用户导入和导出数据,此外convert()函数可以实现不同文件格式之间的转换。
以数据集infert为例:
data(infert)
library(rio)
export(infert,file="infert.csv")
#可以在工作目录下找到新生成的文件infert.csv的数据文件。
convert("infert.csv","infert.sav")
#将csv文件转换成为sav文件
infert.data <- import("infert.sav")
#将生成的infert.sav文件导入R中,命名为infert.data
import() 集成函数,根据文件的后缀选择相应的读取文件。
import_list()可以用来读取excel中存在多个工作簿的列表,输出形式为列表。
(2)其他包导入或导出文件
用于读取和导出文件的R包
- #base包
- #readr包
- #data.table包
library(data.table) #不认识行名
a=fread("soft.txt")
因为不能识别行名,自动将行名设置为第一列
class(a) #存在data table的数据结构会影响后续分析
b=fread("soft.txt",data.table = F)#data.table=F能纯化为数据框结构
class(b)
参数data.table=F,可以将读取的文件直接设置为数据框格式,不用data.table格式。现在需要将数据框的第一列设置为行名。
rownames(b)=b[,1]#b第一列为行名
b=b[,-1]#第一列与行名重复,因此删除第一列名字。用-1表示删除第一列
加载数据集epiDisplay包中的Familydata
rm(list = ls())
#install.packages("epiDisplay")
library(epiDisplay)
data(Familydata)
1.1 查看数据框的前几行和后几行,head( )和tail( )函数。
head(Familydata,5)#不设置行数的话,默认为6行
tail(Familydata,3)
1.2 查看数据框变量名,names( )函数
names(Familydata)
1.3 数据框按照某个变量的值排序,order( )函数
Familydata[order(Familydata$age),]
Familydata[order(Familydata$age,decreasing = T),]
Familydata[order(-Familydata$age),]
本质跟取子集一样,按照age年龄大小进行排序,默认从小到大,如果想要从大到小增加参数decreasing=T。也可以使用age取相反数,在从小到大排序。
1.4 查找和删除重复数据
duplicated(Familydata$code)
duplicated( ) 函数用于查找是否有重复,第二次出现就会表示为重复,输出的值为逻辑值。因为输出的值比较时,难以肉眼检查是否存在重复值,可以使用函数table()统计FALSE和TRUE出现的频数,或any()用于检测逻辑值是否含有TRUE
any(duplicated(Familydata$code))
#结果为FALSE表示没有TRUE,没有重复值
table(duplicated(Familydata$code))
#输出的结果只有FASLE的频数,说明没有TRUE
删除重复数据
Familydata1 <- Familydata
Familydata1[12,] <- Familydata[2,]#给数据框增加一列重复的值
table(duplicated(Familydata1$code))#结果TRUE表示存在重复值
使用函数which( )可以找到变量code( )重复值所在的行
which(duplicated(Familydata1$code))
删除重复的行:利用取子集的思维,将子集中的TRUE提取出来,因此需要将不重复的FASLE转变为TRUE,重复值TRUE转为FALSE不提取出来。
Familydata2 <- Familydata1[!duplicated(Familydata1$code),]
identical(Familydata2,Familydata)#说明两个数据框一致
除了取子集外,函数distinct(数据框,去重的列,.keep_all=T)数据框按照某一列去重复,unique是去重复向量
distinct(Familydata1,code,.keep_all=T)
1.5 在数据框中删除和添加变量
使用$添加变量:
Familydata$log10money <- log10(Familydata$money)
使用函数transform()函数
Familydata <- transform(Familydata,log10money=log10(money))
使用取子集负号删除变量,负号不能用名称取。
Familydata <- Familydata[,-7]
1.6 把数据框添加到搜索路径
搜索路径的目的时因为在变量名称前面加上数据框名和符合$,这种方式有时候比较繁琐,一次函数attach可以将搜索的路径限定在数据框xxx中,直接输入变量名称即可。
函数attach( )可以将数据框添加到搜索路径中,detach()退出搜索路径。
attach(Familydata)
detach(Familydata)
案例:MASS包中birthwt数据集中包含189个研究对象,10个变量,一项关于新生儿低体重危险因素的病例对照研究。
2.1 筛选行:
library(dplyr)
data(birthwt,package = "MASS")
筛选变量年龄age大于35岁的所有记录:
#取子集的方法
birthwt[birthwt$age>35,]
#filter函数
filter(birthwt,age>35)
filter()函数,第一个参数是数据框名字,第二个参数级随后的参数是筛选数据框的表达式。
筛选体重大于4000g或小于2500的所有记录
filter(birthwt,bwt>4000|bwt<2500)
birthwt[birthwt$bwt>4000|birthwt$bwt<2500,]
如果有多个条件,可以用逗号隔开,例如筛选体重大于4000g或小于2500,年龄age大于35岁的所有记录
birthwt[(birthwt$bwt>4000|birthwt$bwt<2500)&birthwt$age>35,]
filter(birthwt,bwt>4000|bwt<2500,age>35)
函数slice()可以按照行号选择指定的行,第二行到第五行的所有记录
slice(birthwt,2:5)
2.2 选择列
函数select()用于选择数据框中的列(变量)
select(birthwt,age,bwt,race,smoke)
注意:MASS包中也有函数select,为了避免混淆如果需要同时加载两个包,可以使用dplyr::select()的格式完成。
除了基础包中的order之外,arrange函数也是可以将数据框记录按照某个变量进行排序的。
arrange(birthwt,bwt)
birthwt[order(birthwt$bwt),]
将变量bwt从小到大进行排序。如果需要从大到小排序,order的参数descreasing=T,arrange()函数的可以使用desc()函数实现或相反数。
birthwt[order(birthwt$bwt,decreasing = T),]
arrange(birthwt,-bwt)
arrange(birthwt,desc(bwt))
当存在多个变量的排序时,按照顺序一次添加即可,首先排序的变量放在第一个,然后第二个
arrange(birthwt,bwt,age)
先按bwt从小到大排序,存在重复相同数字时,按照age年龄进行从小到大排序。
除了基础方法中的$和transform之外,还可以使用函数mutate()。第一个参数就是数据框名称,第二个参数为定义的数据内容,类似于transform函数。
mutate(birthwt,lwt.kg=lwt*0.4536)
transform(birthwt,lwt.kg=lwt*0.4536)
函数group_by( )可以将数据框按照某一个或某几个分类变量拆分成为多个数据框。
输出的数据框虽然外观没有改变,但是已经按照race的三个值分为三个数据框,在进行dplyr包的后续计算会产生三个不同的值,比如:
summarise(group_by(birthwt,race),mean.bwt=mean(bwt),sd.bwt=sd(bwt))
按照race分类变量,计算各组中的出生体重的平均值和标准差。
summarise()函数可以用于计算数据框某个变量的指定统计量。
格式:summarise(数据框,生成统计量名称=统计函数(变量))如果有多个可以用逗号隔开。
tibble是tidyberse系列包(dplyr包)提供的一种类似于数据框的格式。
1. 按照某个共有变量合并
数据框的连接除了merge()函数默认取交集外,还可以使用dplyr包中的jion()函数。
test1 <- data.frame(name = c('jimmy','nicker','Damon','Sophie'),
blood_type = c("A","B","O","AB"))
test1
test2 <- data.frame(name = c('Damon','jimmy','nicker','tony'),
group = c("group1","group1","group2","group2"),
vision = c(4.2,4.3,4.9,4.5))
test2
library(dplyr)
inner_join(test1,test2,by="name")
right_join(test1,test2,by="name")
left_join(test1,test2,by="name")
full_join(test1,test2,by="name")
semi_join(test1,test2,by="name")
anti_join(test1,test2,by="name")
注意两者的区别!
2. 纵向合并
要纵向合并两个数据框可以使用rbind()函数。前提:两个被合并的数据框必须拥有相同的变量,通常用于合并数据框中的观测。
data1 <- data.frame(id=1:5,
sex=c(0,1,1,0,1),
age=c(32,46,25,42,29))
data2 <- data.frame(id=6:10,
sex=c(1,0,1,1,0),
age=c(52,36,28,34,26))
rbind(data1,data2)
3. 横向合并
横向合并两个数据框,可以使用cbind()函数,前提:用于合并两个数据框必须拥有相同的行数。
data3 <- data.frame(days=c(28,57,15,7,19),
outcome=c("discharge","dead","discharge","dead","dead"))
cbind(data1,data3)
library(dplyr)
x1=filter(iris,iris$Sepal.Width>3) #筛选行
x2=select(x1,Sepal.Length,Sepal.Width) #筛选列
x3=arrange(x2,Sepal.Length) #从小到大排序
x4=arrange(x2,desc(Sepal.Length)) #从大到小排序
多次赋值,产生多个变量,x2和x1是中间变量,没有用容易让数据表格更加繁琐。
x=iris %>%
filter(Sepal.Width>3) %>%
select(Sepal.Length,Sepal.Width) %>%
arrange(Sepal.Length)
长格式:每次观测作为一条记录,所以一个观测对象可能占有多行。
宽格式:一个对象的多个不同时间点的观测都记录在同一行里,即是一个观测对象只占一行。
案例:以数据集Indometh,该数据集关于药物吲哚美辛的药物代谢动力学数据,一共6个试验对象,每名对象在连续8小时内定时测试血液中的药物浓度,一共测定了11次的值
这是一个典型的长数据格式,一个观测对象具有不同时间测试结果。
方法一:基础包中reshape()函数
#长数据转为宽数据
wide <- reshape(Indometh,
v.names = "conc", #结果变量的值
idvar = "Subject", #观测对象的id
timevar = "time", #时间变量/也即是多次测量的变量
direction = "wide")
#宽数据转为长数据
long <- reshape(wide,
idvar = "Subject", #观测对象的id
varying = list(2:12), #需要合并的列
v.names = "conc", #合并后值得名称
direction = "long")
time不能识别出具体的时间,只能以1,2,3这样进行分组。
方法二:tidyr包中pivot_wider()函数用于长格式转为宽格式,pivot_longer()用于把宽格式转换为长格式
library(tidyr)
wide <- pivot_wider(as.data.frame(Indometh),
names_from = time, #分组名称
values_from = conc) #变量值
wide
因为Indometh不是数据框格式,所以需要将转化为数据框格式,如果是数据框格式,直接输入数据名称。
long <- pivot_longer(wide,-Subject, #-观测ID名称
names_to = "time",#合并后的组名
values_to="conc") #变量值组的名称
一个整洁的数据集应该满足:每一行为一个观测,每一列代表一个变量。在对医学数据进行分析之前,通常情况下应先把数据集转换为长格式。
9.1 识别缺失值
判断是否是缺失值NA,is.na( )函数,当数量较多时,需要用table函数统计频数便于找到NA。
height <- c(100,150,NA,160)
is.na(height)
table(is.na(height))
注意:任何包含NA的计算结果都是NA,如果需要计算统计量,需要将NA移除,一种方法是设置参数na.rm=T,一种是使用函数na.omit()把缺失值省略。
比如计算某变量的平均值
mean(height)
mean(height,na.rm = T)
mean(na.omit(height))
函数summary()在计算向量的统计量时,会自动忽略缺失值,同时显示出缺失值的个数
summary(height)
9.2 探索数据框中的缺失值
案例:数据集iris,包含了150个鸢尾花样品,分为3个品种(species),每个品种各有50个样品,每个样品的四种属性,即花萼长度(Sepal.Length)、花萼宽度(Sepal.Width)、花瓣长度(Patal.Length)和花瓣宽度(Patal.Width)。数据集不含缺失值,missForest包中的prodNA随机生成缺失值。
#install.packages("missForest")
library(missForest)
set.seed(1234) #设置随机数种子
iris.miss <-prodNA(iris) #默认生成数据10%的随机缺失值
summary(iris.miss)
9.3 缺失值的处理
处理缺失值可以采用3种方法:1.删除,删除带有缺失值的变量或记录;2.替换,用均值、中位数、众数或其他值替代缺失值;3.补全,基于统计模型推测和补充缺失值。
删除:当缺失值的数量比较少,删除后对分析结果影响不大,可以使用函数na.omit()删除数据框中的缺失值。
#方法一:
iris.sub <- na.omit(iris.miss)
nrow(iris.sub)
#方法二:
iris.sub <- iris.miss[complete.cases(iris.miss),]
结果输出为97,也即是只有97列是完整的记录。complete.cases表示如果数据框种有一行有一个缺失值就输出FALSE,完整数据输出为TRUE,通过取子集的方法把TRUE提出出来,也即是完整数据。
使用特定数值替代缺失值:
以变量Spetal.Length为例,用忽略缺失值后的均值替代缺失值
Sepal.Length.Mean <- mean(iris.miss$Sepal.Length,na.rm = T)
Sepal.Length.Mean
iris.miss1 <- iris.miss#便于后面仍然可以使用iris.miss作为案例
iris.miss1$Sepal.Length[is.na(iris.miss1$Sepal.Length)] <- Sepal.Length.Mean
#提取出向量中缺失值,将其赋值给均值
多重插补:是一种基于重复模拟的处理缺失值的方法,常用于处理较为复杂的缺失值问题。R中有多个可以实现缺失值多重插补的包,Amelia包,mice包,mi包。
其中mice包使用链式方程的多变量补全法,被广泛用于数据清洗的过程。常用的方法有:(1)预测均值匹配(pmm)实质上就是线性回归,适用于数值型变量;(2)Logistic回归(logreg):适用于二分类变量;(3)多分类Logistic回归(ployreg),适用于无序多分类变量;(4)比例优势比模型(polr),适用于有序多分类变量。
#install.packages("mice")
library(mice)
imputed.data <- mice(iris.miss,seed=1234)
summary(imputed.data)
在predictorMatrix中每一行代表含有缺失值的变量,如果该行对应的列元素为1,则代表该列变量被用于建模预测。mice输出的结果是一个列表,其中imp也是一个列表,存放着每个变量缺失值的插补值。
imputed.data$imp$Sepal.Length
函数mice()默认进行5次随机抽样,得到的5次随机抽样的插补值。可以选择其中一组来进行插补。
complete.data <- complete(imputed.data,3)
complete.data
函数complete( )表示提取补全的数据,使用第三组数据补全缺失值。
10.1 条件语句
条件语句基本结构:
if(是一个逻辑值,不可是多个逻辑值组合成的向量){CODE1}else{CODE2}
if()里面的逻辑值是TRUE,后面大括号内的语句就会被执行,否则反正
i=-1
if(i<0){print("up")} #i=-1<0则为TURE,这运行print “up“
if(i>0){print("up")}
i=9
if(i<0){print("UP")}else{print("down")}
ifelse()#具有三个重要参数:x逻辑值,yes逻辑值为TRUE时返回的值,no逻辑值为FALSE时返回的值
ifelse(i>0,"+","-")#如果i>0则输出+,否则输出-
x=rnorm(3)
ifelse(x>0,"+","-")
if(x>0{print("+")}else{print("-")})#报错
if只支持单个逻辑值,不支持逻辑值向量;ifelse既支持单个逻辑值,也支持多个逻辑值组合成为向量
非常重要的组合操作:
ifelse()+str_detect(),王炸,可以实现分组
library(stringr) samples = c("tumor1","tumor2","tumor3","normal1","normal2","normal3") k1 = str_detect(samples,"tumor");k1 #逻辑值向量 ifelse(k1,"tumor","normal") k2 = str_detect(samples,"normal");k2 #逻辑值向量 ifelse(k2,"normal","tumor")
如果有多个条件就逐一嵌套:
i = 0
if (i>0){
print('+')
} else if (i==0) {
print('0')
} else if (i< 0){
print('-')
}
ifelse(i>0,"+",ifelse(i<0,"-","0"))
10.2 循环语句
10.2.1 For循环
模板:for(i in x){code}
x <- c(5,6,0,3)
s=0
for (i in x){
s=s+i
print(c(i,s))
}
循环的保存:
s = 0
result = list()
for(i in 1:length(x)){
s=s+x[[i]]
result[[i]] = c(x[[i]],s)
}
result
do.call(cbind,result)#把result的结果按列拼接
do.call(rbind,result) #按行拼接
10.2.2 下标循环
x <- c(5,6,0,3)
s = 0
for (i in 1:length(x)){ #length(x)表示x的长度,i表示下标,1,2,3,4
s=s+x[[i]] #直接用两个中括号,没有理由
print(c(x[[i]],s))
}
函数separate()将数据的一列分割为多列。
生信数据常用一些分隔符,比如chr1:pos1-pos2
method <- data.frame(coord=c("chr8:11666485-11666694","chr12:123215010-123215553","chr20:36148133-36149750")) %>%
separate(col=coord, #要分割的列
into=c("chr","pos"), #分割成几列就写几列名字
sep=":") #列的分隔符是什么
method
分割前 分割后
注意:%>%必须有不然,会报错
函数unite()将数据的多列分割为一列
iris %>%
unite(col = "new col", #需要合成的列名
Petal.Width:Species, #需要合成的列
sep = "//") #分隔符符号
x="my name is jimmy"
str_length(x)
#str_length是计算字符串长度,包括空格,所以输出结果为16
length(x)
#输出结果为1,因为x在引号内作为一个值
str_split(x," ")
#输出的结果为一个列表格式,需要用取子集的方式将字符串单独保存成一个向量
x2=str_split(x," ")[[1]];x2
x2[2]
当需要将拆分的字符串组合成一个表格
y=c("jimmy 150","Tom 148","Tina 146")
str_split(y," ")
str_split(y," ",simplify = T) #组合形成矩阵
str_sub(x,4,8) #提取x中第4个到第8个字符
str_detect(x2,"y") #分别检测字符中是否含有y字母
#前面已经将x拆分成了多个字符串,并组合成为向量
str_starts(x2,"n")#以n开头
str_ends(x2,"e")#以e结尾
x2
str_replace(x2,"m","n") #只替换每一个字符串第一个检测到的字符
str_replace_all(x2,"m","n") #替换所有字符
x
str_remove(x," ")
str_remove_all(x," ")
13.1 清理工作空间
在启动任何新的分析项目时,首先清空工作空间,前提需要的内容保存成.Rdata
rm(list=ls())
rm(list=ls(all=T))
ls( )用于显示当前工作空间中的对象,all=T是为了清理包括隐藏对象在内的所有对象,默认值all=FALSE。
13.2 快速读取csv文件
read.csv在读取大型数据集时速度较慢,有时会报错,可以使用readr包中read_csv( )函数或者data.table包中的fread( )函数读取数据
13.3 剔除不需要的变量
bigdata <- as.data.frame(matrix(rnorm(5000*200),ncol=200))
varnames <- NULL
for (i in letters[1:20]) {
for (j in 1:10) {
varnames <- c(varnames,paste(i,j,sep = "_"))
}
}
names(bigdata) <- varnames
names(bigdata)
创建了一个200个变量,5000个观测对象的数据集。利用两个for循环和R内置的字母数据设置变量名称。
在正式分析之前需要将用不上的变量暂时剔除,减少内存负担。
剔除的方法:联合dplyr包中的select( )函数和tidyselect包中的starts_with( )、end_with( )、contains( )等函数。
选择以a开头的变量:
subdata1 <- select(bigdata,starts_with("a"))
选择以2结尾的变量:
subdatda2 <- select(bigdata,ends_with("2"))
多个筛选条件时,可以使用函数vars()包括多个条件,select_at()筛选
subdata3 <- select_at(bigdata,vars(starts_with("a"),starts_with("b")))
选择变量中包含1的变量
subdata4 <- select(bigdata,contains("1"))
subdata4 <- select_at(bigdata,vars(contains("1")))
剔除以a开头的变量,2结尾的变量,包含1的变量
subdata5 <- select(bigdata,-starts_with("a"))
subdata5 <- select(bigdata,-ends_with("2"))
subdata5 <- select_at(bigdata,vars(-starts_with("a"),-starts_with("b")))
subdata5 <- select_at(bigdata,vars(-contains("1")))
13.4 选取数据集的一个随机样本
对大型数据集的全部记录进行处理往往会降低分析效率,在编写代码时,可以只抽取一部分记录对程序进行测试,以便优化代码并消除bug。
sampledata1 <- sample_n(subdata5,size = 500)
sampledata2 <- sample_frac(subdata5,size = 0.02)
函数sample_n()和sample_frac( )都用于从数据框中随机选取指定数量的行,前者中参数size用于指定行的个数,后者中的参数size用于指定所占行的比例。比如第一个表示,选取其中500例样本,第二个则表示选择其中2%的样本。