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. LeetCode——48. 旋转图像

    给定一个 n × n 的二维矩阵表示一个图像. 将图像顺时针旋转 90 度. 说明: 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵.请不要使用另一个矩阵来旋转图像. 示例 1: 给定 m ...

  2. tensorflow实现sphereFace网络(20层CNN)

    #coding:utf-8 import tensorflow as tf from tensorflow.python.framework import ops import numpy as np ...

  3. Maven--可选依赖

    假设有这样换一个依赖关系,项目 A 依赖于项目 B,项目 B 依赖于项目 X 和 Y,B 对于 X 和 Y的依赖都是可选依赖: A -> B B -> X(可选) B -> Y(可选 ...

  4. python学习Day08--文件操作

    [主要内容] 文件操作: 1. r 2. w 3. a 4. r+ 读写模式. 需要移动光标进行反复读写 5. w+ 6. a+ 7. b bytes 读写操作的是字节. 用在非文本上 8. seek ...

  5. remove_if 的效率测试

    #include <iostream> #include <functional> #include <vector> #include <algorithm ...

  6. Multiple alleles|an intuitive argument|

    I.5 Multiple alleles. 由两个等位基因拓展到多个等位基因,可以得到更多种二倍体基因型: 所以单个等位基因的概率(用i代指某个基因,pi*是该基因的频率)是(以计数的方法表示) 所以 ...

  7. python基础——异常处理及断言

    python常见的异常类型? 异常有很多种类型,常见的由语法错误(SyntaxError).类型错误(TypeError).名字错误(NameError)等等,但我们要知道的是异常本身就是一个类的实例 ...

  8. USB Reverse Tether (a dirty solution)

    Tether your android phone to your PC using USB cable could share your 3g Internet connection with PC ...

  9. DateTimePicket jQuery 日期插件,开始时间和结束时间示例

    需要引入的js文件: <input type="text" id="startTime" placeholder="开始时间"/> ...

  10. day08-内置函数和匿名函数

    1. 1)网络编程只能是二进制.2)照片和视频也是以二进制储存. 3)html网页爬取到的也是二进制编码. 2. 非常重要的4个内置函数:zip ,filter,map,sorted 1)zip: 例 ...