R 作为一种向量化的编程语言,一大特征便是以向量计算替代了循环计算,使效率大大提升。apply函数族正是为解决数据循环处理问题而生的 —— 面向不同数据类型,生成不同返回值的包含8个相关函数的函数族。

为何要用apply?

在使用 R 时,要尽量用 array 的方式思考,避免 for 循环,写过多的 for 循环代码,最后把 R 代码写的跟 C 似得说明你没有进入 R 的思考方式,是一种费力不讨好的行为。那么不用循环怎么实现迭代呢?apply函数族是一把利器,它不是一个函数,而是一族功能类似的函数。


语法详解

apply

apply-函数定义:在 X 上,沿 margin 方向,依次调用 FUN

apply(X, margin, FUN, ...)

参数列表:
X:数组、矩阵、数据框
margin:按维度运算,1表示按行,2表示按列,c(1,3)表示第1、3维
FUN:要使用的函数

  • eg1-矩阵按列求和
> mat <- matrix(1:12, 3, 4)
> mat
[,1] [,2] [,3] [,4]
[1,] 1 4 7 10
[2,] 2 5 8 11
[3,] 3 6 9 12 > apply(mat, 2, sum)
[1] 6 15 24 33
  • eg2-数组第1、3维度组合求和
> ary <- array(1:12, c(2,3,2))
> ary
, , 1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
, , 2
[,1] [,2] [,3]
[1,] 7 9 11
[2,] 8 10 12 > apply(ary, c(1,3), sum)
[,1] [,2]
[1,] 9 27
[2,] 12 30
  • eg3-数据框按列求均值
> data <- data.frame(x1=1:5, x2=6:10)
> data
x1 x2
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10 > apply(data, 2, mean)
x1 x2
3 8

tapply

tapply-函数定义:按 INDEX 值分组,相同值对应下标的 X 中的元素形成一个集合,应用到 FUN

tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)

参数列表:
X:向量、数组
INDEX:用于分组的索引
FUN:要使用的函数
simplify : 是否数组化,当值TRUE时,输出结果按数组进行分组输出

  • eg1-当FUN为NULL,返回分组的结果,返回值中相等的元素所对应的下标属于同一组
> x <- 1:6
> INDEX <- c('a','a','b','c','c','c')
> tapply(x, INDEX)
[1] 1 1 2 3 3 3
  • eg2-向量按 INDEX 分组求和
> tapply(x, INDEX, sum)
a b c
3 3 15

eg3-矩阵按 INDEX 分组求均值

> mat <- matrix(1:10, 2)
> mat
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 5 7 9
[2,] 2 4 6 8 10
> INDEX <- matrix(c(rep(1,5), rep(2,5)), nrow=2)
> INDEX
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 1 2 2
[2,] 1 1 2 2 2
> tapply(mat, INDEX)
[1] 1 1 1 1 1 2 2 2 2 2
> tapply(mat, INDEX, mean)
1 2
3 8

lapply

lapply-函数定义:在 X 上逐个元素调用 FUN, 返回和 X 等长的 list 作为结果集

lapply(X, FUN, ...)

参数列表:
X:列表、向量、数据框
FUN:要使用的函数

  • eg1-计算 list 中的每个 KEY 对应数据的均值
> lst <- list(a=1:10, b=seq(0,7,2), c=c(2,5,8))
> lst
$a
[1] 1 2 3 4 5 6 7 8 9 10
$b
[1] 0 2 4 6
$c
[1] 2 5 8 > lapply(lst, mean)
$a
[1] 5.5
$b
[1] 3
$c
[1] 5
  • eg2-对数据框的列求和
> data <- data.frame(x1=1:5, x2=6:10)
> data
x1 x2
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
> lapply(data, sum)
$x1
[1] 15
$x2
[1] 40
  • eg3-找出闰年:对向量内各元素依次调用函数
> isLeapYear <- function(a){
+ if( (a%%4==0 & a%/%100!=0) | a%%400==0 )
+ a
+ }
> a <- 1900:1910
> unlist(lapply(a, isLeapYear))
[1] 1900 1904 1908

rapply

rapply-函数定义:递归版lapply,对list遍历直至无list,最终非list元素若类型是classes参数指定的类型,则调用FUN

rapply(list, f, classes = "ANY", deflt = NULL,how = c("unlist", "replace", "list"), ...)

参数列表:
list:列表
f:要使用的函数
classes: 匹配类型, ANY为所有类型
deflt: 非匹配类型的默认值
how: 3种操作方式,

  • replace:则用调用f后的结果替换原list中原来的元素;
  • list:新建一个list,类型匹配调用f函数,不匹配赋值为deflt;
  • unlist:执行一次unlist(recursive = TRUE)操作
  • eg1-遍历 list 分组求和
> lst <- list(a=list(aa=c(1:5), ab=c(6:10)), b=list(ba=c(1:10)))
> lst
$a
$a$aa
[1] 1 2 3 4 5
$a$ab
[1] 6 7 8 9 10
$b
$b$ba
[1] 1 2 3 4 5 6 7 8 9 10 > rapply(lst, sum, how="replace")
$a
$a$aa
[1] 15
$a$ab
[1] 40
$b
$b$ba
[1] 55 > rapply(lst, sum, how="unlist") # 输出结果为vector
a.aa a.ab b.ba
15 40 55

sapply

sapply-函数定义:简化版lapply,增加参数simplify和USE.NAMES,可设定输出类型

sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)

参数列表:
X:列表、向量、数据框
FUN:要使用的函数
simplify: 若FALSE,等价于lapply。否则,将lapply输出的list简化为vector或matrix
USE.NAMES: 如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

  • eg1-simplify参数设定输出类型
> lst <- list(a=c(1:5), b=c(6:10))
> sapply(lst, sum, simplify = F) # 输出list
$a
[1] 15
$b
[1] 40 > sapply(lst, sum) # 输出vector
a b
15 40 > sapply(lst, fivenum) # 输出matrix
a b
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
  • eg2-USE.NAMES参数作用
> val <- head(letters)
> val
[1] "a" "b" "c" "d" "e" "f"
> sapply(val, paste)
a b c d e f
"a" "b" "c" "d" "e" "f"
> sapply(val, paste, USE.NAMES = F)
[1] "a" "b" "c" "d" "e" "f"

vapply

vapply-函数定义:类似sapply,但提供参数FUN.VALUE用以设定返回值的行名

vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)

参数列表:
X:列表、数据框
FUN:要使用的函数
FUN.VALUE:定义返回值的行名row.names
USE.NAMES: 如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

  • eg1-FUN.VALUE参数设置返回值行名
> lst <- list(a=c(1:5), b=c(6:10))
> res <- vapply(lst, function(x) c(min(x), max(x)), c(min.=0, max.=0))
> res
a b
min. 1 6
max. 5 10
  • eg2-对数据框的数据累计求和,并对每一行设置行名row.names
> data <- data.frame(cbind(x1=3, x2=c(2:1,4:5)))
> data
x1 x2
1 3 2
2 3 1
3 3 4
4 3 5 > vapply(data, cumsum, FUN.VALUE=c('a'=0,'b'=0,'c'=0,'d'=0))
x1 x2
a 3 2
b 6 3
c 9 7
d 12 12

mapply

mapply-函数定义:多变量版sapply,将FUN应用于多个同结构数据第一个元素组成的数组,然后是第二个元素组成的数组,依此类推

mapply(FUN, ..., MoreArgs=NULL, SIMPLIFY=TRUE, USE.NAMES=TRUE)

参数列表:
FUN:要使用的函数
…: 接收多个数据(list、vector)
MoreArgs: FUN的参数列表
simplify: 若FALSE,输出list。否则,将输出的list简化为vector或matrix
USE.NAMES: 如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

  • eg1-输入两个list并分组求和
> mapply(sum, list(a=1,b=2,c=3), list(a=10,b=20,d=30))
a b c
11 22 33
  • eg2-比较两个向量大小,按索引顺序取较大的值
> a <- 1:10
> b <- 5:-4
> a
[1] 1 2 3 4 5 6 7 8 9 10
> b
[1] 5 4 3 2 1 0 -1 -2 -3 -4
> mapply(max, a, b)
[1] 5 4 3 4 5 6 7 8 9 10
  • eg3-输入向量,返回值多个时返回matrix
> mapply(function(x,y) c(x+y, x^y, x-y), c(1:5), c(1:5))
[,1] [,2] [,3] [,4] [,5]
[1,] 2 4 6 8 10
[2,] 1 4 27 256 3125
[3,] 0 0 0 0 0

eapply

eapply-函数定义:对一个环境空间中的所有变量进行遍历

eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE)

参数列表:
env: 环境空间
FUN:要使用的函数
all.names: 匹配类型, ANY为所有类型
USE.NAMES: 如果X为字符串,TRUE设置字符串为数据名,FALSE不设置

  • eg-eapply操作示例
> # 定义一个环境空间
> env <- new.env()
> # 向这个环境空间中存入3个变量
> env$a <- 1:10
> env$b <- exp(-3:3)
> env$logic <- c(TRUE, FALSE, FALSE, TRUE) > ls(env) # 查看env空间中的变量
[1] "a" "b" "logic"
> ls.str(env) # 查看env空间中的变量字符串结构
a : int [1:10] 1 2 3 4 5 6 7 8 9 10
b : num [1:7] 0.0498 0.1353 0.3679 1 2.7183 ...
logic : logi [1:4] TRUE FALSE FALSE TRUE > eapply(env, mean) # 计算env环境空间中所有变量的均值
大专栏  数据操作-apply函数族perator">$a
[1] 5.5
$b
[1] 4.535125
$logic
[1] 0.5

应用及拓展

应用展示

原始数据为按年份year、地区loc和商品类别type进行统计的销售量。我们要制作两个销售总量的crosstable,一个以年份为行、地区为列,一个以年份为行,类别为列。

  • 应用1-tapply实现crosstable功能
> df <- data.frame(year=kronecker(2001:2003, rep(1,4)),
loc=c('beijing','beijing','shanghai','shanghai'),
type=rep(c('A','B'),6), sale=rep(1:12))
> df
year loc type sale
1 2001 beijing A 1
2 2001 beijing B 2
3 2001 shanghai A 3
4 2001 shanghai B 4
5 2002 beijing A 5
6 2002 beijing B 6
7 2002 shanghai A 7
8 2002 shanghai B 8
9 2003 beijing A 9
10 2003 beijing B 10
11 2003 shanghai A 11
12 2003 shanghai B 12 > tapply(df$sale, df[,c('year','loc')], sum)
loc
year beijing shanghai
2001 3 7
2002 11 15
2003 19 23 > tapply(df$sale, df[,c('year','type')], sum)
type
year A B
2001 4 6
2002 12 14
2003 20 22
  • 应用2-mapply使两个嵌套列表对应项相加
> list1 <- list(a=1:5, b=list(c=1:4, d=5:9))
> list1
$a
[1] 1 2 3 4 5
$b
$b$c
[1] 1 2 3 4
$b$d
[1] 5 6 7 8 9 > list2 <- list(a=1:5, b=list(c=5:8, d=1:5))
> list2
$a
[1] 1 2 3 4 5
$b
$b$c
[1] 5 6 7 8
$b$d
[1] 1 2 3 4 5 > "%+%" <- function(x,y) mapply("+", x, y)
> mapply("%+%", list1, list2)
$a
[1] 2 4 6 8 10
$b
$b$c
[1] 6 8 10 12
$b$d
[1] 6 8 10 12 14

相关函数

by

by-函数定义:by可以当成data.frame上的tapply,在数据框行上施用索引分组运算

by(data, INDICES, FUN, ..., simplify = TRUE)

参数列表:
data: 数据框
INDICES:与数据框行数等长的用于分组的索引
FUN:要使用的函数

  • eg-by对数据框进行按行索引分组计算
> data <- data.frame(a=c(1:5), b=c(6:10))
> data
a b
1 1 6
2 2 7
3 3 8
4 4 9
5 5 10
> INDICES <- c(1,1,2,2,2) > by(data, INDICES, colMeans)
INDICES: 1
a b
1.5 6.5
-------------------------------------------------------------------------------
INDICES: 2
a b
4 9
> by(data, INDICES, rowMeans)
INDICES: 1
1 2
3.5 4.5
-------------------------------------------------------------------------------
INDICES: 2
3 4 5
5.5 6.5 7.5

outer

outer-函数定义:作用于数组的类似于矩阵外积运算方式的运算函数

outer(X, Y, FUN = "*", ...)

参数列表:
X、Y: 向量、数组
FUN:当为空时即为外积运算,否则为将FUN代替外积运算符进行类似外积的运算操作

  • eg-outer的使用
> x <- 1:4; y <- 2:4
> x; y
[1] 1 2 3 4
[1] 2 3 4
> outer(x, y)
[,1] [,2] [,3]
[1,] 2 3 4
[2,] 4 6 8
[3,] 6 9 12
[4,] 8 12 16 > month.abb
[1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
> outer(month.abb, 1999:2003, FUN = "paste")
[,1] [,2] [,3] [,4] [,5]
[1,] "Jan 1999" "Jan 2000" "Jan 2001" "Jan 2002" "Jan 2003"
[2,] "Feb 1999" "Feb 2000" "Feb 2001" "Feb 2002" "Feb 2003"
[3,] "Mar 1999" "Mar 2000" "Mar 2001" "Mar 2002" "Mar 2003"
[4,] "Apr 1999" "Apr 2000" "Apr 2001" "Apr 2002" "Apr 2003"
[5,] "May 1999" "May 2000" "May 2001" "May 2002" "May 2003"
[6,] "Jun 1999" "Jun 2000" "Jun 2001" "Jun 2002" "Jun 2003"
[7,] "Jul 1999" "Jul 2000" "Jul 2001" "Jul 2002" "Jul 2003"
[8,] "Aug 1999" "Aug 2000" "Aug 2001" "Aug 2002" "Aug 2003"
[9,] "Sep 1999" "Sep 2000" "Sep 2001" "Sep 2002" "Sep 2003"
[10,] "Oct 1999" "Oct 2000" "Oct 2001" "Oct 2002" "Oct 2003"
[11,] "Nov 1999" "Nov 2000" "Nov 2001" "Nov 2002" "Nov 2003"
[12,] "Dec 1999" "Dec 2000" "Dec 2001" "Dec 2002" "Dec 2003"

sweep

sweep-函数定义:对数组、矩阵按维度进行运算

sweep(x, MARGIN, STATS, FUN = "-", check.margin = TRUE, ...)

参数列表:
x: 数组、矩阵
MARGIN:运算维度,1表示行,2表示列,3即第三维度,以此类推
STATS:运算参数,类似于减法中的减数,除法中的除数
FUN:要使用的函数

  • eg1-对数组按行运算
> mat <- matrix(1:9, 3)
> mat
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9 > sweep(mat, 1, c(1,4,7), "+") # 第一行都加1,第二行都加4,第三行都加7
[,1] [,2] [,3]
[1,] 2 5 8
[2,] 6 9 12
[3,] 10 13 16
  • eg2-STATS可为其他格式,但注意与取MARGIN后的X结构相符
> A <- array(1:24, dim = 4:2)
> median <- apply(A, 1:2, median)
> A
, , 1
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
, , 2
[,1] [,2] [,3]
[1,] 13 17 21
[2,] 14 18 22
[3,] 15 19 23
[4,] 16 20 24
> median
[,1] [,2] [,3]
[1,] 7 11 15
[2,] 8 12 16
[3,] 9 13 17
[4,] 10 14 18 > sweep(A, 1:2, median)
, , 1
[,1] [,2] [,3]
[1,] -6 -6 -6
[2,] -6 -6 -6
[3,] -6 -6 -6
[4,] -6 -6 -6
, , 2
[,1] [,2] [,3]
[1,] 6 6 6
[2,] 6 6 6
[3,] 6 6 6
[4,] 6 6 6

replicate

replicate-函数定义:rep能把输入参数重复数次,replicate则能调用表达式数次

replicate(n, expr, simplify = "array")

参数列表:
n: 调用的次数
expr:调用的表达式

  • eg-建立一个函数,模拟扔两个骰子的点数之和,然后重复运行10次
> game <- function() {
+ n <- sample(1:6,2,replace=T)
+ return(sum(n))
+ }
> replicate(n=10, game())
[1] 6 6 6 7 7 7 11 8 7 9

aggregate

aggregate-函数定义:可按要求把数据分组,然后对分组后的数据进行各种操作

aggregate(x, by, FUN, ...)

参数列表:
x: 一种R数据结构,通常为数据框
by:分组索引,必须为list格式
FUN:要使用的函数

  • eg-按性别分组查看年龄和身高的均值
> data <- data.frame(name=c("张三","李四","王五","赵六"),
+ sex=c("M","M","F","F"), age=c(20,40,22,30),
+ height=c(166,170,150,155))
> data
name sex age height
1 张三 M 20 166
2 李四 M 40 170
3 王五 F 22 150
4 赵六 F 30 155 > aggregate(data[,3:4], by=list(data$sex), mean)
Group.1 age height
1 F 26 152.5
2 M 30 168.0

致谢

参考文章

数据操作-apply函数族的更多相关文章

  1. R语言apply函数族笔记

    为什么用apply 因为我是一个程序员,所以在最初学习R的时候,当成“又一门编程语言”来学习,但是怎么学都觉得别扭.现在我的看法倾向于,R不是一种通用型的编程语言,而是一种统计领域的软件工具.因此,不 ...

  2. 【R.转载】apply函数族的使用方法

    为什么用apply 因为我是一个程序员,所以在最初学习R的时候,当成"又一门编程语言"来学习,但是怎么学都觉得别扭.现在我的看法倾向于,R不是一种通用型的编程语言,而是一种统计领域 ...

  3. 掌握R语言中的apply函数族(转)

    转自:http://blog.fens.me/r-apply/ 前言 刚开始接触R语言时,会听到各种的R语言使用技巧,其中最重要的一条就是不要用循环,效率特别低,要用向量计算代替循环计算. 那么,这是 ...

  4. R︱高效数据操作——data.table包(实战心得、dplyr对比、key灵活用法、数据合并)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 由于业务中接触的数据量很大,于是不得不转战开始 ...

  5. pandas数据操作

    pandas数据操作 字符串方法 Series对象在其str属性中配备了一组字符串处理方法,可以很容易的应用到数组中的每个元素 t = pd.Series(['a_b_c_d','c_d_e',np. ...

  6. R中apply函数族

    参考于:http://blog.fens.me/r-apply/ 1. apply的家族函数 2. apply函数 apply函数是最常用的代替for循环的函数.apply函数可以对矩阵.数据框.数组 ...

  7. python数据结构:pandas(2)数据操作

    一.Pandas的数据操作 0.DataFrame的数据结构 1.Series索引操作 (0)Series class Series(base.IndexOpsMixin, generic.NDFra ...

  8. StackExchange.Redis帮助类解决方案RedisRepository封装(字符串类型数据操作)

    本文版权归博客园和作者本人共同所有,转载和爬虫请注明原文链接 http://www.cnblogs.com/tdws/tag/NoSql/ 目录 一.基础配置封装 二.String字符串类型数据操作封 ...

  9. hive数据操作

    mdl是数据操作类的语言,包括向数据表加载文件,写查询结果等操作 hive有四种导入数据的方式 >从本地加载数据 LOAD DATA LOCAL INPATH './examples/files ...

随机推荐

  1. Ambiguous HTTP method Actions require an explicit HttpMethod binding for Swagger 2.0 异常

    网上看了很多关于此异常的解决方案,但是大多数都是不能用的,今天把正确的解决方案记录下来,以帮助需要的人 问题:有些接口没有设置HttpPost或HttpGet,非接口设置访问权限为private,控制 ...

  2. ubuntu19.10安装cuda-10.1

    ubuntu19.10安装cuda-10.1 1.安装N卡驱动: 打开ubuntu的软件和更新,设置N卡驱动 2.查看ubuntu显卡驱动 nvidia-smi 显示: Sun Feb 23 06:4 ...

  3. gcc -c xx.c 选项讲解

    -c选项表示编译.汇编指定的源文件(也就是编译源文件),但是不进行链接.使用-c选项可以将每一个源文件编译成对应的目标文件. 目标文件是一种中间文件或者临时文件,如果不设置该选项,gcc 一般不会保留 ...

  4. 好看的UI组合,为以后自己写组件库做准备

    1. 黑色格子背景 { color: rgb(255, 255, 255); text-shadow: 1px 1px 0 rgba(0,0,0,.3); rgb(62, 64, 74); backg ...

  5. msgfmt - 翻译汉化

    说明 目前大部分自由软件实现国际化使用的是gettext. 国际化就是让程序可以使用多国语言来显示程序里的字符串. 程序里一般都有很多字符串,菜单名也好,错误信息也好,都是字符串.假设字符串为stri ...

  6. Spring加载xml配置文件的方式

    梳理Spring的流程 xml是最常见的spring 应用系统配置源.Spring中的几种容器都支持使用xml装配bean,包括: XmlBeanFactory,ClassPathXmlApplica ...

  7. android中的适配器模式

    原文: https://blog.csdn.net/beyond0525/article/details/22814129 类适配模式.对象适配模式.接口适配模式

  8. Linux介绍,基本命令

  9. OpenSSL EVP_Digest系列函数的一个样例

    #include <stdio.h>     #include <openssl/evp.h>         main(int argc, char *argv[])     ...

  10. php time()时间戳作为文件名产生文件同名的bug

    /*time()函数生成的文件名可能是相同的,因为如果php运行的过程如果足够快,time()函数调用的足够频繁,那么有可能time()生成的时间戳会相同,因为时间戳是以秒为单位,所以如果足够频繁有可 ...