R语言学习笔记11
R语言教程-R编程8
函数进阶
函数式编程
在map中使用无名函数以及简写方法
map调用的函数可以是在map中直接定义的函数
上面4个学生,每名3门成绩的问题。
library(purrr) |
使用无名函数格式比较复杂, purrr包为在map()
等泛函中使用无名函数提供了简化的写法, 将无名函数写成“~ 表达式
”格式, 表达式就是无名函数定义, 用.
表示只有一个自变量时的自变量名, 用.x
和.y
表示只有两个自变量时的自变量名, 用..1
、..2
、..3
这样的名字表示有多个自变量时的自变量名。 如:
map_dbl(strsplit(s, split = ",", fixed = T), |
无名函数中的其它变量在每次被map()应用到输入列表的元素时都会重新计算求值。 建议这样的情况改用有名函数, 这样其中访问其它变量时作用域规则比较容易掌控, 也不会重复求值。
在map
中提取列表元素成员的简写
较为复杂的数据, 有时表现为列表的列表, 每个列表元素都是列表或者向量。 JSON、YAML等格式转换为R对象就经常具有这种嵌套结构。
例如, 有如下的嵌套格式数据, 这样的数据不利于用数据框格式保存:
od <- list( |
为了取出每一项来,本来写成:
map_dbl(od, ~ .[[1]]) |
purrr包提供了进一步的简化写法, 在需要一个函数或者一个“~ 表达式”的地方, 可以用整数下标值表示对每个列表元素提取其中的指定成分,如:
map_dbl(od, 1) |
map_chr(od, 2) |
类似地,可以在需要函数的地方写一个成员名, 提取每个列表元素中该成员,如:
map_chr(od, "name") |
在应该用函数的地方还可以提供一个列表, 列表元素为成员序号或者成员名, 进行逐层挖掘,如:
map_chr(od, list("hobbies", 1)) |
表示取出每个列表元素的hobbies成员的第一个元素(每人的第一个业余爱好)。
取出不存在的成员会出错, 但可以用一个.default选项指定查找不到成员时的选项, 如:
map_chr(od, "birth", .default=NA) |
问题:
如何取出两个List中的不一样长的hobbies项?
数据框分组处理示例
d.class |
对d.class数据框, 希望分成男女生两个组, 每组内建立用身高预测体重的一元线性回归模型, 提取各模型的斜率项。 基本R的split函数可以按数据框的某列将数据框分成若干个子数据框, 结果为子数据框的列表。 借助于purrr包的map类函数和管道运算符, 可以将分组建模过程写成:
d.class %>% |
这些步骤用基本R的lapply或者for循环也能够完成, 但会更难读懂, 或者需要生成许多中间临时变量, 不如上面例子这样步骤清晰而且不需要产生中间结果。
dplyr包和plyr包与这个例子的思想类似, 只不过更有针对性。
LM()即为Linear Models的缩写。lm是用来适应线性模型的。它可用于进行回归、单层分析方差分析和协方差分析。
Arguments | 参数
- **formula:**指要拟合的模型形式,
- **data:**是一个数据框,包含了用于拟合模型的数据。
lm()函数举例:
a <- c(2, 3, 4) |
purrr包中map函数的变种
purrr包的map函数输入一个数据自变量和一个函数, 输出为列表; map_dbl()等将输出转化为基础类型的向量。
purrr包还提供了与map
目的类似, 但输入输出类型有变化的一些函数, 包括:
modify()
,输入一个数据自变量和一个函数, 输出与输入数据同类型的结果;map2()
可以输入两个数据自变量和一个函数, 将两个自变量相同下标的元素用函数进行变换, 输出列表;imap()
根据一个下标遍历;walk()
输入一个数据自变量和一个函数, 不返回任何结果,仅利用输入的函数的副作用;- 输入若干个数据自变量和一个函数, 对数据自变量相同下标的元素用函数进行变换;
输入输出类型相同的modify函数
对d.class中的三个数值型列, 都减去列中位数,其它列保持不变:
d1 <- modify(d.class, ~ if(is.numeric(.)) . - median(.) else .) |
purrr包还提供了一个modify_if()函数, 可以对满足条件的列进行修改,
d2 <- modify_if(d.class, is.numeric, ~ . -median(.)) |
对两个自变量的相同下标元素调用函数
map()
函数仅支持一个输入数据的列表或向量。 map2()
函数支持两个输入数据的列表或向量, map2(x, y, f, ...)
对每个下标i
调用f(x[[i]], y[[i]], ...)
, 结果返回一个列表。 如果知道函数f()
会返回类型确定的标量值, 可以用map2_dbl()
等变种。
例如, d1是某市2001年四个季度的若干项经济指标, d2是2002年的对应指标, 计算每项指标年度总和的同比增幅:
library(tibble) |
如果计算结果与两个输入数据类型相同, 可以用modify2()。 比如, 上面的例子数据计算每个指标的同比增幅:
modify2(d1,d2, ~(..2 -..1) / ..1) |
map2()允许输入的x和y两个列表其中一个长度为1, 这时长度为1的列表的一个元素被重复利用。如:
d1b <- d1[,1, drop=F] |
不产生输出的walk类函数
有时仅需要遍历一个数据结构调用函数进行一些显示、绘图, 这称为函数的副作用, 不需要返回结果。 purrr的walk函数针对这种情形。
显示数据框中每个变量的类别:
walk(d.class, ~ cat(typeof(.), "\n")) |
上面这个例子缺点是没有显示对应的变量名。
walk2()函数可以接受两个数据自变量, 类似于map2()。 例如, 需要对一组数据分别保存到文件中, 就可以将数据列表与保存文件名的字符型向量作为walk2()的两个数据自变量。 下面的程序将d.class分成男女生两个子集, 保存到两个csv文件中:
# 按性别拆分为两个tibble |
paste和paste0函数
paste函数可用于字符串连接
paste("a","b",sep = "=")#注意到用等号分隔了 |
sep是在每个元素的分隔符,collapse是元素之间的连接符
paste("a",1:5,sep = "")##先在元素间连接 |
与paste0函数的区别:
paste(c("a", "b","c"),1:3)##默认空格符 |
改用管道运算符:
d.class %>% |
walk、walk2并不是没有输出, 它们返回不显示的第一个自变量, 所以也适合用在管道运算的中间使得管道不至于中断。
可同时访问下标或元素名与元素值的imap类函数
walk函数显示数据框各列类型的例子中, 没有能够同时显示变量名。
如果x有元素名, imap(x, f)相当于imap2(x, names(x), f); 如果x没有元素名, imap(x, f)相当于imap2(x, seq_along(x), f)。 iwalk()与imap()类似但不返回信息。 调用的函数的第二个自变量, 或者无名函数的.y自变量是元素名或者元素下标。 imap_chr()等是固定返回类型的变种。
显示数据框各列的变量名:
iwalk(d.class, ~ cat(.y, ":", typeof(.x), sep = '', "\n")) |
等同于:
iwalk(d.class, function(x,y) cat(y,":", typeof(x), sep = '', "\n")) |
其他写法探索:
ft <- function(x,y){ |
返回字符型向量的写法:
# unname() Remove the names or dimnames attribute of an R object. |
输入数据没有元素名的演示:
dn <- list(1:5, 101:103) |