R 數(shù)據(jù)處理(二十)—— apply

前言

在上一節(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)度為 10list,它并沒(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=FALSEUSE.NAMES=FALSE眯停,那么 sapplylapply 將是一樣的

> 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)境空間是很重要的,需要掌握

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末师溅,一起剝皮案震驚了整個(gè)濱河市茅信,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌墓臭,老刑警劉巖蘸鲸,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窿锉,居然都是意外死亡酌摇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)嗡载,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窑多,“玉大人,你說(shuō)我怎么就攤上這事洼滚」∠ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵遥巴,是天一觀(guān)的道長(zhǎng)千康。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铲掐,這世上最難降的妖魔是什么拾弃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮摆霉,結(jié)果婚禮上砸彬,老公的妹妹穿的比我還像新娘颠毙。我一直安慰自己,他們只是感情好砂碉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著刻两,像睡著了一般增蹭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磅摹,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天滋迈,我揣著相機(jī)與錄音,去河邊找鬼户誓。 笑死饼灿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帝美。 我是一名探鬼主播碍彭,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼悼潭!你這毒婦竟也來(lái)了庇忌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舰褪,失蹤者是張志新(化名)和其女友劉穎皆疹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體占拍,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡略就,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晃酒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片表牢。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖掖疮,靈堂內(nèi)的尸體忽然破棺而出初茶,到底是詐尸還是另有隱情,我是刑警寧澤浊闪,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布恼布,位于F島的核電站,受9級(jí)特大地震影響搁宾,放射性物質(zhì)發(fā)生泄漏折汞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一盖腿、第九天 我趴在偏房一處隱蔽的房頂上張望爽待。 院中可真熱鬧损同,春花似錦、人聲如沸鸟款。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)何什。三九已至组哩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間处渣,已是汗流浹背伶贰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罐栈,地道東北人黍衙。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像荠诬,于是被迫代替她去往敵國(guó)和親琅翻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容