【r<-迭代】使用apply函数簇

在实际使用R时,for循环往往是最后的选择。一般每次循环计算都是独立的,所以我们可以使用更简洁更方便的读写方式来实现同样的效果。

举例,如果使用for循环创建一个列表,包含3个相互独立、服从正态分布的随机向量,其长度由len指定:

len <- c(3, 4, 5)
x <- list()

for (i in 1:3){
    x[[i]] <- rnorm(len[i])
}

代码挺简单的,但能不能更简单?我们使用lapply()

lapply(len, rnorm)
## [[1]]
## [1]  0.286 -1.336 -0.746
## 
## [[2]]
## [1]  0.883  0.457 -1.038  0.874
## 
## [[3]]
## [1] -1.1929 -1.1251 -0.0642  2.6185 -0.7540

apply函数簇中的每个函数都称为高阶函数,高阶函数是以函数为输入的函数

lapply

lapply()接收一个向量和一个函数作为输入参数,它将这个函数应用到向量中的每一个元素,再将结果以列表的形式返回。

这类函数的好处是我们不需要构建一个显示的迭代器来明确迭代步骤的进行。

lapply()不仅适用于向量,也适用于列表。

假设我们有一份学生列表:

students <- list(
    a1 = list(name = "James", age = 25, 
              gender = "M", interest = c("reading", "writing")),
    a2 = list(name = "Jenny", age = 23,
              gender = "F", interest = c("cooking")),
    a3 = list(name = "David", age = 24,
              gender = "M", interest = c("running", "basketball"))
)

现在我们想创建一个字符向量,其中每个元素都由如下形式:

James, 25 year-old man, loves reading, writing.

函数sprintf()通过将占位符替换为相应的输入参数来格式化文本(取自C)。举例:

sprintf("Hello, %s! Your number is %d.", "Tom", 3)
## [1] "Hello, Tom! Your number is 3."

返回我们的问题,我们使用lapply()解决:

lapply(students, function(s){
    type <- switch(s$gender, "M" = "man", "F" = "woman")
    interest <- paste(s$interest, collapse = ", ")
    sprintf("%s, %d year-old %s, loves %s.", s$name, s$age, type, interest)
})
## $a1
## [1] "James, 25 year-old man, loves reading, writing."
## 
## $a2
## [1] "Jenny, 23 year-old woman, loves cooking."
## 
## $a3
## [1] "David, 24 year-old man, loves running, basketball."

sapply

列表并非总是最佳的数据存储容器,有时候我们希望将结果存放在向量或矩阵中,sapply()可以根据结果的结构进行合理简化。

比如,我们如果将平方运算应用到1:10的每个元素,使用lapply()会得到含10个元素的列表,这非常不直观,也常常不是我们想要的,而sapply()可以将其简化为一个向量。

sapply(1:10, function(x) x ^ 2)
##  [1]   1   4   9  16  25  36  49  64  81 100

如果函数每次循环返回一个多元素的向量,sapply()会将结果存储在一个矩阵中,矩阵的每一列是每次循环产生的向量:

sapply(1:10, function(x) c(x, x^2))
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,]    1    2    3    4    5    6    7    8    9    10
## [2,]    1    4    9   16   25   36   49   64   81   100

vapply

使用sapply有时候会暗藏风险,假设我们有列表

x <- list(c(1, 2), c(2, 3), c(1, 3))

我们想得到一个向量,其中每个元素都是x元素对应数字的平方,那么sapply()就比较适用。

sapply(x, function(x) x ^ 2)
##      [,1] [,2] [,3]
## [1,]    1    4    1
## [2,]    4    9    9

如果输入数据有错误,可能会返回意料之外的结果:

x1 <- list(c(1, 2), c(2, 3), c(1, 3, 3))

sapply(x1, function(x) x^2)
## [[1]]
## [1] 1 4
## 
## [[2]]
## [1] 4 9
## 
## [[3]]
## [1] 1 9 9

但如果我们使用vapply(),我们可以设定返回值的模板,以验证结果形式上是不是出问题了。

vapply(x1, function(x) x^2, numeric(2))
## Error in vapply(x1, function(x) x^2, numeric(2)): 值的长度必需为2,
##  但FUN(X[[3]])结果的长度却是3

而对于正确的输入,vapply()sapply()结果一致。

vapply(x, function(x) x^2, numeric(2))
##      [,1] [,2] [,3]
## [1,]    1    4    1
## [2,]    4    9    9

到此,我们已经知道vapply其实就是sapply的安全升级版本。

mapply

相比lapply()sapply()在一个向量上迭代,mapply()可以在多个向量上进行迭代。mapplysapply的多元版本。

mapply(function(a, b, c) a * b + b * c + a * c, 
       a = c(1, 2, 3), b = c(5, 6, 7), c = c(-1, -2 , -3))
## [1] -1 -4 -9

迭代函数可以返回标量,也可以返回多元素向量。

Map()lapply()的多元版本,它通常返回列表。

Map(function(a, b, c) a * b + b * c + a * c, 
       a = c(1, 2, 3), b = c(5, 6, 7), c = c(-1, -2 , -3))
## [[1]]
## [1] -1
## 
## [[2]]
## [1] -4
## 
## [[3]]
## [1] -9

最后,apply作为最常用的函数不在此讲述了,之前的笔记有几篇详细讲过这方面。需要注意的是,在使用apply函数对数据框进行计算时,是不能提取行/列名的。

mat <- data.frame(a = c(1, 2, 3), b = c(5, 6, 7), c = c(-1, -2 , -3))
rownames(mat) <- c("r1", "r2", "r3")
mat
##    a b  c
## r1 1 5 -1
## r2 2 6 -2
## r3 3 7 -3
apply(mat, 1, function(x) rownames(x))
## NULL
apply(mat, 1, function(x) colnames(x))
## NULL
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343