前言
在上一節(jié)中他去,我們主要介紹了 purrr
包提供的工具函數(shù)來(lái)減少 for
循環(huán)的使用浮还,使代碼更加的簡(jiǎn)潔桑孩,便于閱讀拜鹤。
但是,使用 R
原生的 apply
函數(shù)家族也能夠極大減少 for
循環(huán)的使用流椒。
下面我們主要介紹 apply
函數(shù)的使用敏簿。
apply
針對(duì)不同的數(shù)據(jù)類(lèi)型,會(huì)有不同的變形,共同組成了 apply
函數(shù)家族惯裕。包括
apply
, lapply
, sapply
, vapply
, tapply
, mapply
, rapply
, eapply
1. apply
1.1 描述
通過(guò)對(duì)數(shù)組或矩陣的 MARGIN
應(yīng)用函數(shù)獲得的向量温数、數(shù)組或數(shù)值列表
1.2 用法
apply(X, MARGIN, FUN, ...)
1.3 參數(shù)
-
X
: 數(shù)組、矩陣轻猖、數(shù)據(jù)框帆吻,數(shù)據(jù)至少是二維的 -
MARGIN
: 按行計(jì)算或按列計(jì)算,1
表示按行咙边,2
表示按列 -
FUN
: 自定義的調(diào)用函數(shù) -
...
:FUN
的可選參數(shù)
1.4 示例
現(xiàn)在猜煮,我們有下面一個(gè)簡(jiǎn)單矩陣
> (my.matrx <- matrix(c(1:10, 11:20, 21:30), nrow = 10, ncol = 3))
[,1] [,2] [,3]
[1,] 1 11 21
[2,] 2 12 22
[3,] 3 13 23
[4,] 4 14 24
[5,] 5 15 25
[6,] 6 16 26
[7,] 7 17 27
[8,] 8 18 28
[9,] 9 19 29
[10,] 10 20 30
我們可以對(duì)每一行求和
> apply(my.matrx, 1, sum)
[1] 33 36 39 42 45 48 51 54 57 60
計(jì)算每一列的長(zhǎng)度
> apply(my.matrx, 2, length)
[1] 10 10 10
傳遞自定義匿名函數(shù)
> apply(my.matrx, 2, function (x) length(x)-1)
[1] 9 9 9
使用定義在外部的函數(shù)
> st.err <- function(x){
+ sd(x)/sqrt(length(x))
+ }
> apply(my.matrx,2, st.err)
[1] 0.9574271 0.9574271 0.9574271
轉(zhuǎn)換數(shù)據(jù)
現(xiàn)在來(lái)點(diǎn)不一樣的,在前面的示例中败许,我們使用 apply
對(duì)行或列應(yīng)用函數(shù)王带。
其實(shí),還可以對(duì)矩陣的單元格元素應(yīng)用函數(shù)市殷,如果您將 MARGIN
設(shè)置為 1:2
愕撰,那么該函數(shù)將對(duì)每個(gè)單元格進(jìn)行操作
> (my.matrx2 <- apply(my.matrx, 1:2, function(x) x+3))
[,1] [,2] [,3]
[1,] 4 14 24
[2,] 5 15 25
[3,] 6 16 26
[4,] 7 17 27
[5,] 8 18 28
[6,] 9 19 29
[7,] 10 20 30
[8,] 11 21 31
[9,] 12 22 32
[10,] 13 23 33
2. lappy
2.1 描述
將函數(shù)應(yīng)用于輸入變量的每一個(gè)元素,并返回與輸入變量長(zhǎng)度相同的 list
2.2 用法
apply(X, MARGIN, FUN, ...)
2.3 參數(shù)
-
X
:list
醋寝、data.frame
搞挣、vector
等 -
FUN
: 自定義的調(diào)用函數(shù) -
...
:FUN
的可選參數(shù)
2.4 示例
假設(shè)有下面的變量
> (vec <- c(1:10))
[1] 1 2 3 4 5 6 7 8 9 10
對(duì)每個(gè)元素求和
> str(lapply(vec, sum))
List of 10
$ : int 1
$ : int 2
$ : int 3
$ : int 4
$ : int 5
$ : int 6
$ : int 7
$ : int 8
$ : int 9
$ : int 10
返回一個(gè)長(zhǎng)度為 10
的 list
,它并沒(méi)有像我們期望的一樣音羞,返回一個(gè) 1-10
之和囱桨。
因?yàn)椋?code>lapply 會(huì)將向量視為列表低斋,并將函數(shù)應(yīng)用于每個(gè)元素上盈蛮。
讓我們將輸入變?yōu)橐粋€(gè)列表
> A<-c(1:9)
> B<-c(1:12)
> C<-c(1:15)
> my.lst<-list(A,B,C)
> lapply(my.lst, sum)
[[1]]
[1] 45
[[2]]
[1] 78
[[3]]
[1] 120
函數(shù)對(duì)列表中的每個(gè)向量求和,并返回一個(gè)長(zhǎng)度為 3
的列表
如果輸入的是一個(gè)矩陣
> (x <- matrix(1:12, nrow = 3))
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12
> str(lapply(x, sum))
List of 12
$ : int 1
$ : int 2
$ : int 3
$ : int 4
$ : int 5
$ : int 6
$ : int 7
$ : int 8
$ : int 9
$ : int 10
$ : int 11
$ : int 12
lapply
會(huì)循環(huán)矩陣中的每個(gè)值呵燕,而不是按行或按列進(jìn)行分組計(jì)算窘面。
那如果對(duì)一個(gè) data.frame
求和呢翠语?
> str(lapply(as.data.frame(x), sum))
List of 4
$ V1: int 6
$ V2: int 15
$ V3: int 24
$ V4: int 33
可以看到,lapply
會(huì)自動(dòng)對(duì) data.frame
的列分組求和
3. sapply
3.1 描述
sapply
的工作原理和 lapply
一樣财边,但是如果可能的話(huà)肌括,它會(huì)簡(jiǎn)化輸出。
這意味著酣难,如果數(shù)據(jù)可以簡(jiǎn)化们童,它將返回一個(gè)向量,而不是像 lappy
那樣總是返回一個(gè)列表鲸鹦。
3.2 用法
sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
3.3 參數(shù)
-
X
: 數(shù)組、矩陣跷跪、數(shù)據(jù)框 -
FUN
: 自定義的調(diào)用函數(shù) -
...
:FUN
的可選參數(shù) -
simplify
: 是否簡(jiǎn)化為向量馋嗜、矩陣或高維數(shù)組,當(dāng)值array
時(shí)吵瞻,輸出結(jié)果按數(shù)組進(jìn)行分組 -
USE.NAMES
: 如果X
為字符串且未設(shè)置名稱(chēng)葛菇,當(dāng)該值為TRUE
是設(shè)置字符串為數(shù)據(jù)名稱(chēng)甘磨,FALSE
不設(shè)置
3.4 示例
對(duì)向量使用 sapply
求和,返回一個(gè)長(zhǎng)度相等的向量
> vec
[1] 1 2 3 4 5 6 7 8 9 10
> sapply(vec, sum)
[1] 1 2 3 4 5 6 7 8 9 10
對(duì)列表求和
> my.lst
[[1]]
[1] 1 2 3 4 5 6 7 8 9
[[2]]
[1] 1 2 3 4 5 6 7 8 9 10 11 12
[[3]]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
> sapply(my.lst, sum)
[1] 45 78 120
返回的還是一個(gè)向量
傳入矩陣和 data.frame
> sapply(data.frame(x), sum)
X1 X2 X3 X4
6 15 24 33
> sapply(x, sum)
[1] 1 2 3 4 5 6 7 8 9 10 11 12
如果同時(shí)設(shè)置了 simplify=FALSE
和 USE.NAMES=FALSE
眯停,那么 sapply
和 lapply
將是一樣的
> str(sapply(data.frame(x), sum, simplify=FALSE, USE.NAMES=FALSE))
List of 4
$ X1: int 6
$ X2: int 15
$ X3: int 24
$ X4: int 33
使用 simplify = 'array'
構(gòu)建一個(gè)三維數(shù)組
> (v <- structure(10*(5:7), names = LETTERS[1:3]))
A B C
50 60 70
> sapply(v, function(x){outer(rep(x, 3), 2*(1:2))}, simplify = 'array')
, , A
[,1] [,2]
[1,] 100 200
[2,] 100 200
[3,] 100 200
, , B
[,1] [,2]
[1,] 120 240
[2,] 120 240
[3,] 120 240
, , C
[,1] [,2]
[1,] 140 280
[2,] 140 280
[3,] 140 280
還可以對(duì)字符串向量自動(dòng)生成數(shù)據(jù)名
> (val<-head(letters))
[1] "a" "b" "c" "d" "e" "f"
> sapply(val,paste,USE.NAMES=TRUE)
a b c d e f
"a" "b" "c" "d" "e" "f"
> sapply(val,paste,USE.NAMES=FALSE)
[1] "a" "b" "c" "d" "e" "f"
4. vapply
4.1 描述
類(lèi)似于 sapply
济舆,多了一個(gè) FUN.VALUE
參數(shù),用于指定返回值的行名
4.2 用法
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
4.3 參數(shù)
-
X
: 數(shù)組莺债、矩陣滋觉、數(shù)據(jù)框 -
FUN
: 自定義的調(diào)用函數(shù) -
FUN.VALUE
: 定義函數(shù)返回值的模板 -
...
:FUN
的可選參數(shù) -
USE.NAMES
: 如果X
為字符串且未設(shè)置名稱(chēng),當(dāng)該值為TRUE
時(shí)設(shè)置字符串為數(shù)據(jù)名稱(chēng)齐邦,FALSE
不設(shè)置
4.4 示例
例如椎侠,對(duì)數(shù)據(jù)框求累積和,并設(shè)置返回值的模板措拇,為返回值設(shè)置行名
> x
V1 V2 V3 V4
1 1 4 7 10
2 2 5 8 11
3 3 6 9 12
> vapply(as.data.frame(x), cumsum, FUN.VALUE = c('a'=1, 'b'=1, 'c'=1))
V1 V2 V3 V4
a 1 4 7 10
b 3 9 15 21
c 6 15 24 33
那如何從 lapply
, sapply
, vapply
這三個(gè)函數(shù)中進(jìn)行選擇呢我纪?通常盡量使用 sapply
,如果不需要簡(jiǎn)化輸出丐吓,則應(yīng)該使用 lapply
浅悉,如果要指定輸出類(lèi)型,則使用 vapply
5. mapply:
5.1 描述
mapply
也是 sapply
的變形券犁,類(lèi)似多變量 sapply
5.2 用法
mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE,USE.NAMES = TRUE)
5.3 參數(shù)
-
FUN
: 自定義的調(diào)用函數(shù) -
...
: 接受多個(gè)數(shù)據(jù) -
MoreArgs
: 參數(shù)列表 -
simplify
: 是否簡(jiǎn)化為向量术健、矩陣或高維數(shù)組,當(dāng)值array
時(shí)族操,輸出結(jié)果按數(shù)組進(jìn)行分組 -
USE.NAMES
: 如果X
為字符串且未設(shè)置名稱(chēng)苛坚,當(dāng)該值為TRUE
時(shí)設(shè)置字符串為數(shù)據(jù)名稱(chēng),FALSE
不設(shè)置
5.4 示例
取三個(gè)向量對(duì)應(yīng)位置的最大值
> x <- -4:5
> y <- seq(-9, 10, 2)
> z<-round(runif(10,-5,5))
> mapply(max,x,y,z)
[1] 4 0 -1 3 0 2 4 5 7 9
6. tapply
6.1 描述
用于對(duì)數(shù)據(jù)進(jìn)行分組應(yīng)用函數(shù)色难,其中 INDEX
參數(shù)可以把數(shù)據(jù)集進(jìn)行分組泼舱,相當(dāng)于 group by
的操作。
6.2 用法
tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
6.3 參數(shù)
-
X
: 向量 -
INDEX
: 用于分組的索引 -
FUN
: 自定義的調(diào)用函數(shù) -
...
: 接受多個(gè)數(shù)據(jù) -
simplify
: 是否簡(jiǎn)化為向量枷莉、矩陣或高維數(shù)組娇昙,當(dāng)值array
時(shí),輸出結(jié)果按數(shù)組進(jìn)行分組
6.4 示例
首先笤妙,讓我們創(chuàng)建一個(gè)因子數(shù)據(jù)作為索引
> (tdata <- as.data.frame(cbind(c(1,1,1,1,1,2,2,2,2,2), my.matrx)))
V1 V2 V3 V4
1 1 1 11 21
2 1 2 12 22
3 1 3 13 23
4 1 4 14 24
5 1 5 15 25
6 2 6 16 26
7 2 7 17 27
8 2 8 18 28
9 2 9 19 29
10 2 10 20 30
> colnames(tdata)
[1] "V1" "V2" "V3" "V4"
然后把第 1
列作為索引冒掌,并計(jì)算第 2
列的均值
> tapply(tdata$V2, tdata$V1, mean)
1 2
3 8
7. rapply
7.1 描述
rapply
是一個(gè)遞歸版本的 lapply
,只針對(duì) list
類(lèi)型的數(shù)據(jù)蹲盘,對(duì) list
的每個(gè)元素進(jìn)行遞歸遍歷股毫,如果 list
的子元素還包含數(shù)據(jù)則繼續(xù)遍歷。
7.2 用法
rapply(object, f, classes = "ANY", deflt = NULL, how = c("unlist", "replace", "list"), ...)
7.3 參數(shù)
object
:list
類(lèi)型數(shù)據(jù)f
: 自定義函數(shù)classes
: 匹配類(lèi)型,ANY
為所有類(lèi)型deflt
: 非匹配類(lèi)型的默認(rèn)值-
how
: 包含3
種方式:- 如果為
replace
召衔,則用調(diào)用函數(shù)f
后的結(jié)果來(lái)替換原list
中的元素铃诬; - 當(dāng)為
list
時(shí),創(chuàng)建一個(gè)新的list
并調(diào)用f
函數(shù),不匹配賦值為deflt
趣席; - 當(dāng)為
unlist
時(shí)兵志,會(huì)執(zhí)行一次unlist(recursive = TRUE)
的操作
- 如果為
...
: 可選參數(shù)
7.4 示例
假設(shè)我們有如下列表
> l
$x
$x$x
[1] 1 2 3 4 5
$x$y
[1] "a" "b" "c" "d" "e" "f" "g"
$y
[1] 7 8 9 10 11 12 13
$z
[1] "t" "u" "v" "w" "x" "y" "z"
> str(l)
List of 3
$ x:List of 2
..$ x: int [1:5] 1 2 3 4 5
..$ y: chr [1:7] "a" "b" "c" "d" ...
$ y: int [1:7] 7 8 9 10 11 12 13
$ z: chr [1:7] "t" "u" "v" "w" ...
我們想對(duì)數(shù)字進(jìn)行降序排序
> rapply(l, function(x) {sort(x, decreasing = TRUE)}, classes='integer',how='replace')
$x
$x$x
[1] 5 4 3 2 1
$x$y
[1] "a" "b" "c" "d" "e" "f" "g"
$y
[1] 13 12 11 10 9 8 7
$z
[1] "t" "u" "v" "w" "x" "y" "z"
我們?yōu)樽址砑有翘?hào),并將非字符串負(fù)值為 NA
> rapply(l, function(x) paste('*', x, '*'), classes='character',how='list', deflt = NA)
$x
$x$x
[1] NA
$x$y
[1] "* a *" "* b *" "* c *" "* d *" "* e *" "* f *" "* g *"
$y
[1] NA
$z
[1] "* t *" "* u *" "* v *" "* w *" "* x *" "* y *" "* z *"
8. eapply
8.1 描述
將函數(shù)應(yīng)用于環(huán)境中的命名變量中宣肚,并將結(jié)果作為列表返回想罕。
用戶(hù)可以請(qǐng)求使用所有命名對(duì)象(通常以點(diǎn)開(kāi)頭的名稱(chēng)不被使用),不會(huì)對(duì)輸出進(jìn)行排序霉涨,也不會(huì)搜索封閉環(huán)境
8.2 用法
eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE)
8.3 參數(shù)
-
env
: 使用的環(huán)境空間 -
FUN
: 自定義函數(shù) -
...
:FUN
的可選參數(shù) -
all.names
: 指示是否將函數(shù)應(yīng)用于所有值 -
USE.NAMES
: 如果 X 為字符串且未設(shè)置名稱(chēng)按价,當(dāng)該值為TRUE
時(shí)設(shè)置字符串為數(shù)據(jù)名稱(chēng),FALSE
不設(shè)置
8.4 示例
我們新建一個(gè)環(huán)境空間嵌纲,然后在空間內(nèi)新建 3
個(gè)對(duì)象俘枫,最后用 eapply
對(duì)所有環(huán)境空間內(nèi)的變量求均值
# 新建一個(gè)環(huán)境空間
> env <- new.env(hash = FALSE)
# 為環(huán)境空間添加變量
> env$a <- 1:10
> env$beta <- exp(-3:3)
> env$logic <- c(TRUE, FALSE, FALSE, TRUE)
# 對(duì) env 環(huán)境空間的所有變量求均值
> eapply(env, mean)
$logic
[1] 0.5
$beta
[1] 4.535125
$a
[1] 5.5
我們可以使用 ls()
函數(shù)獲取環(huán)境空間內(nèi)的所有變量或?qū)ο?/p>
# 獲取當(dāng)前環(huán)境空間內(nèi)的變量或?qū)ο?> ls() %>% head()
[1] "a" "A" "a2" "args1" "args2" "B"
# 通過(guò)傳入環(huán)境空間,獲取該環(huán)境空間內(nèi)的變量
> ls(env)
[1] "a" "beta" "logic"
獲取環(huán)境空間變量占用內(nèi)存大小
> eapply(env, object.size)
$logic
64 bytes
$beta
112 bytes
$a
96 bytes
一般你很少用到 eapply
逮走,但是對(duì)于 R
包開(kāi)發(fā)者來(lái)說(shuō)鸠蚪,環(huán)境空間是很重要的,需要掌握