分享

R学习:R for Data Science 循环-迭代 purrr 函数代替 for 循环

 公号生信小课堂 2021-10-28

R学习往期回顾:

R学习:R for Data Science 循环-迭代(for while))

R学习:R for Data Science 向量(1)

R学习:R for Data Science 向量(2)

R学习 从Tidyverse学起,入门R语言 dplyr合并数据

R学习 流程控制 if,else,ifelse

R学习 从Tidyverse学起,入门R语言(tidyr和stringr)

R学习 从Tidyverse学起,入门R语言(tibble,readr和dplyr)

R学习:字符串

R学习:环境和函数

R学习:数据框的基本操作

R学习:R for Data Science(五)

R学习:R for Data Science(四)

R学习:R for Data Science(三)

R学习:R for Data Science(二)

R学习:R for Data Science(一)

for循环与函数式编程

for 循环在 R 中不像在其他语言中那么重要,因为 R 是一门函数式编程语言。这意味着可以先将 for 循环包装在函数中,然后再调用这个函数,而不是直接使用 for 循环

library(tidyverse)df <- tibble( a = rnorm(10), b = rnorm(10), c = rnorm(10), d = rnorm(10))

假设想要计算每列的均值。你可以使用 for 循环来完成这个任务

output <- vector("double", length(df))for (i in seq_along(df)) { output[[i]] <- mean(df[[i]])}output

将这段代码提取出来,转换成一个函数:

col_mean <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- mean(df[[i]]) } output}

还可以算每列的中位数和标准差

col_median <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- median(df[[i]]) } output}col_sd <- function(df) { output <- vector("double", length(df)) for (i in seq_along(df)) { output[i] <- sd(df[[i]]) } output}

通过添加支持函数应用到每列的一个参数,我们可以使用同一个函数完成与 col_mean()col_median() col_sd() 函数相同的操作:

col_summary <- function(df, fun) { out <- vector("double", length(df)) for (i in seq_along(df)) { out[i] <- fun(df[[i]]) } out}col_summary(df, median)col_summary(df, mean)

将函数作为参数传入另一个函数的这种做法是一种非常强大的功能,它是促使 R 成为函数式编程语言的因素之一。
我们将学习和使用 purrr 包,它提供的函数可以替代很多常见的 for 循环应用。 R 基础包中的应用函数族(apply()lapply()tapply() 等)也可以完成类似的任务,但 purrr 包中的函数更一致,也更易于学习。

使用 purrr 函数代替 for 循环的目的是将常见的列表处理问题分解为独立的几个部分。
· 对于列表中的单个元素,你能找到解决问题的方法吗?如果找到了解决方法,那么你就可以使用 purrr 将这种方法扩展到列表中的所有元素。

· 如果你面临的是一个非常复杂的问题,那么如何将其分解为几个可行的子问题,然后循序渐进地解决,直至完成最终的解决方案?使用 purrr,你可以解决很多子问题,然后再通过管道操作将这些问题的结果组合起来。

映射函数

先对向量进行循环、然后对其每个元素进行一番处理,最后保存结果。这种模式太普遍了,因此 purrr 包提供了一个函数族来替你完成这种操作。每种类型的输出都有一个相应的函数:
· map() 用于输出列表;
· map_lgl() 用于输出逻辑型向量;
· map_int() 用于输出整型向量;
· map_dbl() 用于输出双精度型向量;
· map_chr() 用于输出字符型向量。

每个函数都使用一个向量作为输入,并对向量的每个元素应用一个函数,然后返回和输入向量同样长度(同样名称)的一个新向量。向量的类型由映射函数的后缀决定。

我们可以使用这些函数来执行与最后一个 for 循环相同的操作。因为那些摘要函数返回的是双精度数,所以我们需要使用 map_dbl() 函数:

map_dbl(df, median)map_dbl(df, mean)

map_*() col_summary() 具有以下几点区别

· 所有 purrr 函数都是用 C 实现的。这使得它们的速度非常快,但牺牲了一些可读性

· 第二个参数(即 .f,要应用的函数)可以是一个公式、一个字符向量或一个整型向量
· map_*() 使用.f 传递一些附加参数,供其在每次调用时使用

· 映射函数还可以保留名称

快捷方式

对于参数 .f,你可以使用几种快捷方式来减少输入量。假设你想对某个数据集中的每个分组都拟合一个线性模型。以下这个简单示例将 mtcars 数据集拆分成 3 个部分(按照气缸的值分类),并对每个部分拟合一个线性模型:

models <- mtcars %>% split(.$cyl) %>% map(function(df) lm(mpg ~ wt, data = df))

因为 R 中创建匿名函数的语法比较繁琐,所以 purrr 提供了一种更方便的快捷方式——单侧公式:

models <- mtcars %>% split(.$cyl) %>% map(~lm(mpg ~ wt, data = .))

我们在以上示例中使用了 . 作为一个代词:它表示当前列表元素(与 for 循环中用 i 表示当前索引是一样的)

需要提取出  R平方 这样的摘要统计量

需要先运行 summary() 函数,然后提取出结果中的 r.squared。我们可以使用匿名函数的快捷方式来完成这个操作:

models %>% map(summary) %>% map_dbl(~.$r.squared)

因为提取命名成分的这种操作非常普遍,所以 purrr 提供了一种更为简洁的快捷方式:使用字符串

models %>% map(summary) %>% map_dbl("r.squared")

后面需要有apply()家族函数的基础知识,为了循序渐进,后面我们将会介绍apply家族,下回见。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多