R语言学习笔记12
R语言教程-R编程9
函数进阶
函数式编程
多个数据自变量的pmap类函数
但是对于列表、数据框等多个自变量则不能自动进行向量化处理。 purrr包的pmap类函数支持对多个列表、数据框、向量等进行向量化处理。 pmap不是将多个列表等作为多个自变量, 而是将它们打包为一个列表。 所以, map2(x, y, f)用pmap()表示为pmap(list(x, y), f)。
在确知输出类型时可以用pmap_chr(), pmap_dbl()等变种, 在不需要输出结果时可以用pwalk()。
# 将三个列表中的对应项用c()函数连接 |
pmap()除了输入一个列表和要并行执行的函数以外, 也可以输入一个数据框, 对数据框的每一行执行函数。
d <- tibble::tibble( |
也可以写为:pmap_chr(d, function(...) paste(..., sep=":"))
其中的三个点代表d中的所有参数,当要写的参数较多时,可以这样使用。
pmap()和其它的map()类函数有一个区别是, 因为将输入数据打包在一个列表中, 而列表元素是有变量名的, 这样就可以将列表变量名取为要调用的函数的自变量名, 使得对输入列表中各元素的每个成员调用函数时, 可以带有对应的形参名调用。
mean()函数可以计算去掉最小、最大一部分后的平均值, mean(x, trim)用trim选项控制两端分别去掉的比例, 但是trim选项必须是标量。 用map_dbl()解决方法如下:
set.seed(4) |
可以用pmap()的列表元素名自动对应到调用函数形参名的方法:
pmap_dbl(list(trims = trimss), mean, x = x1) |
或者
pmap_dbl(list(trims=trimss), ~ mean(x1)) |
相当于写了一个循环函数,求rcauchy(1000)函数产生的1000个随机数中,求取两端分别去掉0.05, 0.1, 0.2, 0.3, 0.4
比例的数据后,求出的平均值。
pmap()的变种有ivoke_map(.f, .x, …), 其中.f是一个元素为函数名的字符型向量, .x是列表, .x的每个元素是.f列表中对应的函数的参数。 如:
—
创建 tibble 的另一种方法是使用 tribble() 函数,tribble 是 transposed tibble(转置 tibble) 的缩写。
tribble() 是定制化的,可以对数据按行进行编码:列标题由公式(以 ~ 开头)定义,数据条目以逗号分隔,这样就可以用易读的方式对少量数据进行布局
会加一条注释(以 # 开头的行)来明确指出标题行的位置
两种写法的对比:
写法1:tribble
tc <- tribble( |
写法2:tibble
tc2 <- tibble( |
两种写法的结果相同。
—
library(tibble) |
这里利用了tibble类型的高级功能: 某一列可以是列表类型, 然后列表元素也可以是列表、向量等复合类型。
purrr包中reduce类函数
reduce函数
purrr包的reduce函数把输入列表(或向量)的元素逐次地用给定的函数进行合并计算。
library(purrr) |
reduce可以对元素为复杂类型的列表进行逐项合并计算。
多个集合的交集的问题。 下面的例子产生了4个集合, 然后反复调用intersect()求出了交集:
set.seed(7) |
# intersect为取交集 |
与上面的结果相同:
reduce(x, intersect) |
也可以用magrittr包的%>%符号写成:
x[[1]] %>% intersect(x[[2]]) %>% intersect(x[[3]]) %>% intersect(x[[4]]) |
还可以写成循环:
y <- x[[1]] |
以上这几种写法中使用reduce函数最为简单直观。
泛函的好处是需要进行的变换或计算是作为参数输入的, 只要输入其它函数就可以改变要做的计算, 比如, 变成求各个集合的并集:
reduce(x, union) |
reduce()支持...
参数, 所以可以给要调用的函数额外的自变量或选项。
可以用选项.init给出合并初值, 在通用程序中使用reduce()时应该提供此选项, 这样如果输入了零长度数据, 可以有一个默认的返回值; 输入非零长度数据时, 此初值作为第一个元素之前的值参与计算, 所以一定要取为与要进行的运算一致的值, 比如连加的初始值自然为0, 连乘积的初始值自然为1, 多个集合交集的初始值为全集(所有参与运算的各个集合应为此初值的子集), 等等。
reduce2函数
其中的第二个参数y可以自定义运算的方式与方法
reduce2(x, y, f)中的x是要进行连续运算的数据列表或向量, 而y是给这些运算提供不同的参数。 如果没有.init初始值, f仅需调用length(x)-1次, 所以y仅需要有length(x)-1个元素; 如果有.init初始值, f需要调用length(x)次, y也需要与x等长。
accumulate函数
对于加法, R的sum()函数可以计算连加, 而cumsum()函数可以计算逐步的连加。如:
cat('1到5累加结果:\n') |
purrr::reduce()将连加推广到了其它的二元运算, purrr::accumulate()则类似cumsum()的推广。
例如,对前面例子中的4个集合, 计算逐步的并集, 结果的第一项保持原来的第一项不变:
accumulate(x, union) |
将上述结果简化显示:
# 使用unique函数去除重复值,保留唯一值,使用sort函数排序 |
purrr包中使用示性函数的泛函
示性函数 : 返回逻辑向量的函数称为示性函数
R中有许多is.xxx函数都是示性函数(predicate functions)。 示性函数本身不是泛函(用函数作为参数的函数), 但是它们可以作为泛函的输入。
purrr包提供了如下的以示性函数函数为输入的泛函:
some(.x, .p)
,对数据列表或向量.x
的每一个元素用.p
判断, 只要至少有一个为真,结果就为真;every(.x, .p)
与some
类似,但需要所有元素的结果都为真结果才为真。 这些函数与any(map_lgl(.x, .p))
和all(map_lgl(.x, .p))
类似, 但是只要在遍历过程中能提前确定返回值就提前结束计算, 比如some
只要遇到一个真值就不再继续判断,every
只要遇到一个价值就不再继续判断。detect(.x, .p)
返回数据.x
的元素中第一个用.p
判断为真的元素值, 而detect_index(.x, .p)
返回第一个为真的下标值。keep(.x, .p)
选取数据.x
的元素中用.p
判断为真的元素的子集;discard(.x, .p)
返回不满足条件的元素子集。
例如,判断数据框中有无因子类型的列:
# 作为参数输入的函数只需要函数名,不用加括号 |
判断数据框是否完全由数值型列组成:
every(d.class, is.numeric) |
返回向量中的第一个超过100的元素的值:
detect(c(1, 5, 77, 105, 99, 123), ~ . >= 100) |
返回向量中的第一个超过100的元素的下标:
detect_index(c(1, 5, 77, 105, 99, 123), ~ .x >= 100) |
下面的例子筛选出数据框的数值型列, 并用map_dbl求每列的平方和:
d.class %>% |
从数据框(或列表)中选一部分满足某种条件的子集进行变换是常用的做法, 所以map提供了map_if()和modify_if()变种, 允许输入一个示性函数, 对满足条件的子集才应用输入的变换函数进行处理, 输入数据中其它元素原样返回。
map_if
返回列表, modify_if
返回与输入数据相同类型的输出。 例如, 将数据框中数值型列除以100, 其它列保持不变:
# modify_if函数的第一个参数为输入数据,第二个参数为判断条件/函数,第三个参数为对判断条件为真的数据的处理 |
也可以写为:
d.class100 <- modify_if(d.class, is.numeric,`/`, 100) |
基本R的Find函数与detect作用类似, Position与detect_index作用类似, Filter函数与keep作用类似。