上一篇笔记记录了三个专题:数据框排序,表达矩阵画箱线图,数据框各种连接方式的内容与示例。
以下记录几个专题的内容与示例:
玩转字符串
条件和循环
隐式循环(apply与lapply)
专题1:玩转字符串
stringr包里专用于处理字符串向量的函数,这些函数有一个共同特点,str开头。
1.1 检测字符串长度:str_length(x)
1.2 字符串拆分:str_split()
1.3 按位置提取字符串:str_sub()
1.4 字符检测:str_detect(x,"h"):重点掌握用法
1.5 字符串替换:str_replace()/str_replace_all()
1.6 字符删除:str_remove()/str_remove_all()
基础包里也有处理字符串函数,但是比较零散。
rm(list = ls())
if(!require(stringr))install.packages('stringr')
library(stringr)
1.1 检测字符串长度
字符串:一个引号里面所有的东西,不管有多少个字母,包括符号,空格。
字符:一个字母就是一个字符,空格也是一个字符。
x <- "The birch canoe slid on the smooth planks."
x
str_length(x)
##统计向量里的字符个数,数一下字符串里面有多少个字符。
length(x)
##统计向量里的元素个数,运行完上面的代码,返回是1个元素,1代表一个”引号“括起来的字符串,不管引号里边有多少字母与空格之类的,有多少个引号就有多少个字符串,
1.2 字符串拆分
按照某个符号(字符)进行分割。拆分向量字符串后成列表,加参数simplify = T
(这个参数以后用得比较多)成为矩阵,矩阵转换数据框,字符型转换为数值型。
如果是一个长字符串,就取[[1]],如果是多个元素组成的向量,就加一个参数simplify = T
x <- "The birch canoe slid on the smooth planks."
str_split(x," ")
##按照空格去分割,按照空格去把x的字符串拆分成若干个字符串,有多少个空格就拆多少个,有7个空格,拆成8个。
# [[1]]
# [1] "The" "birch" "canoe" "slid" "on" "the"
# [7] "smooth" "planks."
### [[1]]列表才有的,用class()函数查看是不是列表
class(str_split(x," "))
#[1] "list"
##就一个向量没必要保持列表的属性。
x2 = str_split(x," ")[[1]];x2
##[[1]]提取列表元素里唯一一个元素。
class(x2)
#[1] "character"
####为什么str_split拆分向量会成列表,因为不是只拆一个字符串的,可以拆一个字符串向量。
y = c("jimmy 150","nicker 140","tony 152")
str_split(y," ")
##" ",以空格为单位拆分,都会生成列表,考虑到维度
##每次拆为一个列表,需要简化,不能简化为数据框,只能简化为矩阵,可以用as.data.frame转化为数据框
str_split(y," ",simplify = T)
##simplify = T为简化为字符型的矩阵,转化为矩阵后,用$符号或是[]对数据框的某一列操作,用as.numeric把字符串转换为数值型。
str_c(x2,collapse = " ")
##与paste0有点类似。" "为分割符把向量连接,内部的连接
str_c(x2,1234,sep = "+")
##外部的连接,给每个元素加1234
1.3 按位置提取字符串
x <- "The birch canoe slid on the smooth planks."
str_sub(x,5,9)
#[1] "birch"
#把字符串的第5到第9位抠出来,单独组成一个字符串
##自己思考:如果一个向量里有很多个字符串怎么提取,除了下标,还有其它方法
1.4 字符检测(重点)
检测字符串或是字符串向量里是否含有某个关键词(一个字母或是单词都可以),返回一个与向量等长的逻辑值(TRUE或FALSE)。
x <- "The birch canoe slid on the smooth planks."
x2 = str_split(x," ")[[1]];x2
str_detect(x2,"h")
#[1] TRUE TRUE FALSE FALSE FALSE TRUE TRUE FALSE
#检测x2里每个元素(向量)里是否含有h这个字母,含有就返回TRUE,不含就返回FALSE。
##比较细化的两个函数
str_starts(x2,"T")
#[1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
##是否以T开头,返回逻辑值,是就返TRUE,否就返FALSE
str_ends(x2,"e")
#[1] TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE
##是否以e结尾,返回逻辑值,是就返TRUE,否就返FALSE
1.5 字符串替换
替换单个和替换全部
x <- "The birch canoe slid on the smooth planks."
x2 = str_split(x," ")[[1]];x2
##替换一个字符。
str_replace(x2,"o","A")
#[1] "The" "birch" "canAe" "slid" "An" "the"
#[7] "smAoth" "planks."
##把字母A替换字母o,但是不全替换,比如两个oo字母连在一起,只替换第一个匹配字符,如smAoth还有第二个o
##如果连在一起的两个字母,只能替换第一个。
##替换所有包含的字符。
str_replace_all(x2,"o","A")
#[1] "The" "birch" "canAe" "slid" "An" "the"
#[7] "smAAth" "planks."
#匹配上所有都要替换掉
##替换单个还是全部和应用场景有关。
1.6 字符删除
删除单个字符或是删除多个字符
x <- "The birch canoe slid on the smooth planks."
str_remove(x," ")
#[1] "Thebirch canoe slid on the smooth planks."
##去掉空格,只删除第一个空格
str_remove_all(x," ")
#[1] "Thebirchcanoeslidonthesmoothplanks."
##删除所有的空格
1.7 字符定位
x <- "The birch canoe slid on the smooth planks."
x2 = str_split(x," ")[[1]];x2
str_locate(x2,"th")
str_locate(x2,"h")
与sum和mean连用,可以统计匹配的个数和比例
x <- "The birch canoe slid on the smooth planks."
x2 = str_split(x," ")[[1]];x2
sum(str_detect(x2,"h"))
#[1] 4
mean(str_detect(x2,"h"))
#[1] 0.5
1.8 提取匹配到的字符
提取单个或是提取多个匹配到的字符串,使用str_extract_all()时,使用参数simplify = T
x <- "The birch canoe slid on the smooth planks."
x2 = str_split(x," ")[[1]];x2
str_extract(x2,"o|e")
#[1] "e" NA "o" NA "o" "e" "o" NA
#提取o或e,默认只提取第一个,不含有就返回NA
str_extract_all(x2,"o|e")
##返回是一个列表
str_extract_all(x2,"o|e",simplify = T)
#simplify = T,使其返回不是列表,而是简化的矩阵
注意:在对字符串函数操作时,记得检查
专题2:条件语句和循环语句
条件语句常用到,循环语句不常用,后面也可多练习。
2.1条件语句
if条件语句:如果...就...
2.1.1 if(){ }
if(){
CODE1
}
##if()括号里面只能是一个逻辑值,不能是一串逻辑值,因为不支持循环。
##如果符合,逻辑值为TRUE,就执行代码;如果不符合,逻辑值为FALSE,就不执行代码,什么都不做。
- (1)只有if,没有else,那么条件是FALSE时就什么都不做
i = -1
if (i<0) print('up')
#[1] "up"
if (i>0) print('up')
##什么都不返回,代码:print('up')
##只有一句代码,可以把{}省略掉
理解下面代码
if(!require(tidyr)) install.packages('tidyr')
##如果这个tidyr包成功不加载,返回TRUE,就安装tidyr,如果加载成功,返回FALSE就不要安装。注意有!,反着来。
- (2) if加上else
if条件语句:如果...就...,否则...
if(){
CODE1
}else{
CODE2
}
##if()括号里面只能是一个逻辑值,如果是TRUE就是执行CODE1,如果是FALSE就是执行else后CODE2
结合else
i =1
if (i>0){
print('+')
} else {
print("-")
}
#[1] "+"
if和if..else函数用的不多。ifelse函数用的比较多
2.1.2 ifelse函数(重点掌握,常用函数)
这个函数是支持向量的,if()的括号里只能是一个逻辑值,但是ifelse()的括号里的逻辑值数量没有限制。
ifelse函数永远只有3个参数:ifelse(x, yes, no),无论怎么嵌套永远只有3个参数,除非作者修改。
x:逻辑值/逻辑值向量的代码
yes:逻辑值为TRUE时的返回值
no:逻辑值为FALSE时的返回值
i=1
ifelse(i>0,"+","-")
##i>0为第一个参数x,一定是逻辑值,一个逻辑值或是逻辑值向量
##"+"为第二个参数yes,x对应的逻辑值为TRUE,这句代码返回输入值是什么。当x对应位置的逻辑值是TRUE的时候,ifelse整句代码输出结果是"+"
##"-"为第三个参数no,当i>0为FALSE时,返回值是"-"
x=rnorm(3)
#[1] 0.9020748 1.4268635 -0.1594190
ifelse(x>0,"+","-")
#[1] "+" "+" "-"
##ifelse()相当于完成一个替换,把一个逻辑值向量里的TRUE换成一个"+",把一个逻辑值向量里的FALSE换成一个"-",以此来输出一个新的向量。
这里不是只x>0,逻辑值或是逻辑值向量,也可以是用代码生成的逻辑值向量,ifelse()只管替换。
生成逻辑值向量的函数:str_detect()
x <- c("The", "birch", "canoe", "slid","on","the", "smooth planks.")
str_detect(x,"h")
#[1] TRUE TRUE FALSE FALSE FALSE TRUE TRUE
#返回逻辑值向量
ifelse(str_detect(x,"h"),"+","-")
##[1] "+" "+" "-" "-" "-" "+" "+"
##如果x每个元素里含有关键词h,输出为+,如果不含h,输出为-
ifelse(str_detect(x,"h"),"control","treat")
#[1] "control" "control" "treat" "treat" "treat" "control"
#[7] "control"
##ifelse(str_detect(x,"h"),"+","-")里的"h"位置除了h可写单词或者是几个字母;"+","-"的位置可以写任何关键词。
ifelse与str_detect两个函数连用实现一个强大的功能:
根据x这个向量是或否含有某个关键词来把它分成两类,一类是含有关键词(TRUE)的叫A,一类是不含关键词(FALSE)的叫B,A和B的名称可以随便替换。
多个条件
- if后面可以跟多几个else if。if()的括号里只能有一个逻辑值,不能是向量和前面一样。
i = 0
if (i>0){
print('+')
} else if (i==0) {
print('0')
} else if (i< 0){
print('-')
}
#[1] "0"
ifelse(i>0,"+",ifelse(i<0,"-","0"))
#[1] "0"
##分三种情况,i>0,i==0,i< 0分别输出+,0,-。
- ifelse()函数也支持多个条件
如果需要向量化的类似操作,里面有3个或是多个数字,可以用ifelse函数,它能支持多个条件。
i=(-1,0,2)
ifelse(i>0,"+",ifelse((i<0),"-","0"))
##如果i>o,输入+,否则..再进行一轮循环比较,如果i< 0输出-,否则输出
这个方法以后在实战中非常常用,结合练习题代码巩固
2.2循环语句
2.2.1 for循环
重复运算,对x里的每个元素i进行同一操作。#x通常是向量和列表,数据框有更好的循环
for(i in x){
CODE
}
##x是一个向量,i是一个抽象的东西,指代x里面的一个元素,第一轮循环就会带入x的第一个元素,第二轮循环时就会带入x的第二个元素。CODE代码是以i为中心去写的。for循环终究有终点,因为向量都是有长度的,运行x的最后一个元素会终止。
两种循环方式:
##第一种循环方式:元素循环
x <- c(5,6,0,3)
s=0
for (i in x){
s=s+i
print(c(i,s))
}
# [1] 5 5
# [1] 6 11
# [1] 0 11
# [1] 3 14
##每轮循环进行累加:没运行前,s=0,第一个元素5运行完后,s=5,参与下一个元素的运行以此类推,上一个元素得到的新s结果会参与到下一个元素里。
##for (i in x):直接拿x里的四个元素进行循环,即第一轮循环i=5..第四轮循环i=3
##第二种循环方式:下标循环
x <- c(5,6,0,3)
s = 0
for (i in 1:length(x)){
s=s+x[[i]]
print(c(x[[i]],s))
}
# [1] 5 5
# [1] 6 11
# [1] 0 11
# [1] 3 14
##s=s+x[[i]]:使用[[]],大神给的建议是,当你需要把向量取子集的代码写在{}里面,有时候用一个[]取子集,结果比较诡异,用[[]]取子集,一般没什么问题。用[[]]相当于加上一道保险杠,写在for保险操作,仅在循环这样操作,下标循环比较灵活。
##下标循环,常用,不限于向量或是列表,只要能取子集的数据结构
##for (i in 1:length(x)),拿向量的下标进行循环,第一轮循环=1..第四轮循环i=4
总结:两种循环都有应用,元素循环比较简单,但应用更广的是下标循环,因为它比较灵活,下标可以替换成数据框的列,也可以替换成列表里的指定元素。这样就可以实现对数据框,列表比较便的替换。下标循环还有一个不可替代的好处,就是能把结果保存下来。
如何将结果存下来?
#补充知识:列表的第二种写法(生成方式):
a = list()
a[[1]]=1:10
a[[2]]=iris
...##可以一直往后添加列表的元素。
x <- c(5,6,0,3)
s = 0
result = list()
for(i in 1:length(x)){
s=s+x[[i]]
result[[i]] = c(x[[i]],s)
}
result
##每过一轮循环,列表就会多一个元素
#补充2:cbind()是按列拼接
cbind(result[[1]],
result[[2]],
result[[3]],
result[[4]])
do.call(cbind,result)
# [,1] [,2] [,3] [,4]
# [1,] 5 6 0 3
# [2,] 5 11 11 14
##得到的列表结果看起来比较麻烦,简化
##do.call()是批量对列表进行操作,把列表里的每个元素做为生成出矩阵的每一列
2.3插播:长脚本管理方式
假如有500行代码,不做拆分也不做任何操作,会变得非常臃肿,得运行完才出结果知道是对还是错,没有办法一段段去调试。有两种方法:
2.3.1 代码分成多个脚本:保存为Rdata
每个脚本最后保存Rdata,下一个脚本开头清空再加载
代码是用来处理数据,画图,保存数据或是读取之类的。总是围绕图片和数据,图片可以保存,数据可以用Rdata保存
#数据下载
rm(list = ls())
options(stringsAsFactors = F)
library(GEOquery)
gse_number = "GSE56649"
eSet <- getGEO(gse_number,
destdir = '.',
getGPL = F)
class(eSet)
length(eSet)
eSet = eSet[[1]]
#(1)提取表达矩阵exp
exp <- exprs(eSet)
exp[1:4,1:4]
exp = log2(exp+1)
boxplot(exp)
#(2)提取临床信息
pd <- pData(eSet)
#(3)调整pd的行名顺序与exp列名完全一致
p = identical(rownames(pd),colnames(exp));p
if(!p) exp = exp[,match(rownames(pd),colnames(exp))]
#(4)提取芯片平台编号
gpl_number <- eSet@annotation
save(gse_number,pd,exp,gpl_number,file = "step1output.Rdata")
##把gse_number,pd,exp,gpl_number这是三个数据打包保存到step1output.Rdata里,以后直接可以使用保存的数据,不需要运行上面的代码。
##使用的时候直接load保存的step1output.Rdata。
rm(list = ls())
load(file="step1output.Rdata")
##.Rdata"放在当前工作目录下
不断清空再调试,清空是不要让上一步残留的结果影响调试
2.3.2 if结合逻辑值
if(F){…},则{}里的脚本被跳过(不被执行),
if(T){…},则{}里的脚本被运行,
凡是带有{}的代码,均可以被折叠。
#第一段代码
if(F){
a=1
b=a^2
d=a+b+a^2
}
#第二段代码
if(T){
a=1
b=a^2
d=a+b+a^2
}
有段代码不行运行也不想删掉,有两个选择,一是在每行代码前加#号注释,二是把不想运行的代码放在if(F){}的大括号里,想运行时把F改为T,用if的一个好地方,凡是有{}地方都可以折叠。在R语言控制台的边框数字编号处点击三角箭头,可实现长串代码折叠。
#months<-c(1,3,5,9,12)
#scores <- c(5,6,5,6,7)
#speaking <- c("good","fine","good","fine","excellent")
#resultdata <- data.frame(months,scores,speaking)
##在每行代码前面加#号,表示注释不运行。
if(F){rownames(resultdata) <- c("once","twice","third","fourth","fifth")
colnames(resultdata) <- c("times","pionts","oraltest")
}
##if(F)不运行{}里面的代码,注意{}里面的完整的两句代码不需要逗号
专题3:隐式循环
不是用for语句而是用函数来实现循环,比for循环应该广一些。
apply:处理数据框和矩阵;
lapply:处理列表和向量。
3.1 矩阵/数据框的隐式循环---apply
apply 处理矩阵或数据框
apply(X, MARGIN, FUN, …) ,apply()函数有三个参数
X是数据框/矩阵名:对数据框或是矩阵进行循环运算的一个函数);
MARGIN的位置为1表示行,为2表示列:只能单独对所有对行或是对所有列运算,不能同时对行和列一起运算);
FUN是函数:函数一般带括号,但是FUN作为一个函数里的参数时,不需要带括号,在do.call(cbind,result)里,cbind函数作为do.call函数里的参数,也不带括号。
对X的每一行/列进行FUN这个函数
test<- iris[1:6,1:4]
class(test)
#[1] "data.frame"
apply(test, 2, mean)
# Sepal.Length Sepal.Width Petal.Length Petal.Width
# 4.9500000 3.3833333 1.4500000 0.2333333
##第一行向量的名字,用数据结构把两种信息组合起来。
##test为数据框名,2表示每一列,FUN为mean函数,整句代码的意思是对数据框test的每一列求平均值,
apply(test, 1, sum)
# 1 2 3 4 5 6
# 10.2 9.5 9.4 9.4 10.2 11.4
#对数据框test每一行求和
mean和sum是现成的函数,比较简单的例子,apply函数真正强大之处是结合自定义函数。
如何挑出一个表达矩阵里方差最大的1000个基因?
方法:
1.计算每个基因的方差(每个基因是每一行,方差var)
2.每个基因方差排列
3.最后1000个数字所对应的基因
具体操作代码:
load("test2.Rdata")
##里面保存的数据框名字是test
dim(test)
apply(test, 1, var)
##计算数据框test方差
sort(apply(test, 1, var))
##从小到大排序,最大的1000应在最后面
tail(sort(apply(test, 1, var)),1000)
##用tail函数取最后1000个方差最大的基因,默认参数是6不写,这里是1000,写上
names(tail(sort(apply(test, 1, var)),1000))
#提取基因名字,挑出1000个基因名字
3.2 lapply(list, FUN, …)
3.2.1列表的隐式循环—lapply
对列表/向量中的每个元素(向量)实施相同的操作(列表没有行列,对列表每个元素操作),所以lapply比apply简单。
lapply函数返回结果是一个列表
test <- list(x = 36:33,y = 32:35,z = 30:27);test
返回值是列表,对列表中的每个元素(向量)求均值(试试方差var,分位数quantile)
lapply(test,mean)
##列表没有行和列,对列表里每个元素批量操作。
##对列表里每个元素进行求平均值,结果以列表的形式展示
##返回是有多个向量组成的列表
lapply(test,fivenum)
#返回是多个数字组成的向量
#fivenum 五个数值:最小值、下四分位数数、中位数、上四分位数、最大值
3.2.2列表的隐式循环—lapply
lapply函数返回结果是一个列表,需要简化。sapply 简化结果,直接返回矩阵或向量
test <- list(x = 36:33,y = 32:35,z = 30:27);test
sapply(test,mean)
##输出结果为一个有名字的向量,合理简化
sapply(test,fivenum)
##对于复杂的数据结构,简化成矩阵
class(sapply(test,fivenum))
4.补充知识点
4.1 花样缺失值
处理NA
library(tidyr)
### 原始数据
X<-data.frame(X1 = LETTERS[1:5],X2 = 1:5)
X[2,2] <- NA
X[4,1] <- NA
X
### 1.去掉含有NA的行,可以选择只根据某一列来去除
drop_na(X)##去掉含有NA的函数
drop_na(X,X1)##只看X1这一行,有缺失值就去掉,没有就留下
drop_na(X,X2)##只看X2这一行,有缺失值就去掉,没有就留下
### 2.替换NA
replace_na(X,list(X2=0))
### 3.用上一行的值填充NA
X
fill(X,X2)##把X上的X2行的NA值填充上一行是几,就替换成几。临床医生比较受用,比如三位病人占到一行
在实战中画图时特别是画火山图,pca图时,有NA值的存在,没法画图。用基础包的函数,可以判断向量、矩阵、数据框等数据是否有缺失值,有的话,再用函数去除。
a<-data.frame(X1 = LETTERS[1:5],X2 = 1:5)
a[2,2] <- NA
a[4,1] <- NA
a
#is.na(a)##如果表达矩阵或是数据框的函数有几万行,这个函数不能单用
table(is.na(a))
#FALSE TRUE
# 8 2
b=na.omit(a)
##na.omit()是去除NA值的函数
table(is.na(b))
#FALSE
# 6
4.2 R语言遍历、创建、删除文件
写代码代替点鼠标
dir()
file.create()
file.exists()
file.remove()
file.rename(from, to)
file.append(file1,file2)
file.copy(from,to,overwrite=recursive,recursive=FALSE,
copy.mode = TRUE, copy.date = FALSE)
file.symlink(from, to)
file.link(from, to)
dir.create("ashu")
unlink("ashu",recursive = T)
摘抄小洁老师课件的内容,以后有时间再慢慢探索应用。
说明
以上内容是听生信技能树小洁老师的R语言线上课,根据自己的理解记录下来,小洁老师授课非常细心,对不同水平的同学都照顾到,并且补充很多技巧以及注意事项。