查看原文
其他

R语言中性价比最高的函数以及最贵的函数

果子 果子学生信 2023-06-15

今天讲几个我喜欢的函数。

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


没有比较速度,因为自带函数速度快到无边无界。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存