R语言学习笔记10
R语言教程-R编程7
函数进阶
函数式编程
纯函数:
- 没有副作用,全局变量在函数之间传递是不允许的,结果也是去确定的,不会产生不同的结果(如R的rnorm是随机产生正态分布的一组数据,每次运行产生不同的结果)
- 不受外部影响,函数返回值只依赖于其自变量及函数的定义。不依赖于任何外部信息(也就不能依赖于全局变量与系统设置值)。
- 不受赋值影响。函数定义不需要反复对内部对象(所谓“状态变量”)赋值或修改。
绘图的函数中经常需要用par()修改绘图参数, 这会使得后续程序出错。 为此,可以在函数开头保存原始的绘图参数, 函数结束时恢复到原始的绘图参数。
mfcol, mfrow
A vector of the form c(nr, nc). Subsequent figures will be drawn in an nr-by-nc array on the device by columns (mfcol), or rows (mfrow), respectively.
R语言中的on.exit() 函数用来保证当函数退出时,函数把全局工作区恢复到原来的状态。
如
f <- function(){ |
如果函数中需要多次调用on.exit()指定多个恢复动作, 除第一个调用的on.exit()以外都应该加上add=TRUE选项。
如果需要指定越晚添加的恢复动作越先执行, 在on.exit()中还要加上after=FALSE选项。
泛函(functionals):支持内嵌函数, 并可以输入函数作为函数的自变量, 称这样的函数为泛函(functionals),如lapply类函数;
函数工厂:可以输出函数作为函数结果,称这样的函数为函数工厂;
函数算子(function operators):可以输入函数, 进行一定修改后输出函数的函数。
利用R的purrr扩展包,可以用统一的风格使用函数式编程, 比基本R的lapply类函数、Map、Reduce等更容易使用。
泛函
许多函数需要用函数作为参数,称这样的函数为泛函(functionals)。典型的泛函是lapply类函数。这样的函数具有很好的通用性,因为需要进行的操作可以输入一个函数来规定, 用输入的函数规定要进行什么样的操作。
purrr::map函数
设我们要对列表或向量x的每个元素x[[i]]调用函数f(), 将结果保存成一个列表。
其中的输入x是任意的,函数f是任意的。purrr包的map()函数可以用一条命令完成上述任务:
y <- map(x, f) |
示例:
d.class <- readr::read_csv('class.csv') |
typeof()函数求变量的存储类型
d.class |
d.class是一个tibble数据框, tibble也是一个列表, 每个列表元素是数据框的一列。
如下程序使用purrr::map()求每一列的存储类型, map的结果总是列表,每个列表元素对应于输入的一个元素, 如:
library(purrr) |
当结果比较简单时, 保存为列表不够方便, 函数unlist()可以将比较简单的列表转换为基本类型的向量
ve <- unlist(class_ty) |
关于一个数据框的结构, 用str()函数可以得到更为详细的信息:
str即structure的缩写,得到输入参数的结构信息
str(d.class) |
purrr::map()
总是返回列表。 如果确知其调用的函数总是返回某种类型的标量值, 可以用map
的变种:
map_lgl()
:返回逻辑向量;map_int()
:返回整型向量;map_dbl()
: 返回双精度浮点型向量(double类型);map_chr()
: 返回字符型向量。
比如, 求d.class
各列类型, 因为确知typeof()
函数对每列返回一个标量字符串,所以可以写成:
map_chr(d.class, typeof) |
# is.numeric 判断是否为数值型,结果为逻辑向量 |
...
形参
在R函数的形参中, 允许有一个特殊的…形参(三个小数点), 这在调用泛函类型的函数时起到重要作用。 在调用泛函时, 所有没有形参与之匹配的实参, 不论是带有名字还是不带有名字的, 都自动归入这个参数, 将会由泛函传递给作为其自变量的函数。 …参数的类型相当于一个列表, 列表元素可以部分有名部分无名, 用list(…)可以将其转换成列表再访问。
例如,函数mean()可以计算去掉部分最低、最高值之后的平均值, 用选项trim=指定一个两边分别舍弃的值的个数比例。 为了将d.class的三列数值型列计算上下各自扣除10%的平均值, 需要利用map_dbl()函数的…参数输入trim选项值,如:
library(purrr) |
上面的数值型列是直接在程序中固定列号选出的, 也可以用map_lgl()选出:
dsub <- d.class[,map_lgl(d.class, is.numeric)] |
purrr包提供了一个keep函数, 可以专门用来选择数据框各列或列表元素中满足某种条件的子集, 这个条件用一个返回逻辑值的函数来给出。如:
library(purrr) |
利用magrittr包的管道运算符%>%
可以将对一个数据框的删选、计算过程更清晰地表达出来, 不需要dsub这样存储中间结果的变量
利用%>%
管道传递数值:
d.class %>% |
在map类泛函中...
仅用来将额外的选项传递给要调用的函数, 不支持向量化, 如果需要对两个或多个自变量的对应元素作变换, 需用用purrr包的map2等函数。 如果泛函中调用的是无名函数, 则...
参数会造成变量作用域理解困难。
用map处理strsplit函数结果示例
# 假设4个学生3次测验成绩 |
# 可以使用strsplit()函数把三组成绩拆开 |
# 求三次x小测验的总分 |
以上表达式等于:
cat(sum(as.numeric(t1[[1]])),'-', names(t1[1])) |
或者写成:
# 求三次x小测验的总分 |
分别计算三个人的总分:
- 写成一个循环:
# 分别打印三个人的总分 |
用strsplit()处理有4个字符串的字符型向量s, 结果是长度为4的列表:
tmpr <- strsplit(s, ',', fixed=T); tmpr |
- 用map()和as.numeric()可以把列表中所有字符型转为数值型, 输出为一个列表, 然后再对各个列表元素中的向量求和。 使用管道运算符表达逐步的操作:
# 一句话的表达式 |
等同于:
s %>% |
受上面程序写法的启发,还可以写成:
t2 <- map(strsplit(s, split = ',',fixed = T),as.numeric) |