R语言中性价比最高的函数以及最贵的函数
今天讲几个我喜欢的函数。
1.性价比最高的函数t()
中文名称转置,就是矩阵的行列转换
先创建一个矩阵
dd <- matrix(seq(1,18),nrow = 3)
colnames(dd) <- paste0("gene",LETTERS[1:6])
rownames(dd) <- paste0("sample",LETTERS[1:3])
dd
用t()转置一下,行列就互换了
t(dd)
就一个小小的字母t完成这么复杂的操作,在调整数据格式中必不可少,比如,大部分原始数据都是,行是基因,列是样本,而ggplot2绘图需要的是清洁数据,行是样本,列是基因,这个过程中许需要使用转置。
我曾经想,如果没有t()
, 我们该如何编程实现,以前我无能为力,但是自从我想通了R语言里面数据结构的本质上都是向量,我就想到了方法。
我认为,向量,矩阵,数据框,列表本质上都是一个东西,是挂在晾衣绳上的小盒子:
这样就好理解,对于data.frame而言,为什么names()和colnames()返回的结果一样,为什么获列的时候df[,1]和df[1]的结果也一样,还有,对于批量读入的数据如果存为列表,可以使用do.call(cbind,list)把列表变成数据框,因为本质上他们都是一个结构。
这样一想,连字符串都可以看作是向量:
如此,我先用lapply批量获取行,再用cbind
把他们按照列合并就可以了
myt <- function(dd){
tt <- do.call(cbind,lapply(1:nrow(dd), function(x){dd[x,]}))
colnames(tt) <- rownames(dd)
return(tt)
}
myt(dd)
既然sapply是lapply的简化版本,他默认返回的是向量,通常我们看到的是一维的向量,也可以是一列列组成的向量
sapply(1:nrow(dd), function(x){dd[x,]})
既然sapply可以,那么apply理论上也应该可以,只要我按照行去取出行,那么他会自动把得到的行按列合并,在R语言中"["也是函数,表示取出。
apply(dd,1,"[")
确实也实现了效果,目前开来apply的方案是除了t()之外书写最简单的。我们看看他们的速度呢?运行10万次比较一下
t()当真是性价比最高的函数,书写最简单,速度十分快!
2.最贵的函数是table()
table用来统计元素出现的次数,举例如下
从ABCD四个字母中可放回抽取20次,得到向量dd
set.seed(522)
dd <- sample(LETTERS[1:4],20,replace = T)
dd
使用table()
统计一下,返回每个字母出现的次数
为什么要说他最贵呢,情况是这样的,以前人们参加考试,比如托福,要背10000个以上的单词,但是在2003年李笑来先生分析历年的真题并制作词频后发现,去掉''a'',''the''等高频词,还有低频词,剩下的只有2100个单词,每天背诵100个,21天就可以背完,因此还写了一本畅销书,叫《TOEFL核心词汇21天突破》。
这本书的稿费支持了他大部分生活开销,应该超过100万,当年他用了9个月的时间来完成,今天我们用R语言导入,用jieba来分词,一个table()就可以搞定了。
这都是闲聊,我在机场的时候,也突发奇想,看看能不能写函数来实现,也不是很困难,先找到不重复的元素,然后计算他们的个数就可以
mytable <- function(x){
sapply(unique(x,fromLast = TRUE), function(x){sum(dd==x)})
}
mytable(dd)
可以顺利实现,现在我来比较一下速度
system.time(for (i in 1:100000) {
table(dd)
})
system.time(for (i in 1:100000) {
mytable(dd)
})
很惊喜,居然速度快三倍!
3.大道至简的函数是unique()
我在构建上一个table函数的时候,用到了unique函数,这个函数可以实现去重的功能。
set.seed(522)
dd <- sample(LETTERS[1:4],20,replace = T)
dd
unique(dd)
因为在机场没事,就想着如何自己来实现去重的功能。
我是这样想的
先找到第一个元素,记录,然后去掉向量里所有这个元素,剩下的向量,再找到第一个元素,记录,去除这个元素,以此类推就实现了。
无论是一个for循环还是函数,首先自己要能够清晰地定义每一步该做什么,本次中以此类推
让我想到要使用递归功能,经过一番思考,我总结了递归函数的写法:
1.要用if语句来判断
2.要定义最极端的情况,比如最够一个元素带入函数的结果
3.尝试保留每一次迭代的结果,每次都用定义的函数
myunique <- function(dd){
if(length(dd)==0){
return(NULL)
}else{
return(c(dd[1],myunique(dd[dd !=dd[1]])))
}
}
测试一下效果也是不错
再测试一下速度
system.time(for (i in 1:100000) {
unique(dd)
})
## 比较时间
system.time(for (i in 1:100000) {
myunique(dd)
})
还是速度赶不上,好在我现在会写递归函数了,比如,算一个数的阶乘。
jiecheng <- function(x){
if(x==0){
return(1)
}else{
return(x*jiecheng(x-1))
}
}
测试一下效果:
实际上R语言里面有专门的函数实现这个功能是`factorial
没有比较速度,因为自带函数速度快到无边无界。