vlambda博客
学习文章列表

数据科学04 | R语言程序设计-循环函数

R中主要的循环函数(loop functions)有 lapply( )、sapply( )、apply( )、tapply( ) 和 mapply( )。 

lapply( )

lapply( )对列表中每个元素,包括各种类型的对象(向量、矩阵、数据框等),运用函数,返回一个新的列表。

lapplyfunction (X, FUN, ...) { FUN <- match.fun(FUN) if (!is.vector(X) || is.object(X))  X <- as.list(X)  .Internal(lapply(X, FUN))}<bytecode: 0x7fde85867b90><environment: namespace:base>
  1. 参数X是一个列表,X不是列表时会被as.list( )强制转化为列表,如果不能转化将报错

  2. 参数FUN,对列表里每个元素做运算的函数

  3. 其余的参数可用参数...传递给函数FUN

x <- list(a = 1:4, b = rnorm(10), c = rnorm(20, 1), d = rnorm(100, 5)) lapply(x, mean)$a[1] 2.5
$b[1] 0.3474802
$c[1] 0.937003
$d[1] 4.957727#返回列表和原列表有相同的元素名a和b
x<-1:4lapply(x, runif, min = 0, max = 10)[[1]][1] 1.621091
[[2]][1] 4.75397920 0.01932835
[[3]][1] 4.414591 2.609297 9.384137
[[4]][1] 7.158333 1.630855 4.761880 6.902567#runif(x)函数生成x个服从均匀分布的随机变量组成的向量#对x调用lapply(),给runif()函数传递参数,指定生成介于0到10之间的随机数

lapply( ) 和相关循环函数可以传入匿名函数给参数FUN,不需要定义函数名称

例:提取矩阵的第一列  

x <- list(a = matrix(1:4, 2, 2), b = matrix(1:6, 3, 2)) x$a [,1] [,2][1,] 1 3[2,] 2 4
$b [,1] [,2][1,] 1 4[2,] 2 5[3,] 3 6
lapply(x, function(elt) elt[,1])$a[1] 1 2
$b[1] 1 2 3

sapply( )

sapply( )可以简化lapply( )的结果。  

  • 如果结果列表中每个元素长度都为1,sapply( ) 返回一个包含所有元素的向量  

  • 如果结果列表中每个元素都是等长的向量, sapply( ) 返回矩阵

  • 如果无法简化对象,sapply( ) 直接返回列表  

x <- list(a = 1:4, b = rnorm(10), c = rnorm(20, 1), d = rnorm(100, 5)) lapply(x, mean)$a[1] 2.5
$b[1] 0.2616772
$c[1] 1.054757
$d[1] 5.055208
sapply(x, mean) a b c d 2.5000000 0.2616772 1.0547574 5.0552082
mean(x)[1] NAWarning message:In mean.default(x) : 参数不是数值也不是逻辑值:回覆NA#mean函数不适用于列表,对列表本身使用mean()函数将报错

apply( )

apply( )对数组的各个维度(如行或列)进行运算。

str(apply)function (X, MARGIN, FUN, ...)  

1. 参数x是一个数组;
2. 参数MARGIN是整数向量,指明对数组的运用函数的维度
    如MARGIN=1表示对二维数组的行运算,2表示对二维数组的列进行运算

x <- matrix(rnorm(200), 20, 10) #创建了一个20行10列的矩阵apply(x, 2, mean) #对矩阵中每列求平均值[1] 0.07982867 0.05471546 0.04272418 -0.07888251 -0.43758027 0.12137198 0.05869929[8] -0.24674699 0.21602219 -0.33674517#得到一个长度为10的向量,这个向量是矩阵中每个列的平均值
apply(x, 1, sum) #对每行求和 [1] 1.3538445 -7.9108873 0.4657281 -2.2937298 -4.8767536 -5.6521484 2.1168591 -3.8917693 [9] -2.3622401 5.3025761 -4.2599949 2.3621288 -0.8809210 -2.6845400 4.9910454 -0.2658782[17] 5.4142552 -1.7345767 -1.2493809 5.5245197

可以直接使用经过优化的专用函数求和、求平均值:

  • rowSums = apply(x, 1, sum)

  • rowMeans = apply(x, 1, mean)

  • colSums = apply(x, 2, sum)

  • colMeans = apply(x, 2, mean)  

例:对数组求平均值  

a[,,1] [,1] [,2][1,] -1.138137 0.4264642[2,] 1.253815 -0.2950715
apply(a, c(1, 2), mean) #保留数组的第1、2维求平均值 [,1] [,2][1,] -0.1066356 0.1550131[2,] -0.2499600 0.1434852
rowMeans(a, dims = 2) [,1] [,2][1,] -0.1066356 0.1550131[2,] -0.2499600 0.1434852

mapply( )

mapply( ) 以一种并行的方式对多个列表的元素进行运算,是lapply( )的多变量版本。

str(mapply)function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
  1. ... 参数接收传递多个列表数据作为参数传递给FUN

  2. MoreArgs::参数列表  

  3. SIMPLIFY:设置结果是否要被简化

  4. USE.NAMES:如果X为字符串,TRUE设置字符串为数据名

list(rep(1, 4), rep(2, 3), rep(3, 2), rep(4, 1))[[1]][1] 1 1 1 1
[[2]][1] 2 2 2
[[3]][1] 3 3
[[4]][1] 4#用rep()将1重复4次,将2重复3次,将3重复2次,将4重复1次输入#list()将所有结果以列表形式返回
mapply(rep, 1:4, 4:1)

mapply( )函数依次将从1到4作为第一个参数,从4到1作为第二个参数传递给rep( )函数,返回相同的结果。

例:应用mapply( )生成一个均值为1的随机变量,两个均值为2的随机变量,三个均值为3的随机变量等等

#定义noise()函数生成一些正态随机噪音,有三个参数:观测数、均值以及标准差noise <- function(n, mean, sd) {  rnorm(n, mean, sd)} noise(5, 1, 2)[1] 0.6441040 0.1480373 2.9933176 2.4553214 -2.4532612#生成5个均值为1,标准差为2的正态随机变量
noise(1:5, 1:5, 2)[1] 1.7067970 3.4536273 4.3365220 -0.8486346 4.5292851 #向noise()传递一个由参数组成的向量,会得到一个长度为5的向量,无法得到预期结果
mapply(noise, 1:5, 1:5, 2)[[1]][1] 4.959267
[[2]][1] 3.593589 -1.418552
[[3]][1] -0.3273374 3.9822191 2.6518890
[[4]][1] 5.922581 4.587653 4.161999 4.367324
[[5]][1] 5.332510 2.460802 9.698987 2.175989 4.966077

mapply( )函数依次将三个参数 1:5、1:5、2传递给noise( )函数运算得到预期结果。

tapply( )

tapply( ),table apply( ) 的缩写,将函数应用于向量的子集。

str(tapply)function (X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)
  1. X是一个数值或者其它类型的向量  

  2. INDEX是另一个长度与第一个向量相同的向量,用来表明第一个向量中的各元素分别属于哪一组

  3. simplify设置结果是否要被简化

x <- c(rnorm(10), runif(10), rnorm(10, 1)) #生成10个均值为0的正态随机变量、10个均匀随机变量以及10个均值为1的正态随机变量
f<-gl(3,10) f[1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3Levels: 1 2 3#gl()函数创建因子变量,因子变量有三个水平,每个水平会重复十次,表明观测值分别属于哪个组
tapply(x, f, mean) #将x按分组子集求均值 1 2 3 0.3586938 0.6950377 0.7174120
tapply(x, f, mean, simplify = FALSE)$`1`[1] 0.3586938
$`2`[1] 0.6950377
$`3`[1] 0.717412#不简化结果得到的是一个列表
#可以计算一些更加复杂的统计量,计算观测值的范围tapply(x, f, range)$`1`[1] -1.042507 2.236323
$`2`[1] 0.01919943 0.99887748
$`3`[1] -1.371023 1.933389

split( )

split( )将对象分组,和lapply( )或sapply( )结合应用。

str(split)function (x, f, drop = FALSE, ...)    
  1. x为一个向量或列表、数据框

  2. f为因子变量

  3. drop指示空因子水平是否去掉  

#加载airquality数据集library(datasets) head(airquality) Ozone Solar.R Wind Temp Month Day1 41 190 7.4 67 5 12 36 118 8.0 72 5 23 12 149 12.6 74 5 34 18 313 11.5 62 5 45 NA NA 14.3 56 5 56 28 NA 14.9 66 5 6
#将数据框按因子变量Month分组s <- split(airquality, airquality$Month)#联合lapply()按月计算平均值lapply(s, function(x) colMeans(x[, c("Ozone", "Solar.R", "Wind")]))$`5` Ozone Solar.R Wind NA NA 11.62258
$`6` Ozone Solar.R Wind NA 190.16667 10.26667
$`7` Ozone Solar.R Wind NA 216.483871 8.941935
$`8` Ozone Solar.R Wind NA NA 8.793548
$`9` Ozone Solar.R Wind NA 167.4333 10.1800 #Ozone和Solar.R变量有缺失值,导致无法计算平均值
#调用sapply()简化返回的结果sapply(s, function(x) colMeans(x[, c("Ozone", "Solar.R", "Wind")])) 5 6 7 8 9Ozone NA NA NA NA NASolar.R NA 190.16667 216.483871 NA 167.4333Wind 11.62258 10.26667 8.941935 8.793548 10.1800
#给colMeans()传递na.rm参数,在计算平均值之前移除每列的缺失值sapply(s, function(x) colMeans(x[, c("Ozone", "Solar.R", "Wind")], na.rm = TRUE)) 5 6 7 8 9Ozone 23.61538 29.44444 59.115385 59.961538 31.44828Solar.R 181.29630 190.16667 216.483871 171.857143 167.43333Wind 11.62258 10.26667 8.941935 8.793548 10.18000

观察不同因子组合产生的不同水平:

x <- rnorm(10) f1<-gl(2,5#2个因子水平,重复5次f2<-gl(5,2) #5个因子水平,重复2次f1 [1] 1 1 1 1 1 2 2 2 2 2Levels: 1 2f2 [1] 1 1 2 2 3 3 4 4 5 5Levels: 1 2 3 4 5
interaction(f1, f2) [1] 1.1 1.1 1.2 1.2 1.3 2.3 2.4 2.4 2.5 2.5Levels: 1.1 2.1 1.2 2.2 1.3 2.3 1.4 2.4 1.5 2.5#interaction()函数可以组合所有的因子水平,有10种不同水平的组合
#向split()函数直接传递一个包含两个因子变量的列表,会自动调用interaction()str(split(x, list(f1, f2)))List of 10 $ 1.1: num [1:2] 0.922 2.05 $ 2.1: num(0) $ 1.2: num [1:2] -0.491 -2.309 $ 2.2: num(0) $ 1.3: num 1.01 $ 2.3: num -0.709 $ 1.4: num(0) $ 2.4: num [1:2] -0.688 1.026 $ 1.5: num(0) $ 2.5: num [1:2] -0.285 -1.221#尽管有10种不同水平的组合,但不一定会在每个水平上都进行观测,某些水平会没有观测值
#drop=TRUE去掉所有在分组过程中观测值为空的因子水平str(split(x, list(f1, f2), drop = TRUE))List of 6 $ 1.1: num [1:2] 0.922 2.05 $ 1.2: num [1:2] -0.491 -2.309 $ 1.3: num 1.01 $ 2.3: num -0.709 $ 2.4: num [1:2] -0.688 1.026 $ 2.5: num [1:2] -0.285 -1.221


编辑:李雪纯 冯文清
校审:张健 罗鹏