【问题标题】:How to reproduce a loop using a function in R package purrr如何使用 R 包 purrr 中的函数重现循环
【发布时间】:2019-11-07 17:36:17
【问题描述】:

我经常在我的代码中使用循环。有人告诉我,我应该使用函数,而不是使用循环,并且可以使用 R 包 purr 中的函数重写循环。

作为示例,代码仅显示了鸢尾花数据集中不同物种的计数,其中 Sepal.Width

 library(dplyr)
 #dataframe to put the output in
 sepaltable <- data.frame(Species=character(),
                     Total=numeric(), 
                     stringsAsFactors=FALSE) 

 #list of species to iterate over
 specieslist<-unique(iris$Species)

 #loop to populate the dataframe with the name of the species 
 #and the count of how many there were in the iris dataset

 for (i in  seq_along (specieslist)){
 a<-paste(specieslist[i])  
 b<- filter(iris,`Species`==a & Sepal.Width <=3)
 c<-nrow(b)
 sepaltable[i,"Species"]<-a
 sepaltable[i,"Total"]<-c
 }

循环使用每个物种的名称以及鸢尾花数据集中有多少物种来填充可分离的数据框。我想在不使用循环的情况下使用 R 包 purrr 中的函数来重现此循环的效果。有人可以帮忙吗?

【问题讨论】:

    标签: r purrr


    【解决方案1】:

    我们可以使用dplyr中的逻辑表达式sum分组

    library(dplyr)
    iris %>% 
       group_by(Species) %>%
       summarise(Total = sum(Sepal.Width <=3))
    

    或者如果需要purrr

    library(purrr)
    map_dfr(specieslist,  ~iris %>% 
          summarise(Total = sum(Species == .x & Sepal.Width <=3),
              Species = .x )) %>%
       select(Species, Total)
    

    注意:mapapply 系列函数 (lapply/sapply/vapply/rapply/mapply/Map/apply) 都是循环

    【讨论】:

    • 同意 akrun - 没有理由在这里实现 purrr。
    【解决方案2】:

    对于您提供的类型示例,akrun 的回答是最直接的方法,特别是因为您已经在使用 dplyr。 dplyr 包用于处理基本数据表摘要,尤其是示例中使用的组统计信息。
    但是,对于大多数情况下编写循环的更复杂的情况,您可以使用函数和 apply 系列来完成同样的事情。

    使用您的示例:

    # write function that does the stuff you put in your loop
    summSpecies <- function(a) {
          b<- filter(iris,`Species`==a & Sepal.Width <=3)
          c<-nrow(b)
          return(c)
    }
    
    # apply the loop over your list
    sapply(specieslist,summSpecies) #sapply simplifies the output to return a vector (in this case)
    #[1]  8 42 33
    
    # You can build this into a data frame
    sepaltable <- data.frame(Species=specieslist,
                             Total=sapply(specieslist,summSpecies), 
                             stringsAsFactors=FALSE) 
    sepaltable
    #      Species Total
    # 1     setosa     8
    # 2 versicolor    42
    # 3  virginica    33
    

    我对示例中提出的方法进行了比较:

    Unit: microseconds
    #            expr      min        lq     mean   median        uq       max neval
    #      ForLoop.OP 2548.519 2725.9020 3107.153 2819.837 3006.5915 11654.194   100
    #     Apply.Brian 2385.638 2534.2390 2810.854 2625.050 2822.5145  9641.172   100
    #     dplyr.akrun 721.136  837.6065 1180.244  864.604  902.9815 13440.076   100
    #     purrr.akrun 3572.656 3783.2845 4147.900 3874.095 4073.5690 10517.602   100
    #    purrr.Axeman 2440.973 2527.322 2866.7686 2586.8960 2774.097  9577.360   100
    

    为此类任务优化的现有功能显然是赢家,这不足为奇。 for 循环方法落后于 apply 系列方法。

    【讨论】:

    • 如果你想使用purrr,请将sapply替换为map_int
    • 在循环示例中,首先生成由循环填充的空数据帧。是否可以对函数做同样的事情,因为函数填充空数据帧,而不是必须在最后将结果合并为向量?
    • @Basil 确定您可以先生成空数据框,然后使用例如 sepaltable$Total&lt;- sapply(specieslist,summSpecies) 填充它,无论 sepaltable 是否有一个名为 Total 的列,这都可以。您还可以使用数据框的一列作为输入(即sepaltable$Total&lt;- sapply(sepaltable$Species,summSpecies)
    猜你喜欢
    • 2021-02-28
    • 2021-09-16
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 2018-05-31
    • 2020-02-08
    • 2021-10-10
    • 2015-07-09
    相关资源
    最近更新 更多