gra*_*tur 1011 r lapply sapply r-faq tapply
每当我想在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)
:输入是一个矩阵.output是一个向量,其中element i
是f(矩阵的row/col i)tapply(vector, grouping, f)
:output是一个矩阵/数组,其中矩阵/数组中的元素是向量f
分组g
的值,和g
被推送到行/列名称by(dataframe, grouping, f)
:让我们g
成为一个分组.适用f
于组/数据框的每一列.漂亮打印分组和f
每列的值.aggregate(matrix, grouping, f)
:类似于by
,但不是将输出打印得很漂亮,而是将所有内容都粘贴到数据帧中.侧问题:我还没有学会plyr或重塑-将plyr
或reshape
更换所有这些完全?
jor*_*ran 1295
R具有许多*应用功能,这些功能在帮助文件中有很好的描述(例如?apply
).但是,他们已经足够了,开始使用R可能很难决定哪一个适合他们的情况甚至记住它们.他们可能会有一个普遍的感觉,"我应该在这里使用*apply函数",但最初要保持一致是很困难的.
尽管事实(在其他答案中已经注明),*apply系列的大部分功能都被极受欢迎的plyr
软件包所覆盖,但基本功能仍然有用且值得了解.
这个答案旨在作为一种新的useRs 的路标,以帮助指导他们针对他们的特定问题正确的*应用功能.注意,这不是为了简单地反刍或替换R文档!希望这个答案可以帮助您确定哪种*应用功能适合您的情况,然后由您来进一步研究.除了一个例外,性能差异将无法解决.
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
Run Code Online (Sandbox Code Playgroud)
如果你想行/列指或二维矩阵的款项,一定要调查高度优化,闪电般快速的colMeans
,
rowMeans
,colSums
,rowSums
.
lapply - 当你想依次将一个函数应用于列表的每个元素并获得一个列表.
这是许多其他*应用功能的主力.剥回他们的代码,你经常会找到它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
Run Code Online (Sandbox Code Playgroud)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
Run Code Online (Sandbox Code Playgroud)
在更高级的用途中sapply
,如果合适,它将尝试将结果强制转换为多维数组.例如,如果我们的函数返回相同长度的向量,sapply
则将它们用作矩阵的列:
sapply(1:5,function(x) rnorm(3,x))
Run Code Online (Sandbox Code Playgroud)
如果我们的函数返回一个二维矩阵,sapply
将基本上做同样的事情,将每个返回的矩阵视为一个长向量:
sapply(1:5,function(x) matrix(x,2,2))
Run Code Online (Sandbox Code Playgroud)
除非我们指定simplify = "array"
,否则它将使用各个矩阵来构建一个多维数组:
sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
Run Code Online (Sandbox Code Playgroud)
这些行为中的每一个当然都取决于我们的函数返回相同长度或维度的向量或矩阵.
vapply - 当您想要使用sapply
但可能需要从代码中挤出更多速度.
因为vapply
,你基本上给R一个你的函数将返回什么样的东西的例子,这可以节省一些时间来强制返回值以适应单个原子向量.
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
Run Code Online (Sandbox Code Playgroud)mapply - 当你有几个数据结构(例如矢量,列表)并且你想要将函数应用于每个的第一个元素,然后是每个元素的第二个元素等时,将结果强制转换为矢量/数组,如同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
Run Code Online (Sandbox Code Playgroud)Map - with 的包装器,因此保证返回列表.mapply
SIMPLIFY = FALSE
Map(sum, 1:5, 1:5, 1:5)
[[1]]
[1] 3
[[2]]
[1] 6
[[3]]
[1] 9
[[4]]
[1] 12
[[5]]
[1] 15
Run Code Online (Sandbox Code Playgroud)rapply - 当您想要递归地将函数应用于嵌套列表结构的每个元素时.
为了让您了解多么罕见rapply
,我在第一次发布这个答案时就忘记了!显然,我相信很多人都会使用它,但是YMMV.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")
Run Code Online (Sandbox Code Playgroud)tapply - 当你想给一个函数应用到子集的载体和子集是由一些其它载体,通常是一个因素确定.
*的黑羊适用于各种各样的家庭.帮助文件使用短语"ragged array"可能有点令人困惑,但实际上非常简单.
矢量:
x <- 1:20
Run Code Online (Sandbox Code Playgroud)
定义组的因子(长度相同!):
y <- factor(rep(letters[1:5], each = 4))
Run Code Online (Sandbox Code Playgroud)
在以下x
定义的每个子组中添加值y
:
tapply(x, y, sum)
a b c d e
10 26 42 58 74
Run Code Online (Sandbox Code Playgroud)
可以处理更复杂的示例,其中子组由若干因子列表的唯一组合定义.tapply
在本质上分裂应用-结合是R中常见(功能相似aggregate
,by
,ave
,ddply
,等等)因此,它的黑色羊状态.
JoF*_*wld 184
在旁注中,以下是各种plyr
功能如何与基本功能相对应*apply
(从plyr网页的介绍到plyr文档http://had.co.nz/plyr/)
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
Run Code Online (Sandbox Code Playgroud)
其中一个目标plyr
是为每个函数提供一致的命名约定,对函数名中的输入和输出数据类型进行编码.它还提供输出的一致性,因为输出dlply()
很容易通过,ldply()
以产生有用的输出等.
从概念上讲,学习plyr
并不比理解基本*apply
功能困难.
plyr
reshape
在我的日常使用中,功能已经取代了几乎所有这些功能.但是,从介绍到Plyr文件:
相关功能
tapply
并sweep
没有相应的功能plyr
,并且仍然有用.merge
对于将摘要与原始数据相结合非常有用.
iso*_*mes 129
来自http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy的幻灯片21 :
(希望很明显,它apply
对应于@Hadley's aaply
并且aggregate
对应于@Hadley's ddply
等.如果你没有从这张图片中得到它,那么同一幻灯片的幻灯片20将会澄清.)
(左边是输入,顶部是输出)
Ass*_*him 96
首先从Joran的优秀答案开始 - 怀疑任何事情都可以更好.
然后,以下助记符可能有助于记住每个之间的区别.虽然有些是显而易见的,但有些可能不那么明显 - 对于这些,你会在Joran的讨论中找到理由.
助记符
lapply
是一个列表应用,它作用于列表或向量并返回一个列表.sapply
是一个简单的 lapply
(函数默认为在可能的情况下返回向量或矩阵)vapply
是经过验证的申请(允许预先指定退货对象类型)rapply
是嵌套列表的递归应用,即列表中的列表tapply
是标记应用,其中标记标识子集apply
是 通用的:应用一个函数的矩阵的行或列(或者,更一般地,以阵列的尺寸)建立正确的背景
如果使用这个apply
家庭仍然觉得你有点陌生,那么可能是你错过了一个关键的观点.
这两篇文章可以提供帮助.它们提供了激发函数apply
族提供的函数式编程技术的必要背景.
Lisp的用户将立即认识到这种范式.如果你不熟悉Lisp,一旦你了解了FP,你就会获得一个强大的观点来使用R - 并且apply
会更有意义.
Sab*_*DeM 48
因为我意识到这篇文章的(非常优秀的)答案缺乏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
Run Code Online (Sandbox Code Playgroud)
如果我们打印这两个对象,ct
并且cb
我们"基本上"具有相同的结果,唯一的区别在于它们的显示方式和不同的class
属性,分别by
为for cb
和array
for ct
.
正如我所说,by
当我们不能使用时会产生力量tapply
; 以下代码是一个例子:
tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) :
arguments must have same length
Run Code Online (Sandbox Code Playgroud)
R表示参数必须具有相同的长度,比如"我们想要计算沿着因子summary
的所有变量":但是R不能这样做,因为它不知道如何处理.iris
Species
使用by
函数R为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
Run Code Online (Sandbox Code Playgroud)
它确实有效,结果非常令人惊讶.这是一个类的对象by
,沿着Species
(例如,对于每个类)计算summary
每个变量.
请注意,如果第一个参数是a data frame
,则dispatched函数必须具有该类对象的方法.例如,我们将此代码与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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
两个直接的区别是第二个参数aggregate
必须是一个列表,而tapply
can(非强制性)是一个列表,输出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
Run Code Online (Sandbox Code Playgroud)
我们可以实现相同,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
Run Code Online (Sandbox Code Playgroud)
还有一些时候我们不能使用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
Run Code Online (Sandbox Code Playgroud)
我们无法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
Run Code Online (Sandbox Code Playgroud)
虽然by
我们实际上无法实现,但实际上以下函数调用会返回错误(但很可能与提供的函数有关mean
):
by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)
Run Code Online (Sandbox Code Playgroud)
其他时候结果是相同的,差异只是在类中(然后它是如何显示/打印的,而不仅仅是 - 例如,如何将其子集化)对象:
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
Run Code Online (Sandbox Code Playgroud)
以前的代码实现了相同的目标和结果,在某些方面,使用的工具只是个人品味和需求的问题; 前两个对象在子集方面有非常不同的需求.
jan*_*cki 33
有很多很好的答案可以讨论每个函数的用例差异.答案都没有讨论性能上的差异.这是合理的,因为各种函数期望各种输入并产生各种输出,但是它们中的大多数具有通过系列/组来评估的一般共同目标.我的回答是关注绩效.由于上述原因,来自向量的输入创建包括在定时中,apply
因此也不测量该函数.
我已经测试了两种不同的功能sum
和length
一次.测试的体积输入为50M,输出为50K.我还包括了两个目前流行的软件包,这些软件包在提出问题时没有被广泛使用,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
Run Code Online (Sandbox Code Playgroud)
# 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
Run Code Online (Sandbox Code Playgroud)
Joh*_*aul 24
尽管这里有很多很好的答案,但还有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], ...).
Run Code Online (Sandbox Code Playgroud)
这使得它似乎只对线性代数类型的东西有用.但是,它可以很好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
Run Code Online (Sandbox Code Playgroud)
当我有一个值向量和条件向量时,我亲自使用了这个,并希望看到哪些值符合哪些条件.
eapply
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
Run Code Online (Sandbox Code Playgroud)
坦率地说,我不会使用它,但如果你正在构建大量的软件包或创建很多环境,它可能会派上用场.
小智 23
这可能值得一提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
## ...
Run Code Online (Sandbox Code Playgroud)
基本包中没有任何东西像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
## ...
Run Code Online (Sandbox Code Playgroud)
我最近发现了相当有用的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,"-")
print(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
# 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
Run Code Online (Sandbox Code Playgroud)
注意:对于这个简单的例子,当然可以更容易地实现相同的结果
apply(dataPoints, 2, scale)
在最近在 CRAN 上发布的折叠包中,我尝试将大多数常见的应用功能压缩为两个功能:
dapply
(Data-Apply) 将函数应用于矩阵和 data.frames 的行或(默认)列,并且(默认)返回相同类型和相同属性的对象(除非每个计算的结果是原子和drop = TRUE
)。性能与lapply
data.frame 列相当,比apply
矩阵行或列快约 2 倍。并行性可通过mclapply
(仅适用于 MAC)获得。句法:
dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L,
return = c("same", "matrix", "data.frame"), drop = TRUE)
Run Code Online (Sandbox Code Playgroud)
例子:
# 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 ...
Run Code Online (Sandbox Code Playgroud)
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"))
Run Code Online (Sandbox Code Playgroud)
例子:
# 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 ...
Run Code Online (Sandbox Code Playgroud)
分组变量列表也可以提供给g
.
谈论性能:崩溃的一个主要目标是在 R 中培养高性能编程,并超越 split-apply-combine alltogine。为此,该包具有一整套基于 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)
Run Code Online (Sandbox Code Playgroud)
例子:
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 ...
Run Code Online (Sandbox Code Playgroud)
在小插图包中,我提供了基准。使用 fast 函数编程比使用dplyr或data.table编程要快得多,尤其是在较小的数据上,但在大数据上也是如此。