R function lapply

包括函数:lapply, sapply, vapply

lapply

1
lapply(X, FUN, ...)

该函数会返回一个长度和X参数的长度相同的列表,其中每个元素都是X参数在FUN函数作用下的结果。

实现源码

1
2
3
4
5
6
7
function (X, FUN, ...)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X)) # 如果不是向量(列表等也是向量),则会先转成list
X <- as.list(X)
.Internal(lapply(X, FUN)) # 直接调用C核心的函数
}

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
> a
[,1] [,2]
[1,] 1 3
[2,] 2 4
> lapply(a, function(x)x^2) # length(a) == 4
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
[[4]]
[1] 16
> d
$a
[1] 1 2
$b
[1] 3 4 5
> lapply(d, sum) # length(d) == 2
$a
[1] 3
$b
[1] 12

一个实际的例子:一次性加载某个目录下的R文件

1
2
3
4
5
6
CaiSource <- function(x, p.path) {
source(paste(p.path, x, sep=""))
}
# lib.path是指定的目录
lapply(list.files(path=lib.path, pattern='\\.[rR]$'), CaiSource, lib.path)

sapply

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

sapply是对lapply的封装,实现代码:

1
2
3
4
5
6
7
8
9
10
function (X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
{
FUN <- match.fun(FUN)
answer <- lapply(X = X, FUN = FUN, ...) # 直接调用lapply
if (USE.NAMES && is.character(X) && is.null(names(answer)))
names(answer) <- X # USE。NAMES参数
if (!identical(simplify, FALSE) && length(answer))
simplify2array(answer, higher = (simplify == "array")) # simplify参数,默认会转换成array
else answer
}

vapply

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

该函数和sapply类似

1
2
3
4
5
6
7
function (X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
{
FUN <- match.fun(FUN)
if (!is.vector(X) || is.object(X)) # 对数据进行预处理
X <- as.list(X)
.Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES))
}

FUN.VALUE的值定义是?

一段R程序的优化

R性能优化

原程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CaiAnalyseEiMac <- function(x) {
# 分析应用中,一个mac对应多个imei地址的情况
#
# Args:
# x: list类型,待分析数据
# x$aid: 应用ID,格式例如:aid=23。(下面的格式也类同)
# x$ei: imei列表
# x$mac: mac列表
# Return:
# list,对应多个imei的mac的占比
# 格式化应用数据
x$aid <- substr(x$aid, 5, 100)
n <- length(x$aid)
aid.lst <- unique(x$aid)
# 计算总体一个mac对应多个imei的情况
# 初始化
tmp.lst <- list()
mac.unique <- unique(x$mac)
for (mac in mac.unique) {
tmp.lst[[mac]] <- c()
}
# 把imei都加入mac列表
for (i in 1:n) {
tmp.lst[[x$mac[i]]] <- c(tmp.lst[[x$mac[i]]], x$ei[i])
}
# 汇总唯一值的个数
tmp.lst <- lapply(tmp.lst, FUN=function(x){return(length(unique(x)))})
}

因为数据量比较大,在工作的机器上跑的时间超过半小时。。。。主要原因有两个:

  • copy-on-change,这是R的机制,循环里有大量的修改list操作;
  • R的循环效率比较低

后来发现tapply函数可以达到目的,主要代码如下:

1
tmp.lst <- tapply(x$mac, x$ei, function(x)length(unique(x)))

非常的简洁,而且时间消耗就几秒而已。