提问人:grautur 提问时间:8/18/2010 最后编辑:Waelgrautur 更新时间:7/2/2023 访问量:438336
分组函数(tapply、by、aggregate)和 *apply 系列
Grouping functions (tapply, by, aggregate) and the *apply family
问:
每当我想在 R 中做一些“map”py 时,我通常会尝试使用家族中的函数。apply
然而,我一直不太理解它们之间的区别——{、、等}如何将函数应用于输入/分组输入,输出会是什么样子,甚至输入可以是什么——所以我经常只是把它们都看一遍,直到我得到我想要的东西。sapply
lapply
有人可以解释一下什么时候如何使用哪一个吗?
我目前(可能不正确/不完整)的理解是......
sapply(vec, f)
:输入为向量。output 是一个向量/矩阵,其中 element 是 ,如果具有多元素输出,则为您提供一个矩阵i
f(vec[i])
f
lapply(vec, f)
:与 相同,但输出是列表?sapply
apply(matrix, 1/2, f)
:输入为矩阵。输出是一个向量,其中元素是 f(矩阵的行/列 i)i
tapply(vector, grouping, f)
:输出是一个矩阵/数组,其中矩阵/数组中的元素是向量分组的值,并被推送到行/列名称f
g
g
by(dataframe, grouping, f)
:设为分组。应用于组/数据帧的每一列。漂亮地打印每列的分组和值。g
f
f
aggregate(matrix, grouping, f)
:类似于 ,但不是漂亮地打印输出,而是将所有内容粘贴到 DataFrame 中。by
附带问题:我还没有学会 plyr 或重塑——会或完全取代所有这些吗?plyr
reshape
答:
在旁注中,以下是各种函数与基本函数的对应关系(从 plyr 网页的介绍到 plyr 文档 http://had.co.nz/plyr/plyr
*apply
)
Base function Input Output plyr function
---------------------------------------
aggregate d d ddply + colwise
apply a a/l aaply / alply
by d l dlply
lapply l l llply
mapply a a/l maply / mlply
replicate r a/l raply / rlply
sapply l a laply
目标之一是为每个函数提供一致的命名约定,在函数名称中对输入和输出数据类型进行编码。它还提供了输出的一致性,因为输出很容易传递到产生有用的输出等。plyr
dlply()
ldply()
从概念上讲,学习并不比理解基本函数更难。plyr
*apply
plyr
在常使用中,功能几乎取代了所有这些功能。但是,同样来自 Plyr 简介文档:reshape
相关函数,在 中没有相应的函数,并且仍然有用。 可用于将摘要与原始数据相结合。
tapply
sweep
plyr
merge
评论
*apply()
ddply()
ddply()
R 有许多 *apply 函数,这些函数在帮助文件中进行了描述(例如)。但是,它们的数量已经足够多了,以至于刚开始使用的人可能很难决定哪一个适合他们的情况,甚至很难记住它们。他们可能有一种普遍的感觉,即“我应该在这里使用 *apply 函数”,但一开始很难让他们都保持直截了当。?apply
尽管(在其他答案中指出)*apply 系列的大部分功能都包含在非常流行的软件包中,但基本功能仍然有用且值得了解。plyr
这个答案旨在作为新用户的路标,以帮助引导他们找到针对其特定问题的正确 *apply 函数。请注意,这并不是简单地反刍或替换 R 文档!希望这个答案能帮助您决定哪个 *apply 函数适合您的情况,然后由您进一步研究它。除一个例外情况外,性能差异将不会得到解决。
apply - 当您想将函数应用于行或列时 矩阵(和更高维的类似物);通常不建议用于数据帧,因为它会首先强制转换为矩阵。
# Two dimensional matrix M <- matrix(seq(1,16), 4, 4) # apply min to rows apply(M, 1, min) [1] 1 2 3 4 # apply max to columns apply(M, 2, max) [1] 4 8 12 16 # 3 dimensional array M <- array( seq(32), dim = c(4,4,2)) # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension apply(M, c(1,2), sum) # Result is two-dimensional [,1] [,2] [,3] [,4] [1,] 18 26 34 42 [2,] 20 28 36 44 [3,] 22 30 38 46 [4,] 24 32 40 48
如果需要 2D 矩阵的行/列均值或总和,请确保 研究高度优化、闪电般快速、。
colMeans
rowMeans
colSums
rowSums
lapply - 当您想将函数应用于 依次列出并返回列表。
这是许多其他 *apply 函数的主力。皮 支持他们的代码,你经常会在下面找到。
lapply
x <- list(a = 1, b = 1:3, c = 10:100) lapply(x, FUN = length) $a [1] 1 $b [1] 3 $c [1] 91 lapply(x, FUN = sum) $a [1] 1 $b [1] 6 $c [1] 5005
sapply - 当您想将函数应用于 依次列出,但您需要一个向量,而不是列表。
如果您发现自己在打字,请停下来考虑一下。
unlist(lapply(...))
sapply
x <- list(a = 1, b = 1:3, c = 10:100) # Compare with above; a named vector, not a list sapply(x, FUN = length) a b c 1 3 91 sapply(x, FUN = sum) a b c 1 6 5005
在更高级的用途中,它将试图胁迫 结果转换为多维数组(如果适用)。例如,如果我们的函数返回相同长度的向量,则将它们用作矩阵的列:
sapply
sapply
sapply(1:5,function(x) rnorm(3,x))
如果我们的函数返回一个二维矩阵,将执行基本相同的操作,将每个返回的矩阵视为单个长向量:
sapply
sapply(1:5,function(x) matrix(x,2,2))
除非我们指定 ,在这种情况下,它将使用单个矩阵来构建一个多维数组:
simplify = "array"
sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
当然,这些行为中的每一个都取决于我们的函数返回相同长度或维度的向量或矩阵。
vapply - 当您想使用
sapply
但可能需要 从代码中榨取更多的速度,或者想要更多的类型安全性。对于,你基本上给 R 举了一个例子,说明什么样的东西 您的函数将返回,这可以节省一些强制返回的时间 值以适合单个原子向量。
vapply
x <- list(a = 1, b = 1:3, c = 10:100) #Note that since the advantage here is mainly speed, this # example is only for illustration. We're telling R that # everything returned by length() should be an integer of # length 1. vapply(x, FUN = length, FUN.VALUE = 0L) a b c 1 3 91
mapply - 当您有多个数据结构(例如 向量、列表),并且您想将函数应用于第 1 个元素 每个元素,然后是每个元素的第二个元素,依此类推,强制结果 添加到向量/数组,如
sapply
。从某种意义上说,这是多变量的,您的函数必须接受 多个参数。
#Sums the 1st elements, the 2nd elements, etc. mapply(sum, 1:5, 1:5, 1:5) [1] 3 6 9 12 15 #To do rep(1,4), rep(2,3), etc. mapply(rep, 1:4, 4:1) [[1]] [1] 1 1 1 1 [[2]] [1] 2 2 2 [[3]] [1] 3 3 [[4]] [1] 4
Map - 使用
SIMPLIFY = FALSE
进行 mapply
的包装器,因此可以保证返回列表。Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15
rapply - 当您想以递归方式将函数应用于嵌套列表结构的每个元素时。
为了让您了解不常见,我在第一次发布此答案时忘记了它!显然,我相信很多人都在使用它,但是 YMMV。 最好用用户定义的函数来说明:
rapply
rapply
# Append ! to string, otherwise increment myFun <- function(x){ if(is.character(x)){ return(paste(x,"!",sep="")) } else{ return(x + 1) } } #A nested list structure l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), b = 3, c = "Yikes", d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5))) # Result is named vector, coerced to character rapply(l, myFun) # Result is a nested list like l, with values altered rapply(l, myFun, how="replace")
tapply - 当您想将函数应用于 向量和子集由其他向量定义,通常是 因素。
*应用家族的害群之马,某种程度上。帮助文件的使用 “参差不齐的阵列”这个短语可能有点令人困惑,但实际上确实如此 很简单。
向量:
x <- 1:20
定义组的因子(长度相同!
y <- factor(rep(letters[1:5], each = 4))
将 中定义的每个子组中的值相加,如下所示:
x
y
tapply(x, y, sum) a b c d e 10 26 42 58 74
在定义子组的地方可以处理更复杂的示例 通过几个因素列表的独特组合。 是 在精神上类似于 split-apply-combine 函数 常见于 R(、、、等)因此,它的 害群之马的地位。
tapply
aggregate
by
ave
ddply
评论
by
aggregate
tapply
aggregate
by
摘自第 21 张幻灯片(共 http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy 张):
(希望很清楚,这与@Hadley相对应,对应于@Hadley等,同一幻灯片的第 20 张幻灯片将澄清您是否没有从这张图片中得到它。apply
aaply
aggregate
ddply
(左边是输入,顶部是输出)
首先,从乔兰的出色回答开始——毫无疑问,任何事情都能比这更好。
那么下面的助记符可能有助于记住两者之间的区别。虽然有些是显而易见的,但有些可能不那么---对于这些,你会在乔兰的讨论中找到理由。
记忆术
lapply
是一个列表应用,它作用于列表或向量并返回一个列表。sapply
是一个简单的(函数默认在可能的情况下返回向量或矩阵)lapply
vapply
是已验证的应用(允许预先指定返回对象类型)rapply
是嵌套列表的递归应用,即列表中的列表tapply
是一个标记的应用,其中标记标识子集apply
是泛型的:将函数应用于矩阵的行或列(或者更一般地说,应用于数组的维度)
建立正确的背景
如果使用家庭对你来说仍然有点陌生,那么可能是你错过了一个关键的观点。apply
这两篇文章可以提供帮助。它们提供了必要的背景来激励函数系列提供的函数式编程技术。apply
Lisp 的用户会立即认识到这种范式。如果你不熟悉 Lisp,一旦你掌握了 FP,你就会获得一个在 R 中使用的重要观点——并且会更有意义。apply
- 高级 R:函数式编程,作者:Hadley Wickham
- R 中的简单函数式编程,作者:Michael Barton
也许值得一提的是. 是友善的表弟。它以一种形式返回结果,您可以直接将其插入到数据框中。ave
ave
tapply
dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
## A B C D E
## 2.5 6.5 10.5 14.5 18.5
## great, but putting it back in the data frame is another line:
dfr$m <- means[dfr$f]
dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
dfr
## a f m m2
## 1 A 2.5 2.5
## 2 A 2.5 2.5
## 3 A 2.5 2.5
## 4 A 2.5 2.5
## 5 B 6.5 6.5
## 6 B 6.5 6.5
## 7 B 6.5 6.5
## ...
基础包中没有任何内容可以像整个数据帧那样工作(就像数据帧一样)。但你可以捏造它:ave
by
tapply
dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
x <- dfr[x,]
sum(x$m*x$m2)
})
dfr
## a f m m2 foo
## 1 1 A 2.5 2.5 25
## 2 2 A 2.5 2.5 25
## 3 3 A 2.5 2.5 25
## ...
自从我意识到这篇文章的(非常出色的)答案缺乏和解释以来。这是我的贡献。by
aggregate
由
不过,如文档中所述,该函数可以作为 的“包装器”。当我们想要计算一个无法处理的任务时,就会产生强大的力量。一个示例是以下代码:by
tapply
by
tapply
ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )
cb
iris$Species: setosa
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.300 3.200 3.400 3.428 3.675 4.400
--------------------------------------------------------------
iris$Species: versicolor
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.000 2.525 2.800 2.770 3.000 3.400
--------------------------------------------------------------
iris$Species: virginica
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.200 2.800 3.000 2.974 3.175 3.800
ct
$setosa
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.300 3.200 3.400 3.428 3.675 4.400
$versicolor
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.000 2.525 2.800 2.770 3.000 3.400
$virginica
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.200 2.800 3.000 2.974 3.175 3.800
如果我们打印这两个对象,并且 ,我们“基本上”会得到相同的结果,唯一的区别在于它们的显示方式和不同的属性,分别是 for 和 for 。ct
cb
class
by
cb
array
ct
正如我所说,当我们不能使用时,力量就会产生;以下代码是一个示例:by
tapply
tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) :
arguments must have same length
R 说参数必须具有相同的长度,比如“我们要计算沿因子的所有变量”:但 R 无法做到这一点,因为它不知道如何处理。summary
iris
Species
使用函数 R 为类调度一个特定的方法,然后让函数工作,即使第一个参数(和类型)的长度不同。by
data frame
summary
bywork <- by(iris, iris$Species, summary )
bywork
iris$Species: setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.300 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:4.800 1st Qu.:3.200 1st Qu.:1.400 1st Qu.:0.200 versicolor: 0
Median :5.000 Median :3.400 Median :1.500 Median :0.200 virginica : 0
Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246
3rd Qu.:5.200 3rd Qu.:3.675 3rd Qu.:1.575 3rd Qu.:0.300
Max. :5.800 Max. :4.400 Max. :1.900 Max. :0.600
--------------------------------------------------------------
iris$Species: versicolor
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.900 Min. :2.000 Min. :3.00 Min. :1.000 setosa : 0
1st Qu.:5.600 1st Qu.:2.525 1st Qu.:4.00 1st Qu.:1.200 versicolor:50
Median :5.900 Median :2.800 Median :4.35 Median :1.300 virginica : 0
Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326
3rd Qu.:6.300 3rd Qu.:3.000 3rd Qu.:4.60 3rd Qu.:1.500
Max. :7.000 Max. :3.400 Max. :5.10 Max. :1.800
--------------------------------------------------------------
iris$Species: virginica
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.900 Min. :2.200 Min. :4.500 Min. :1.400 setosa : 0
1st Qu.:6.225 1st Qu.:2.800 1st Qu.:5.100 1st Qu.:1.800 versicolor: 0
Median :6.500 Median :3.000 Median :5.550 Median :2.000 virginica :50
Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026
3rd Qu.:6.900 3rd Qu.:3.175 3rd Qu.:5.875 3rd Qu.:2.300
Max. :7.900 Max. :3.800 Max. :6.900 Max. :2.500
它确实有效,结果非常令人惊讶。它是一个类对象,它(比如说,对于每个变量)计算每个变量。by
Species
summary
请注意,如果第一个参数是 ,则调度函数必须具有该类对象的方法。例如,我们将此代码与函数一起使用,我们将拥有完全没有意义的代码:data frame
mean
by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
-------------------------------------------
iris$Species: versicolor
[1] NA
-------------------------------------------
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
骨料
aggregate
如果我们以这种方式使用它,可以将其视为另一种不同的使用方式。tapply
at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)
at
setosa versicolor virginica
5.006 5.936 6.588
ag
Group.1 x
1 setosa 5.006
2 versicolor 5.936
3 virginica 6.588
两个直接的区别是,的第二个参数必须是列表,而 can (不是强制性的) 是列表,并且 的输出是数据帧,而 的输出是 .aggregate
tapply
aggregate
tapply
array
它的强大之处在于它可以通过参数轻松处理数据子集,并且它还具有对象方法。aggregate
subset
ts
formula
在某些情况下,这些元素使使用它变得更加容易。
以下是一些示例(可在文档中找到):aggregate
tapply
ag <- aggregate(len ~ ., data = ToothGrowth, mean)
ag
supp dose len
1 OJ 0.5 13.23
2 VC 0.5 7.98
3 OJ 1.0 22.70
4 VC 1.0 16.77
5 OJ 2.0 26.06
6 VC 2.0 26.14
我们可以实现相同的目的,但语法稍微难一些,输出(在某些情况下)可读性较差:tapply
att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)
att
OJ VC
0.5 13.23 7.98
1 22.70 16.77
2 26.06 26.14
还有其他时候我们不能使用或我们必须使用 .by
tapply
aggregate
ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)
ag1
Month Ozone Temp
1 5 23.61538 66.73077
2 6 29.44444 78.22222
3 7 59.11538 83.88462
4 8 59.96154 83.96154
5 9 31.44828 76.89655
我们不能在一次调用中获得之前的结果,但我们必须计算每个元素的平均值,然后将它们组合起来(另请注意,我们必须调用 ,因为该函数的方法默认具有 ):tapply
Month
na.rm = TRUE
formula
aggregate
na.action = na.omit
ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)
cbind(ta1, ta2)
ta1 ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000
虽然我们只是无法实现这一点,但实际上以下函数调用返回错误(但很可能与提供的函数有关):by
mean
by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)
其他时候,结果是相同的,区别仅在于类(然后如何显示/打印它,而不仅仅是 - 例如,如何子集它)对象:
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
前面的代码实现了相同的目标和结果,在某些时候使用什么工具只是个人品味和需求的问题;前两个对象在子集方面的需求非常不同。
有很多很好的答案,讨论了每个函数用例的差异。没有一个答案讨论性能差异。这是合理的,因为各种函数需要不同的输入并产生不同的输出,但它们中的大多数都有一个通用的共同目标,即按系列/组进行评估。我的答案是将重点放在性能上。由于上述原因,从向量创建的输入包含在计时中,因此函数也不被测量。apply
我一次测试了两个不同的功能。测试的音量为输入50M,输出50K。我还收录了两个目前流行的软件包,它们在提出问题时没有被广泛使用,并且.如果您的目标是获得良好的性能,那么两者都绝对值得一看。sum
length
data.table
dplyr
library(dplyr)
library(data.table)
set.seed(123)
n = 5e7
k = 5e5
x = runif(n)
grp = sample(k, n, TRUE)
timing = list()
# sapply
timing[["sapply"]] = system.time({
lt = split(x, grp)
r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
})
# lapply
timing[["lapply"]] = system.time({
lt = split(x, grp)
r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
})
# tapply
timing[["tapply"]] = system.time(
r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
)
# by
timing[["by"]] = system.time(
r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)
# aggregate
timing[["aggregate"]] = system.time(
r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)
# dplyr
timing[["dplyr"]] = system.time({
df = data_frame(x, grp)
r.dplyr = summarise(group_by(df, grp), sum(x), n())
})
# data.table
timing[["data.table"]] = system.time({
dt = setnames(setDT(list(x, grp)), c("x","grp"))
r.data.table = dt[, .(sum(x), .N), grp]
})
# all output size match to group count
sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table),
function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
# sapply lapply tapply by aggregate dplyr data.table
# TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# print timings
as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE
)[,.(fun = V1, elapsed = V2)
][order(-elapsed)]
# fun elapsed
#1: aggregate 109.139
#2: by 25.738
#3: dplyr 18.978
#4: tapply 17.006
#5: lapply 11.524
#6: sapply 11.326
#7: data.table 2.686
尽管这里有很多很好的答案,但还有 2 个基本函数值得一提,有用的函数和晦涩的函数outer
eapply
外
outer
是一个非常有用的功能,隐藏为一个更平凡的功能。如果您阅读其描述的帮助,则说:outer
The outer product of the arrays X and Y is the array A with dimension
c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =
FUN(X[arrayindex.x], Y[arrayindex.y], ...).
这使得这似乎只对线性代数类型的东西有用。但是,它的使用方式与将函数应用于两个输入向量非常相似。不同之处在于,将函数应用于前两个元素,然后应用于后两个元素,以此类推,而将函数应用于第一个向量的一个元素和第二个向量的一个元素的每个组合。例如:mapply
mapply
outer
A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
mapply(FUN=pmax, A, B)
> mapply(FUN=pmax, A, B)
[1] 1 3 6 9 12
outer(A,B, pmax)
> outer(A,B, pmax)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 6 9 12
[2,] 3 3 6 9 12
[3,] 5 5 6 9 12
[4,] 7 7 7 9 12
[5,] 9 9 9 9 12
当我有一个值向量和一个条件向量并希望查看哪些值满足哪些条件时,我个人就使用了它。
电子申请
eapply
就像,它不是将函数应用于列表中的每个元素,而是将函数应用于环境中的每个元素。例如,如果要在全局环境中查找用户定义函数的列表:lapply
A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
C<-list(x=1, y=2)
D<-function(x){x+1}
> eapply(.GlobalEnv, is.function)
$A
[1] FALSE
$B
[1] FALSE
$C
[1] FALSE
$D
[1] TRUE
坦率地说,我不太使用它,但是如果您正在构建大量软件包或创建大量环境,它可能会派上用场。
我最近发现了一个相当有用的函数,为了完整起见,我在这里添加了它:sweep
扫
基本思想是逐行或按列扫描数组并返回修改后的数组。一个例子可以清楚地说明这一点(来源:datacamp):
假设您有一个矩阵,并希望逐列对其进行标准化:
dataPoints <- matrix(4:15, nrow = 4)
# Find means per column with `apply()`
dataPoints_means <- apply(dataPoints, 2, mean)
# Find standard deviation with `apply()`
dataPoints_sdev <- apply(dataPoints, 2, sd)
# Center the points
dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
# Return the result
dataPoints_Trans1
## [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,] 0.5 0.5 0.5
## [4,] 1.5 1.5 1.5
# Normalize
dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")
# Return the result
dataPoints_Trans2
## [,1] [,2] [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,] 0.3872983 0.3872983 0.3872983
## [4,] 1.1618950 1.1618950 1.1618950
注意:对于这个简单的例子,当然可以通过以下方式更容易地获得相同的结果
apply(dataPoints, 2, scale)
评论
sweep
apply
sapply
lapply
apply
sweep(matrix(1:6,nrow=2),2,7:9,list)
apply
apply
sweep
在最近在 CRAN 上发布的 collapse 包中,我尝试将大多数常见的应用功能压缩为仅 2 个函数:
dapply
(Data-Apply) 将函数应用于矩阵和 data.frames 的行或(默认)列,并且(默认)返回具有相同类型和相同属性的对象(除非每次计算的结果是原子和 )。性能与 data.frame 列相当,比矩阵行或列快约 2 倍。并行性可通过以下方式获得(仅适用于 MAC)。drop = TRUE
lapply
apply
mclapply
语法:
dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L,
return = c("same", "matrix", "data.frame"), drop = TRUE)
例子:
# Apply to columns:
dapply(mtcars, log)
dapply(mtcars, sum)
dapply(mtcars, quantile)
# Apply to rows:
dapply(mtcars, sum, MARGIN = 1)
dapply(mtcars, quantile, MARGIN = 1)
# Return as matrix:
dapply(mtcars, quantile, return = "matrix")
dapply(mtcars, quantile, MARGIN = 1, return = "matrix")
# Same for matrices ...
BY
是 S3 泛型,用于使用向量、矩阵和 data.frame 方法进行拆分-应用-组合计算。它比 和 快得多(在大数据上也比 快)。tapply
by
aggregate
plyr
dplyr
语法:
BY(X, g, FUN, ..., use.g.names = TRUE, sort = TRUE,
expand.wide = FALSE, parallel = FALSE, mc.cores = 1L,
return = c("same", "matrix", "data.frame", "list"))
例子:
# Vectors:
BY(iris$Sepal.Length, iris$Species, sum)
BY(iris$Sepal.Length, iris$Species, quantile)
BY(iris$Sepal.Length, iris$Species, quantile, expand.wide = TRUE) # This returns a matrix
# Data.frames
BY(iris[-5], iris$Species, sum)
BY(iris[-5], iris$Species, quantile)
BY(iris[-5], iris$Species, quantile, expand.wide = TRUE) # This returns a wider data.frame
BY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix
# Same for matrices ...
还可以向 提供分组变量列表。g
谈论性能:collapse 的一个主要目标是在 R 中培养高性能编程,并超越 split-apply-combine alltogether。为此,该软件包具有一整套基于 C++ 的快速泛型函数:、、、它们在单次传递数据时执行分组计算(即没有拆分和重组)。fmean
fmedian
fmode
fsum
fprod
fsd
fvar
fmin
fmax
ffirst
flast
fNobs
fNdistinct
fscale
fbetween
fwithin
fHDbetween
fHDwithin
flag
fdiff
fgrowth
语法:
fFUN(x, g = NULL, [w = NULL,] TRA = NULL, [na.rm = TRUE,] use.g.names = TRUE, drop = TRUE)
例子:
v <- iris$Sepal.Length
f <- iris$Species
# Vectors
fmean(v) # mean
fmean(v, f) # grouped mean
fsd(v, f) # grouped standard deviation
fsd(v, f, TRA = "/") # grouped scaling
fscale(v, f) # grouped standardizing (scaling and centering)
fwithin(v, f) # grouped demeaning
w <- abs(rnorm(nrow(iris)))
fmean(v, w = w) # Weighted mean
fmean(v, f, w) # Weighted grouped mean
fsd(v, f, w) # Weighted grouped standard-deviation
fsd(v, f, w, "/") # Weighted grouped scaling
fscale(v, f, w) # Weighted grouped standardizing
fwithin(v, f, w) # Weighted grouped demeaning
# Same using data.frames...
fmean(iris[-5], f) # grouped mean
fscale(iris[-5], f) # grouped standardizing
fwithin(iris[-5], f) # grouped demeaning
# Same with matrices ...
在软件包小插曲中,我提供了基准测试。使用 fast 函数进行编程比使用 dplyr 或 data.table 进行编程要快得多,尤其是在较小的数据上,但也在大数据上。
从 R 4.3.0 开始,将支持数据帧和/或两者,并支持使用公式对数据帧行进行分组。tapply
tapply
by
> R.version.string
[1] "R version 4.3.0 beta (2023-04-07 r84200)"
> dd <- data.frame(x = 1:10, f = gl(5L, 2L), g = gl(2L, 5L))
x f g
1 1 1 1
2 2 1 1
3 3 2 1
4 4 2 1
5 5 3 1
6 6 3 2
7 7 4 2
8 8 4 2
9 9 5 2
10 10 5 2
> tapply(dd, ~f + g, nrow)
g
f 1 2
1 2 0
2 2 0
3 1 1
4 0 2
5 0 2
> by(dd, ~g, identity)
g: 1
x f g
1 1 1 1
2 2 1 1
3 3 2 1
4 4 2 1
5 5 3 1
------------------------------------------------------------
g: 2
x f g
6 6 3 2
7 7 4 2
8 8 4 2
9 9 5 2
10 10 5 2
某些软件包中也有一些替代方案,上面没有讨论。
包中的函数提供了 apply 系列函数的替代方法,用于在群集上执行并行计算。R 中并行计算的其他替代方法包括包和包,它们允许并行执行循环和函数。该软件包提供了一个简单且一致的 API,用于使用 futures,这是一种异步计算表达式的方法,无论是并行还是顺序。此外,该包还提供了一种用于迭代和映射的函数式编程方法,并支持通过该包进行并行化。parApply()
parallels
foreach
doParallel
future
purrr
future
以下是一些示例
parApply() 示例:
library(parallel)
# Create a matrix
m <- matrix(1:20, nrow = 5)
# Define a function to apply to each column of the matrix
my_fun <- function(x) {
x^2
}
# Apply the function to each column of the matrix in parallel
result <- parApply(cl = makeCluster(2), X = m, MARGIN = 2, FUN = my_fun)
# View the result
result
foreach 示例:
library(foreach)
library(doParallel)
# Register a parallel backend
registerDoParallel(cores = 2)
# Create a list of numbers
my_list <- list(1, 2, 3, 4, 5)
# Define a function to apply to each element of the list
my_fun <- function(x) {
x^2
}
# Apply the function to each element of the list in parallel
result <- foreach(i = my_list) %dopar% my_fun(i)
# View the result
result
未来示例:
library(future)
# Plan to use a parallel backend
plan(multisession, workers = 2)
# Create a list of numbers
my_list <- list(1, 2, 3, 4, 5)
# Define a function to apply to each element of the list
my_fun <- function(x) {
x^2
}
# Apply the function to each element of the list in parallel using futures
result <- future_map(my_list, my_fun)
# View the result
result
咕噜咕噜的例子:
library(purrr)
library(future)
# Plan to use a parallel backend
plan(multisession, workers = 2)
# Create a list of numbers
my_list <- list(1, 2, 3, 4, 5)
# Define a function to apply to each element of the list
my_fun <- function(x) {
x^2
}
# Apply the function to each element of the list in parallel using purrr
result <- future_map(my_list, my_fun)
# View the result
result
编辑 2023-07-02(由未来的作者):将已弃用且不再存在的未来后端替换为 .multiprocess
multisession
评论
cl <- makeCluster(2)
parLapply()
stopCluster(cl)
评论
*apply()
by
doBy
data.table
sapply
只是在输出上添加了 。 强制转换为原子向量,但输出可以是向量或列表。 将 DataFrame 拆分为子 DataFrame,但不会单独用于列。仅当存在 'data.frame'-class 的方法时,才能按列应用。 是泛型的,因此第一个参数的不同类存在不同的方法。lapply
simplify2array
apply
by
f
f
by
aggregate