美文网首页生物信息分析:从入门到精通
每一个R语言初学者都应该掌握for循环

每一个R语言初学者都应该掌握for循环

作者: 9d760c7ce737 | 来源:发表于2019-05-06 23:52 被阅读192次

    今天群里有人提问:

    如何对数据框进行多变量分组,分别求平均数?

    给出的原始数据是这个样子的

    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循环的秘诀只有一个:清晰地定义做一件事情的每一个步骤。

    如果你需要练习的那个小文件,回复"我爱循环"自助获取。

    相关文章

      网友评论

        本文标题:每一个R语言初学者都应该掌握for循环

        本文链接:https://www.haomeiwen.com/subject/ntwjoqtx.html