一、背景

多数情况下,我们经常在R语言编程中,使用循环处理各种数据,已达到必要的结果。在R语言中,写循环的修仙道路:手动for循环—apply函数族—purr包的泛函数式编程。

关于purrr 与 apply 族:

  • purrr 提供了更多的一致性、规范性和便利性,更容易记住和使用。
  • 速度来说,apply 族稍微快可以忽略不计的一丢丢。

基于 purrr 包的泛函式循环迭代的核心思想及常用操作:

  1. 循环迭代,就是将一个函数依次应用(映射)到序列的每一个元素上。
  2. 常用操作:
    • map():依次应用一元函数到一个序列的每个元素上,基本等同 lapply()
    • map2():依次应用二元函数到两个序列的每对元素上
    • pmap():应用多元函数到多个序列的每组元素上,可以实现对数据框逐行迭代
  • map 系列默认返回列表型
  • 可根据想要的返回类型添加后缀:_int, _dbl, _lgl, _chr, _df
  • 可以接着对返回的数据框df做行/列合并:_dfr, _dfc
  • 如果只想要函数依次作用的过程,而不需要返回结果,改用 walk 系列即可
  • 所应用的函数,有 purrr公式风格简写(匿名函数),支持一元,二元,多元函数
  1. map_* 系列函数

    • map_chr(.x, .f): 返回字符型向量
    • map_lgl(.x, .f): 返回逻辑型向量
    • map_dbl(.x, .f): 返回实数型向量
    • map_int(.x, .f): 返回整数型向量
    • map_dfr(.x, .f): 返回数据框列表,再 bind_rows 按行合并为一个数据框
    • map_dfc(.x, .f): 返回数据框列表,再 bind_cols 按列合并为一个数据框

二、基本概念

(1)序列:可根据位置或名字进行索引的数据结构

包括:

  • 原子向量:各个值都是同类型的,包括 6 种类型:logical、integer、double、character、complex、raw,其中 integer 和 double 也统称为numeric
  • 列表:各个值是不同类型的
  • 数据框:每一列的数据类型必须相同

所谓循环迭代,就是依次在序列上做相同的操作。

(2) 泛函式编程:函数的函数称为泛函,在编程中表示函数作用在函数上,或者说函数包含其它函数作为参数。

  • 循环迭代,本质上就是将一个函数依次应用(映射)到序列的每一个元素上。如泛函式:map(x, f)

(3) 管道:管道可以将数据从一个函数传给另一个函数,形成若干函数构成的管道式数据流,依次变换数据。

例如:

x %>% f() %>% g()
# 等同于 g(f(x))
# 解读:依次对数据进行若干操作:先对 x 进行 f 操作, 接着对结果进行 g 操作

注意:

  • 使用管道的好处是:提高程序可读性,避免引入不必要的中间变量。
  • 数据经过管道默认传递给函数的第一个参数(表现为省略);若在非第一个参数处使用该数据,用 “.” 代替,这使得管道作用更加强大和灵活。

三、基于purr的匿名函数编程(泛函数式)

在序列上做循环迭代(应用函数),经常需要自定义函数,但有些简单的函数也用 function 定义,就会显得麻烦。所以,purrr 包提供了对 purrr 风格公式(匿名函数)的支持。

在上述描述中,purrr 包实现迭代循环是用 map(.x, .f).f是要应用的函数。如果想用匿名函数来写.f,并将.f应用在序列 .x 上(也就是将匿名函数.f和序列 .x 关联),那么就限定用序列参数名关联好了,即将序列参数名作为匿名函数的参数

详细的示例

一元函数:序列参数 .x

比如,f(x) = x^2 + 1, 其 purrr 风格公式(匿名函数)就写为:~ .x ^ 2 + 1

二元函数:序列参数是 .x, .y

比如,f(x, y) = x^2 - 3 y, 其 purrr 风格公式(匿名函数)就写为:~ .x ^ 2 - 3 * .y

多元函数:序列参数是 ..1, ..2, ..3,

比如,f(x, y, z) = ln(x + y + z), 其 purrr 风格公式(匿名函数)就写为:~ log(…1 + …2 + …3)

注:所有序列参数,可以用 … 代替,比如,sum(…1, …2, …3) 同 sum(…)

四、map系列函数解读

4.1 map函数

map(.x, .f, ...)
map_*(.x, .f, ...)

其中,.x 为序列;.f为一元函数,或 purrr 风格公式(匿名函数);...可以设置函数 .f的其它参数。

示例1:计算每列的均值
  • 返回列表
# 依次将 mean() 函数,应用到第1列,第2列,...
> df <- iris[,1:4]
> map(df, mean)
# $Sepal.Length
# [1] 5.843333
#
# $Sepal.Width
# [1] 3.057333
#
# $Petal.Length
# [1] 3.758
#
# $Petal.Width
# [1] 1.199333

注解:

  • df 是数据框(特殊的列表),作为序列其元素依次是:df[[1]], df[[2]], … 所以,map(df, mean) 相当于依次计算:mean(df[[1]]), mean(df[[2]]), …
  • 返回向量
> map_dbl(df, mean)
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width
# 5.843333     3.057333     3.758000     1.199333
  • 使用多个参数

mean()函数还有其它参数,如 na.rm,若上述计算过程需要设置忽略缺失值

> map_dbl(df, mean, na.rm=TRUE)
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width
# 5.843333     3.057333     3.758000     1.199333
  • purr 风格公式(匿名函数)
> map_dbl(df, ~mean(.x, na.rm=TRUE))
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width
# 5.843333     3.057333     3.758000     1.199333
示例2:批量读取数据文件并合并(列名相同)
files = list.files("datas/", pattern = "xlsx", full.names = TRUE)
df = map_dfr(files, read_xlsx)    # 批量读取+按行堆叠合并

注解

  • files 获取 datas 文件夹下所有 .xlsx 文件的路径,若嵌套只需设置参数 recursive = TRUR;
  • map_dfr(files, read_xlsx) 依次将 read_xlsx() 函数应用到各个文件路径上,即依次读取数据,返回结果是数据框,同时“r”表示再做按行合并,一步到位。若需要设置 read_xlsx() 的其它参数,只需在后面设置即可。
示例3:批量建模
  1. 根据分类变量对数据进行分组,对每组分别建模,再提取模型信息
> df <- mtcars %>% select(mpg, cyl, wt)
> head(df)
# mpg cyl    wt
# Mazda RX4         21.0   6 2.620
# Mazda RX4 Wag     21.0   6 2.875
# Datsun 710        22.8   4 2.320
# Hornet 4 Drive    21.4   6 3.215
# Hornet Sportabout 18.7   8 3.440
# Valiant           18.1   6 3.460
  1. 嵌套列表列:按照某列分组,并形成嵌套的数据结构
> df <- df %>% group_nest(cyl)
> df
# # A tibble: 3 × 2
# cyl               data
# <dbl> <list<tibble[,2]>>
# 1     4           [11 × 2]
# 2     6            [7 × 2]
# 3     8           [14 × 2]
> df$data[[1]]
# # A tibble: 11 × 2
# mpg    wt
# <dbl> <dbl>
#   1  22.8  2.32
# 2  24.4  3.19
# 3  22.8  3.15
# 4  32.4  2.2
# 5  30.4  1.62
# 6  33.9  1.84
# 7  21.5  2.46
# 8  27.3  1.94
# 9  26    2.14
# 10  30.4  1.51
# 11  21.4  2.78
  1. 建模
> df <- df %>% mutate(# 序列参数名作为匿名函数的参数model=map(data, ~lm(mpg~wt, data = .x)),  # 分组建模pred=map(model, predict)                   # 计算每个样本的预测值)
> df
# # A tibble: 3 × 4
# cyl               data model  pred
# <dbl> <list<tibble[,2]>> <list> <list>
#   1     4           [11 × 2] <lm>   <dbl [11]>
#   2     6            [7 × 2] <lm>   <dbl [7]>
#   3     8           [14 × 2] <lm>   <dbl [14]>
  1. 模型基本信息
> df$model %>% map(summary)
# [[1]]
#
# Call:
#   lm(formula = mpg ~ wt, data = .x)
#
# Residuals:
#   Min      1Q  Median      3Q     Max
# -4.1513 -1.9795 -0.6272  1.9299  5.2523
#
# Coefficients:
#   Estimate Std. Error t value Pr(>|t|)
# (Intercept)   39.571      4.347   9.104 7.77e-06 ***
#   wt            -5.647      1.850  -3.052   0.0137 *
#   ---
#   Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# Residual standard error: 3.332 on 9 degrees of freedom
# Multiple R-squared:  0.5086,  Adjusted R-squared:  0.454
# F-statistic: 9.316 on 1 and 9 DF,  p-value: 0.01374
#
#
# [[2]]
#
# Call:
#   lm(formula = mpg ~ wt, data = .x)
#
# Residuals:
#   1       2       3       4       5       6       7
# -0.1250  0.5840  1.9292 -0.6897  0.3547 -1.0453 -1.0080
#
# Coefficients:
#   Estimate Std. Error t value Pr(>|t|)
# (Intercept)   28.409      4.184   6.789  0.00105 **
#   wt            -2.780      1.335  -2.083  0.09176 .
# ---
#   Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# Residual standard error: 1.165 on 5 degrees of freedom
# Multiple R-squared:  0.4645,  Adjusted R-squared:  0.3574
# F-statistic: 4.337 on 1 and 5 DF,  p-value: 0.09176
#
#
# [[3]]
#
# Call:
#   lm(formula = mpg ~ wt, data = .x)
#
# Residuals:
#   Min      1Q  Median      3Q     Max
# -2.1491 -1.4664 -0.8458  1.5711  3.7619
#
# Coefficients:
#   Estimate Std. Error t value Pr(>|t|)
# (Intercept)  23.8680     3.0055   7.942 4.05e-06 ***
#   wt           -2.1924     0.7392  -2.966   0.0118 *
#   ---
#   Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# Residual standard error: 2.024 on 12 degrees of freedom
# Multiple R-squared:  0.423,   Adjusted R-squared:  0.3749
# F-statistic: 8.796 on 1 and 12 DF,  p-value: 0.01179
> df$model %>% map(summary) %>% map_dbl("r.squared")
# 用列表的元素名做 map 相当于提取该元素
# [1] 0.5086326 0.4645102 0.4229655> df %>% unnest(c(data, pred))
# 解除嵌套
# # A tibble: 32 × 5
# cyl   mpg    wt model   pred
# <dbl> <dbl> <dbl> <list> <dbl>
# 1     4  22.8  2.32 <lm>    26.5
# 2     4  24.4  3.19 <lm>    21.6
# 3     4  22.8  3.15 <lm>    21.8
# 4     4  32.4  2.2  <lm>    27.1
# 5     4  30.4  1.62 <lm>    30.5
# 6     4  33.9  1.84 <lm>    29.2
# 7     4  21.5  2.46 <lm>    25.7
# 8     4  27.3  1.94 <lm>    28.6
# 9     4  26    2.14 <lm>    27.5
# 10     4  30.4  1.51 <lm>    31.0
# # … with 22 more rows

4.2 map2()函数:依次应用二元函数到两个序列的每对元素上

map2(.x, .y .f, ...)
map2_*(.x, .y, .f, ...)

其中,.x为序列1,.y为序列2,.f 为要应用的二元函数,或 purrr 风格公式(匿名函数),...可设置函数 .f 的其它参数。

示例:计算BMI指数
> height = c(1.58, 1.76, 1.64)
> weight = c(52, 73, 68)
> bmi = function(h, w) w/h^2
> map2_dbl(height, weight, bmi)
# [1] 20.83000 23.56663 25.28257# purr风格公式(匿名函数)
> map2_dbl(height, weight, ~.y / .x^2)
# [1] 20.83000 23.56663 25.28257

说明:

  • 序列1其元素为:height[[1]], height[[2]], …
  • 序列2其元素为:weight[[1]], weight[[2]], …
  • 所以,map2_dbl(height, weight, cal_BMI) 相当于依次计算:cal_BMI(height[[1]], weight[[1]]), cal_BMI(height[[2]], weight[[2]]), …
> df = tibble(height = height, weight = weight)
> df %>% mutate(bmi=map2_dbl(height, weight, bmi))
# # A tibble: 3 × 3
# height weight   bmi
# <dbl>  <dbl> <dbl>
#   1   1.58     52  20.8
# 2   1.76     73  23.6
# 3   1.64     68  25.3# purrr 风格公式(匿名函数)
> df %>% mutate(bmi=map2_dbl(height, weight, ~.y/.x^2))
# # A tibble: 3 × 3
# height weight   bmi
# <dbl>  <dbl> <dbl>
#   1   1.58     52  20.8
# 2   1.76     73  23.6
# 3   1.64     68  25.3

4.3 pmap(): 应用多元函数到多个序列的每组元素上,可以实现对数据框逐行迭代

pmap(.l, .f, ...)
pmap_*(.l, .f, ...)

其中,.l为数据框,.f为要应用的多元函数,...可设置函数 .f 的其它参数
注:.f是几元函数,对应数据框 .l有几列,.f将依次在数据框 .l的每一行上进行迭代。

示例5: 分别生成不同数量不同均值、标准差的正态分布随机数。
> df <- tibble(n = c(1,3,5),mean = c(5,10,-3),sd = c(1,5,10)
)
> pmap(df, rnorm)

说明:这里的 rnorm(n, mean, sd) 是三元函数,pmap(df, rnorm) 相当于将三元函数 rnorm() 依次应用到数据框 df 的每一行上,即依次执行:rnorm(1, 5, 1), rnorm(3, 10, 5), rnorm(5, -3, 10)

  • 特别注意,这里 df 中的列名,必须与 rnorm() 函数的参数名相同(列序随便)。若要避免这种局限,可以使用 purrr 风格公式写法:
pmap(df, ~ rnorm(..1, ..2, ..3))    # 结果同上(略), 或者简写为
pmap(df, ~ rnorm(...))

优雅的循环迭代和泛函数编程-purr packages 和 map 函数相关推荐

  1. python手机版怎么用-如何优雅的在手机上进行Python编程

    原标题:如何优雅的在手机上进行Python编程 很多人都在学习Python,但是我们往往在清香于忙碌工作的同时的时候,很少有空余时间去学习py.今天就给大家推荐一个运行在android手机上的开发软件 ...

  2. python循环输入字典_python - 使用'for'循环迭代字典

    使用'for'循环迭代字典 .values Python如何识别它只需要从中读取密钥   字典? 关键是Python中的一个特殊词吗? 或者只是一个   变量? 这不仅仅是.values循环. 这里重 ...

  3. python中循环迭代语句_python条件与循环-循环

    1 while语句 while用于实现循环语句,通过判断条件是否为真,来决定是否继续执行. 1.1 一般语法 语法如下: while expression: suite_to_repeat 1.2 计 ...

  4. 阿里开发者们的第13个感悟:工程师需要在循环迭代中成长

    2015年12月20日,云栖社区上线.2018年12月20日,云栖社区3岁. 阿里巴巴常说"晴天修屋顶". 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备. 所以社区特别 ...

  5. java当中有关循环的代码_有关Java循环的内容,编程中还是比较常用的,下面分享给大家几个循环的示例代码,练习一下。1、循环输出1到100之间所有能被3或能被4整除的数。pack...

    有关Java循环的内容,编程中还是比较常用的,下面分享给大家几个循环的示例代码,练习一下. 1.循环输出1到100之间所有能被3或能被4整除的数. package com.hz.loop02; /** ...

  6. 手机上有没有学python的软件-如何优雅的在手机上进行Python编程

    原标题:如何优雅的在手机上进行Python编程 很多人都在学习Python,但是我们往往在清香于忙碌工作的同时的时候,很少有空余时间去学习py.今天就给大家推荐一个运行在android手机上的开发软件 ...

  7. 增强型的for循环linkedlist_38. 为什么千万别用for循环迭代LinkedList

    今天晚上7点时候,想着每天的8点健身还早,突然想起来以前的一个知识点说千万别用for循环迭代LinkedList,效率奇低,今天就想着来写个测试例子并分析原理: 代码测试与现象 哈哈哈,首先还是先上源 ...

  8. js循环/迭代/遍历有多少方法

    js循环/迭代/遍历有多少方法 JavaScript中存在着很多循环的方法 常见的有for,while,do while,for in等, ES5中的forEach, ES6的for of , jqu ...

  9. 手机python代码写好了怎么运行-如何优雅的在手机上进行Python编程

    原标题:如何优雅的在手机上进行Python编程 很多人都在学习Python,但是我们往往在清香于忙碌工作的同时的时候,很少有空余时间去学习py.今天就给大家推荐一个运行在android手机上的开发软件 ...

最新文章

  1. 如何为ORACLE表空间创建大容量数据文件
  2. jQuery的Accordion插件
  3. JavaScript教程之DOM和BOM
  4. java rsa 公钥加密_java – 使用公钥进行RSA解密
  5. webwork在freemarker中使用iterator
  6. 10-30-010-安全简介-Kafka 安全机制
  7. el-table——可合并单元格的表格
  8. 已安装过matplotlib但提示ModuleNotFoundError: No module named ‘matplotlib‘的解决方法
  9. H5横竖屏的两种解决方法
  10. 超级保镖计算机管理系统
  11. Sublime Text 3在行前插入递增数字序号的方法
  12. 打开和设置IDEA欢迎界面
  13. 辞旧迎新:祝您阖家幸福安康,万事如意
  14. 一个学习小组有5个人,每个人有三门课的考试成绩。求全组分科的平均成绩和各科总平均成绩。
  15. R数据分析当中的化整为零(Split-Apply-Combine)策略
  16. Leetcode 1235. Maximum Profit in Job Scheduling (python)
  17. jk触发器的异步置位端和异步复位端的表示方法
  18. aiwi游戏里的忍者神龟
  19. PHP如何开发医疗HIS系统短信通知
  20. 深度玄学-实战开发步骤

热门文章

  1. [转] 判断中文,日文(日语),韩文(韩语)的正则表达式
  2. 重磅| 创业者的格局要大——徐小平谈中国创业
  3. 2019 CCPC final
  4. 私人云/让web客户端可显示服务器目录(二)
  5. 百分百解决win10蓝屏问题,硬件损坏除外
  6. 深度学习入门论文(语音识别领域)
  7. 史上程序员被黑得最惨的一次?
  8. Galileo:一款开源Web应用审计框架
  9. java swing(GUI) MySQL实现的KTV点歌系统源码附带视频指导教程
  10. android stdio 编译问题