r语言for循环怎么把结果保留_R学习:R for Data Science 循环-迭代 purrr 函数代替 for 循环...

时间:2024-10-09 08:27:00

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 循环

  1. library(tidyverse)
  2. df <- tibble(
  3. a = rnorm(10),
  4. b = rnorm(10),
  5. c = rnorm(10),
  6. d = rnorm(10)
  7. )

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

  1. output <- vector("double", length(df))
  2. for (i in seq_along(df)) {
  3. output[[i]] <- mean(df[[i]])
  4. }
  5. output

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

  1. col_mean <- function(df) {
  2. output <- vector("double", length(df))
  3. for (i in seq_along(df)) {
  4. output[i] <- mean(df[[i]])
  5. }
  6. output
  7. }

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

  1. col_median <- function(df) {
  2. output <- vector("double", length(df))
  3. for (i in seq_along(df)) {
  4. output[i] <- median(df[[i]])
  5. }
  6. output
  7. }
  8. col_sd <- function(df) {
  9. output <- vector("double", length(df))
  10. for (i in seq_along(df)) {
  11. output[i] <- sd(df[[i]])
  12. }
  13. output
  14. }

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

  1. col_summary <- function(df, fun) {
  2. out <- vector("double", length(df))
  3. for (i in seq_along(df)) {
  4. out[i] <- fun(df[[i]])
  5. }
  6. out
  7. }
  8. col_summary(df, median)
  9. 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() 函数:

  1. map_dbl(df, median)
  2. map_dbl(df, mean)

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

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

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

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

快捷方式

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

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

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

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

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

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

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

  1. models %>%
  2. map(summary) %>%
  3. map_dbl(~.$)

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

  1. models %>%
  2. map(summary) %>%
  3. map_dbl("")

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

TCGA单基因免疫相关泛癌分析,懒人福音, 重磅来袭​

公众号“生信小课堂”

TCGA数据分析课程:生物信息学教学