概念

高阶函数(higher-order function)是这样的函数:它接受一个函数作为输入或者返回一个函数作为输出。

  • 闭包: 返回另一个函数
  • 泛函: 一种接受函数作为输入,并返回一个向量作为输出的函数。如下,randomise就是一个泛函,接受mean函数并返回向量

    1. randomise <- function(f){
    2. f(runif(1000))
    3. }
    4. > randomise(mean)
    5. [1] 0.5001271

    三大最常用的泛函:

  • lapply

  • tapply
  • apply

    lapply

    lapply()接受一个函数,把它应用于列表中的每个元素,最后以列表的形式返回结果。 lapply()是构建许多其它泛函的基础,lapply使用C编写,用r也可以实现一个简单的lapply2 ```r lapply2 <- function(x, func, …){ out <- vector(“list”, length(x)) for (i in seq_along(x)){ out[[i]] <- func(x[[i]]) } out } x <- list(1,3,4) lapply2(l, function(x){x * 2}) # [[1]] [1] 2

[[2]] [1] 6

[[3]] [1] 8

  1. `**lapply()**`**是对一种通用的 **`**for**`**循环模式的包装器:**为输出创建一个容器,把` f()`应用到列表的每个组件,并把结果填充到容器中。 所有其它的` for `循环泛函,都是这种模式的变种:它们只是使用不同的类型输入或输出<br />`lapply`对数据框的操作也是非常的方便,因为数据框的每一列是一个`list`
  2. ```r
  3. unlist(lapply(mtcars, mean))
  4. #mpg cyl disp hp drat wt qsec vs
  5. # 20.090625 6.187500 230.721875 146.687500 3.596563 3.217250 17.848750 0.437500
  6. # am gear carb
  7. # 0.406250 3.687500 2.812500
  8. # 每一列除以均值
  9. mtcars[] <- lapply(mtcars, function(x) x / mean(x))

循环模式

r中对向量的循环有三种形式:

  1. 在元素上循环: for (x in xs):不好的形式,
  2. 在数字索引上循环: for (i in seq_along(xs))
  3. 在名字上循环:for (nm in names(xs))

第一种形式通常不是好选择,因为它导致了低效的保存输出的方式。 使用这种形式,很自然地会通过扩展数据结构来保存输出,

  1. xs <- runif(1e3)
  2. res <- c()
  3. for (x in xs) {
  4. # 慢,每次都要把整个向量复制一次
  5. res <- c(res, sqrt(x))
  6. }

最好是事先为输出创建好所需要的空间,然后进行填充。 使用第二种形式来实现这种方式是最简单的

  1. xs <- runif(1e3)
  2. res <- numeric(length(xs))
  3. for (i in seq_along(xs)) {
  4. res[i] <- sqrt(xs[i])
  5. }

就像使用 for 循环有三种基本方法一样,也有三种使用 lapply()的基本方法

  1. lapply(xs, function(x) {})
  2. lapply(seq_along(xs), function(i) {})
  3. lapply(names(xs), function(nm) {})

如需要知道你正在使用的元素的位置或者名字,那么你应该使用第二种或者第三种形式。 两者都给了你元素的位置(i、 nm)和值(xs[[i]]、 xs[[nm]])

sapply和vapply

sapply()vapply()lapply()非常相似,唯一的区别是它们简化了输出,产生的是原子向量。

  • sapply()会猜测输出类型
  • vapply()使用一个额外的参数来指定输出类型

如,

  1. 当给定一个数据框时, sapply()vapply()返回相同的结果。
  2. 当给定一个空列表时, sapply()返回另一个空列表,而不是更正确的零长度逻辑向量。 ```r

    sapply(mtcars,is.character)

    mpg cyl disp hp drat wt qsec vs am gear carb

    FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

vapply(mtcars,sum, 1) mpg cyl disp hp drat wt qsec vs am gear 642.900 198.000 7383.100 4694.000 115.090 102.952 571.160 14.000 13.000 118.000 carb 90.000

vapply(mtcars,mean, integer(1)) Error in vapply(mtcars, mean, integer(1)) : 值的种类必需是’integer’, 但FUN(X[[1]])结果的种类却是’double’ `sapply()`是` lapply()`一种很简单的包装,它只是在最后一步把列表转换成向量。`vapply()`是 `lapply()`的一种实现,它将结果赋给一个适当类型的向量(或矩阵),而不是列表。r df2 <- data.frame(x = 1:10, y = Sys.time() + 1:10) sapply(df2, class) $x [1] “integer”

$y [1] “POSIXct” “POSIXt”

vapply(df2, class, character(1)) Error in vapply(df2, class, character(1)) : 值的长度必需为1, 但FUN(X[[2]])结果的长度却是2 ```

Map

使用lapply(),只有一个作为参数的函数是变化的;其它参数是固定的。 这使得它对于一些问题不太适合。 例如,当你有两个列表时,一个存放观察数据,另一个存放权重
可以使用 Map,它是lapply()的一个变种,它的所有参数都可以变化
当只用lapply实现 ```r xs <- replicate(5, runif(10), simplify = FALSE) # 生成5组数据,存放在一个list里面 ws <- replicate(5, rpois(10, 5) + 1, simplify = FALSE)

unlist(lapply(seq_along(xs), function(i){ weighted.mean(xs[[i]], ws[[i]]) }))

[1] 0.4641686 0.4416736 0.5133203 0.5646282 0.5747209

  1. 使用`Map`函数处理,`Map()`的第一个参数是函数
  2. ```r
  3. unlist(Map(weighted.mean, xs, ws))
  4. [1] 0.4641686 0.4416736 0.5133203 0.5646282 0.5747209

有两个(或更多)列表(或数据框)需要并行处理, 可以使用Map()。 例如,标准化列的另一种方式是,首先计算均值,然后除以这一列。

  1. # lapply 实现
  2. mtcars[] <- lapply(mtcars, function(x) x / mean(x))
  3. # Map 实现
  4. mtcars[] <- Map(`/`, mtcars, lapply(mtcars, mean))

操作矩阵和数据框

apply()sweep()outer()tapply()

apply

apply()sapply()的一个变种,它可以处理矩阵和数组。 它通过把矩阵或数组的每行或者每列分解成单个数值,对矩阵或数组进行汇总计算
有四个参数:

  • X: 需要进行汇总计算的矩阵或数组。
  • MARGIN: 一个整数向量,它给出需要进行汇总计算的维度, 1 = rows2 =columns
  • FUN: 汇总计算的函数。
  • ...: 传给 FUN 的其它参数。

    1. a <- matrix(1:20, nrow = 5)
    2. apply(a, 1, mean)

    apply()不能完全确定你会得到什么类型的输出。 这意味着,在函数内部使用 apply()是不安全的

    sweep

    需要将apply()统计出来的统计量代回原数据集去做相应操作的时候就可以用到sweep()
    如,将矩阵的每一行减去对应行的最小值后除以最大值

    1. x <- matrix(rnorm(20, 0, 10), nrow = 4)
    2. x1 <- sweep(x, 1, apply(x, 1, min), `-`)
    3. x2 <- sweep(x1, 1, apply(x1, 1, max), `/`)

    outer

    它需要多个向量输入,并创建一个矩阵或数组作为输出,对于每一种输入组合,都应用输入函数,即对作为输
    入的两个向量中的所有元素,进行两两配对,每一个配对作为参数输入到函数中, 用法如下,也可使用%o%中缀函数替代,1:3 %o% 1:3

    1. > outer(1:3,1:1, `*`)
    2. [,1]
    3. [1,] 1
    4. [2,] 2
    5. [3,] 3
    6. > outer(1:3,1:2, `*`)
    7. [,1] [,2]
    8. [1,] 1 2
    9. [2,] 2 4
    10. [3,] 3 6
    11. > outer(1:3,1:3, `*`)
    12. [,1] [,2] [,3]
    13. [1,] 1 2 3
    14. [2,] 2 4 6
    15. [3,] 3 6 9
    16. > outer(1:3,1:1, "paste")
    17. [,1]
    18. [1,] "1 1"
    19. [2,] "2 1"
    20. [3,] "3 1"
    21. > outer(1:3,1:2, "paste")
    22. [,1] [,2]
    23. [1,] "1 1" "1 2"
    24. [2,] "2 1" "2 2"
    25. [3,] "3 1" "3 2"

    tapply

    tapply可以对数据进行分组汇总计算

    1. pulse <- round(rnorm(22, 70, 10 / 3)) + rep(c(0, 5), c(10, 12))
    2. group <- rep(c("A", "B"), c(10, 12))
    3. tapply(pulse, group, function(x) mean(x))
    4. A B
    5. 70.90000 74.41667

    也可以对数据框进行分组求均值等

    1. df <- data.frame(a = c(1:20), b = c(2:21), group = rep(c("A", "B"), c(10, 10)))
    2. tapply(df$a, df$group, mean) # tapply(df[["a"]], df[["group"]], mean)
    3. A B
    4. 5.5 15.5

    tapply()仅仅是split()sapply()的联合,自己简单实现一下

    1. tapply2 <- function(vector, index, func, ...){
    2. split_vector <- split(vector, index)
    3. sapply(split_vector, func)
    4. }
    5. tapply2(df$a, df$group, mean)
    6. A B
    7. 5.5 15.5

    操作列表

    泛函的另一种方式,是作为一组通用的工具, 改变列表、 对列表取子集以及消去列表。 每一种函数式编程语言都有三种工具来做这些事情:Map()Reduce()Filter()

    Reduce

    Reduce()可以削减向量 x 成为单个值,它通过递归地调用函数 f 做到这一点,在每两个参数之间调用一次 f。 它使用 f 来合并前两个元素,得到一个结果,然后合并这个结果与第三个元素, …,以此类推。 调用 Reduce(f, 1:3)相当于调用 f(f(1, 2),3)Reduce也被称为 fold,因为它将列表中的相邻元素合并在了一起

    1. Reduce(sum, 1:3)
    2. # [1] 6
  • right,控制值是从左边还是从右边进行合并。

  • init,一个可选的初始值。
  • accumulate,是否输出中间结果的选项

使用Reduce求列表中所有元素的交集

  1. l <- replicate(5, sample(1:10, 15, replace = T), simplify = FALSE)
  2. Reduce(intersect, l)
  3. # [1] 6 2 10 4

谓词函数

谓词函数是返回一个 TRUEFALSE的函数,比如 is.characterallis.NULL。谓词泛函对列表或数据框中的每个元素应用谓词函数。 在基础 R 语言中,有三个有用的谓词泛函:Filter()Find()Position()

  • Filter(): 只选择那些匹配谓词函数的元素

    1. Filter(function(x) x>1, c(0.1,0.2,1,2,3))
  • Find(): 返回第一个匹配谓词函数的元素(right = TRUE,则返回最后一个元素)。

    1. Find(function(x) x>1, c(0.1, 0.2, 1, 2, 3))
  • Position(): 返回第一个匹配谓词函数的元素的位置(right = TRUE,则返回最后一个元素的位置)

    1. Position(function(x) x>1, c(0.1, 0.2, 1, 2, 3))

    如使用Filter函数对dataframe中的数值列统计均值 ```r df <- data.frame( a = c(1:100), b = c(2:101), c = c(3:102), e = rep(c(“a”,”b”), c(50, 50)) )

vapply(Filter(is.numeric, df), mean, double(1)) sapply(Filter(is.numeric, df), mean) ```