每一个R语言初学者都应该掌握for循环!
今天群里有人提问:
如何对数据框进行多变量分组,分别求平均数?
给出的原始数据是这个样子的
data <- data.table::fread("Temperature.txt",data.table = F)
年代是从1990到2005
月份是从1月到12月
要求按照Year和Month对行进行分组,求得每组的平均Temperature。
这个事情,第一反应就是dplyr包中的group_by
联合summarize
:
library(dplyr)
results1 <- data %>%
group_by(Year,Month) %>%
summarise(Mean = mean(Temperature,na.rm = T))
十分方便,最终的结果是这个样子的
因为群里有人说,常用的实现这个目的的方法至少10种,而我脑子里面只有两种,所以内心十分恐慌。并且,他们还要求使用apply来完成,我彻底沦陷了。
想来想去,Hadley Wickham大神的这个包是有毒的,一用上之后便不思进取,因为实在太好用了。在神包tidyverse之前,作者还有一种方法可以实现这个操作,就是plyr包的split-apply-combine思想
方法二
用起来也是十分简单,而且,思路和函数组成都很类似,因为是一个作者写的。
library(plyr)
results2 <- ddply(data,.(Year,Month),
summarise,
Mean = mean(Temperature,na.rm = T))
方法三
做这个事情的本质是批量,那么map
函数配合bind_rows
函数肯定是可以的,但是过程很波折
library(purrr)
library(dplyr)
results3 <- data %>%
select(Year,Month,Temperature) %>%
split(.$Year) %>%
map(function(x){
data.frame(t(bind_cols(map(split(x,x$Month),function(y){
mean =mean(y$Temperature,na.rm = T)
c(y$Year[1],y$Month[1],mean)
}))))
}) %>%
bind_rows()
colnames(results3) <- c("Year","Month","Mean")
方法四
相比较而言,lapply
函数配合do.call
函数就简单一点
library(dplyr)
## 先切割
results4 <- data %>%
select(Year,Month,Temperature) %>%
split(.$Year)
##lapply批量操作,操作中内嵌一个lapply
results4 <- lapply(results4, function(x){
do.call(rbind,lapply(split(x,x$Month),function(y){
mean =mean(y$Temperature,na.rm = T)
c(y$Year[1],y$Month[1],mean)}))
})
## 结果变成数据框
results4 <- data.frame(do.call(rbind,results4))
## 命名
colnames(results4) <- c("Year","Month","Mean")
方法五
在很长一段时间内,只要是批量的事情,我都是用for循环来搞定的。它解决了我大部分问题,所以,我上课的时候强调,每一个人都要学会写for循环。
一般当你小有所成,就会出现鄙视链,开始瞧不起那些用for循环的初学者,并嘲笑别人说,for循环速度太慢了!但是,对于初学者而言,解决问题才是最重要的,速度可以放在一边,况且,速度并没有那么重要。
可以看看以前写过的这个帖子,for循环50s,并行化8s。我觉得没什么差别。
8秒完成2万个基因的生存分析,人人都可以!
至于用lapply还有map这些批量函数,我纯粹是为了要面子。还有就是用lapply是一种自然而然水到渠成的行为,当你能够随意写出for循环后,你就可以把它改写成函数,一旦写成函数,apply家族的成员就可以批量对其操作。
写好for循环的关键只有一个:
清晰地定义做一件事情的每一个步骤。
具体到当前的情况,我的想法是这样的
1. 先建立一个空的数据框,他有三列,我准备逐行填满
2. 第1行的第1列,我填入的是一个年份,比如1999
3. 确定年份后,在1999年中,我再开始选择第1个月,把这个数值填入第1行第2列
4. 当确定了年份,月份,那么就会产生一个小数据框,我就可以对温度这一列求平均值,填入第1行的第3列。
5. 第一行完成后,下面的行依葫芦画瓢。
基于这个思想,我写出了以下的循环:
results5 <- data.frame()
i=1
for (Year in unique(data$Year)) {
for (Month in unique(data$Month)) {
# 第一列
results5[i,1]= Year
# 第二列
results5[i,2]= Month
# 第三列
results5[i,3]= mean(data[(data$Year==Year & data$Month==Month),"Temperature"],na.rm=T)
i=i+1
}
}
colnames(results5) <- c("Year","Month","Mean")
最终这5个方法的结果一样
但是,我依然不知道其他5种方法是什么。
总结一下:
1.group_by联合summarize是批量分组计算的首选
2.只要是需要批量做的事情,都可以用for循环来完成
3.写好for循环的秘诀只有一个:清晰地定义做一件事情的每一个步骤。
如果你需要练习的那个小文件,回复"我爱循环"自助获取。