R语言基础系列:
apply函数家族是R语言中数据处理的一组核心函数,通过使用apply函数,我们可以实现对数据的循环、分组、过滤、类型控制等操作。
apply函数本身是用来解决数据循环处理的问题,为了面向不同的数据类型,不同的返回值,apply函数组成了一个函数加族,包括了8个功能类似的函数。
下面将分别介绍这几个函数的定义和使用方法。
1. apply函数
apply函数是最常用的代替for循环的函数。apply函数可以对
矩阵
、
数据框
、
数组
(二维、多维),
,对子元素进行迭代,并把子元素以参数传递的形式给自定义的FUN函数中,并以返回计算结果。
1.1 函数定义:
apply(X, MARGIN, FUN, ...)
1.2 参数列表:
X:数组、矩阵、数据框
MARGIN:按行计算或按按列计算,1表示按行,2表示按列
FUN:自定义的调用函数
…:更多参数,可选
1.3 应用:
# 对一个矩阵的每一列求和
# 生成data.frame
> x <- cbind(x1 = 3, x2 = c(4:1, 2:5)); x
x1 x2
[1,] 3 4
[2,] 3 3
[3,] 3 2
[4,] 3 1
[5,] 3 2
[6,] 3 3
[7,] 3 4
[8,] 3 5
apply(x,2,sum)
# x1 x2
# 24 24
apply(x,1,mean)
# [1] 3.5 3.0 2.5 2.0 2.5 3.0 3.5 4.0
2. lapply函数
lapply函数是一个最基础循环操作函数之一,用来对list
、data.frame
数据集进行循环,并作为结果集,通过lapply的开头的第一个字母’l’就可以判断返回结果集的类型。
2.1 函数定义
lapply(X, FUN, ...)
2.2 参数列表
X:list、data.frame数据
FUN:自定义的调用函数
…:更多参数,可选
2.3 应用
输入list,返回对每个list进行操作后的list。
# 构建一个list数据集x,分别包括a,b,c 三个KEY值。
x <- list(a = 1:10, b = rnorm(6,10,5), c = c(TRUE,FALSE,FALSE,TRUE));x
# [1] 1 2 3 4 5 6 7 8 9 10
# [1] 9.186837 7.372569 9.942975 16.946282 2.550059
# [6] 6.991188
# [1] TRUE FALSE FALSE TRUE
# 分别计算每个KEY对应该的数据的分位数。
lapply(x,fivenum)
# [1] 1.0 3.0 5.5 8.0 10.0
# [1] 2.550059 6.991188 8.279703 9.942975 16.946282
# [1] 0.0 0.0 0.5 1.0 1.0
如果输入的是数据框,lapply会自动把数据框按列进行分组,再进行计算。
lapply(data.frame(x), sum)
[1] 12
[1] 12
lapply可以很方便地对list数据集中个每个list进行循环操作,还可以用data.frame数据集按列进行循环,但如果传入的数据集是一个向量或矩阵对象,那么直接使用lapply就不能达到想要的效果了。lapply会分别循环矩阵中的每个值,而不是按行或按列进行分组计算。
x <- cbind(x1=3, x2=c(2:1,4:5))
x; class(x)
# x1 x2
# [1,] 3 2
# [2,] 3 1
# [3,] 3 4
# [4,] 3 5
# [1] "matrix" "array"
lapply(x, sum)
# [[1]]
# [1] 3
# [[2]]
# [1] 3
# [[3]]
# [1] 3
# [[4]]
# [1] 3
# [[5]]
# [1] 2
# [[6]]
# [1] 1
# [[7]]
# [1] 4
# [[8]]
# [1] 5
3. sapply函数
sapply函数是一个简化版的lapply,sapply增加了2个参数simplify和USE.NAMES,主要就是让输出看起来更友好,与lapply不同的是,,而不是list对象。
3.1 函数定义
sapply(X, FUN, ..., simplify=TRUE, USE.NAMES = TRUE)
3.2 参数列表
X:数组、矩阵、数据框
FUN:自定义的调用函数
…:更多参数,可选
simplify:是否数组化,当设置simplify='array'时,输出结果按数组进行分组
USE.NAMES:如果X为字符串,TRUE设置字符串为数据名,FALSE不设置
3.3 应用
x <- cbind(x1=3, x2=c(2:1,4:5))
# 对矩阵计算,计算过程同lapply函数
sapply(x, sum)
# [1] 3 3 3 3 2 1 4 5
# 对数据框计算
sapply(data.frame(x), sum)
# x1 x2
# 12 12
# 检查结果类型,sapply返回类型为向量,而lapply的返回类型为list
class(lapply(x, sum))
# [1] "list"
class(sapply(x, sum))
# [1] "numeric"
如果simplify=FALSE和USE.NAMES=FALSE,那么完全sapply函数就等于lapply函数了。
sapply(data.frame(x), sum, simplify=FALSE, USE.NAMES=FALSE)
# $x1
# [1] 12
# $x2
# [1] 12
对于simplify为array时,我们可以参考下面的例子,构建一个三维数组,其中二个维度为方阵。
a<-1:2
# 按数组分组
sapply(a,function(x) matrix(x,2,2), simplify='array')
# , , 1
# [,1] [,2]
# [1,] 1 1
# [2,] 1 1
# , , 2
# [,1] [,2]
# [1,] 2 2
# [2,] 2 2
# 默认情况,则自动合并分组
sapply(a,function(x) matrix(x,2,2))
# [,1] [,2]
# [1,] 1 2
# [2,] 1 2
# [3,] 1 2
# [4,] 1 2
对于字符串的向量,还可以自动生成数据名。
val<-head(letters)
# 默认设置数据名
sapply(val,paste,USE.NAMES=TRUE)
# a b c d e f
# "a" "b" "c" "d" "e" "f"
# USE.NAMES=FALSE,则不设置数据名
sapply(val,paste,USE.NAMES=FALSE)
# [1] "a" "b" "c" "d" "e" "f"
4. tapply函数
tapply用于分组的循环计算,通过INDEX参数(分类变量)对数据集X进行分组并计算分组计算,相当于group by的操作。
4.1 函数定义
tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
4.2 参数列表
INDEX:用于分组的索引
FUN:自定义的调用函数
…:接收多个数据
simplify:是否数组化,当值array时,输出结果按数组进行分组
4.3 应用
#计算不同品种(Species)的鸢尾花(iris)的花瓣长度(Petal.Length)的均值。
tapply(X = iris$Petal.Length,INDEX = iris$Species,FUN = mean)
# setosa versicolor virginica
# 1.462 4.260 5.552
5. mapply函数
mapply也是sapply的变形函数,类似多变量的sapply,但是参数定义有些变化。第一参数为自定义的FUN函数,第二个参数’…’可以接收多个数据,作为FUN函数的参数调用。
5.1 函数定义
mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE,USE.NAMES = TRUE)
5.2 参数列表
FUN:自定义的调用函数
…:接收多个数据
MoreArgs:参数列表
SIMPLIFY:是否数组化,当值array时,输出结果按数组进行分组
USE.NAMES:如果X为字符串,TRUE设置字符串为数据名,FALSE不设置
5.3 应用
比如,比较3个向量大小,按索引顺序取较大的值。
set.seed(1)
# 定义3个向量
x<-1:10
y<-5:-4
z<-round(runif(10,-5,5))
# 按索引顺序取较大的值。
mapply(max,x,y,z)
# [1] 5 4 3 4 5 6 7 8 9 10
再看一个例子,生成4个符合正态分布的数据集,分别对应的均值和方差为c(1,10,100,1000)。
set.seed(1)
# 长度为4
n<-rep(4,4)
# m为均值,v为方差
m<-v<-c(1,10,100,1000)
# 生成4组数据,按列分组
mapply(rnorm,n,m,v)
# [,1] [,2] [,3] [,4]
# [1,] 0.3735462 13.295078 157.57814 378.7594
# [2,] 1.1836433 1.795316 69.46116 -1214.6999