分组函数(tapply,by,aggregate)和* apply family

每当我想在R中“映射”py时,我通常会尝试在apply系列中使用函数。

然而,我从来没有完全理解它们之间的差异 – 如何将input/分组input的function,输出的结果,甚至是input的内容我经常只是通过他们,直到我得到我想要的。

有人可以解释如何使用哪一个?

我目前(可能不正确/不完整)的理解是…

  1. sapply(vec, f) :input是一个向量。 输出是一个向量/matrix,其中元素if(vec[i]) ,给出一个matrix,如果f有一个多元素输出

  2. lapply(vec, f) :与sapply相同,但是输出是列表?

  3. apply(matrix, 1/2, f) :input是一个matrix。 输出是一个向量,其中元素i是f(matrix的行/列)
  4. tapply(vector, grouping, f) :output是一个matrix/数组,其中matrix/数组中的一个元素是该vector的分组g处的f的值,并且g被推到行/列名
  5. by(dataframe, grouping, f) :让g是一个分组。 将f应用于组/数据框的每一列。 漂亮的打印分组和每列的f的值。
  6. aggregate(matrix, grouping, f) :类似于,但不是漂亮地打印输出,聚合将所有东西都粘到数据框中。

旁边的问题:我还没有学会plyr或重塑 – 将plyrreshape完全取代所有这些?

R有很多*应用function,这些function在帮助文件中有很好的描述(例如?apply )。 尽pipe如此,开始使用R可能难以确定哪一个适合他们的情况,甚至是记住它们。 他们可能有一个普遍的意思,就是“我应该在这里使用一个应用函数”,但是一开始就把它们全部保持直线是很困难的。

尽pipe(在其他答案中已经提到)这个* apply系列的很多function都被极其stream行的plyr包所覆盖,但是基本function仍然有用,值得了解。

这个答案的目的是作为一种新的用途的路标 ,以帮助指导他们针对其特定问题的正确*应用function。 请注意,这不是为了简单地反刍或replaceR文档! 希望是这个答案可以帮助你决定哪个函数适合你的情况,然后由你来进一步研究。 除了一个例外,性能差异将不被解决。

  • 应用当你想对一个matrix(和更高维的类似物)的行或列应用一个函数; 通常不build议dataframe,因为它首先会强制转换为matrix。

     # 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[*, , ] - ie Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - ie 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 

    如果你想要二维matrix的行/列的意思或总和,一定要调查高度优化,闪电般colMeansrowMeanscolSumsrowSums

  • lapply当你想依次对列表中的每个元素应用一个函数并返回一个列表。

    这是许多其他应用function的主力。 剥离他们的代码,你会经常在下面findlapply

     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) abc 1 3 91 sapply(x, FUN = sum) abc 1 6 5005 

    在更高级的sapply使用中,如果合适的sapply它会尝试将结果强制转换成multidimensional array。 例如,如果我们的函数返回相同长度的向量, sapply将使用它们作为matrix的列:

     sapply(1:5,function(x) rnorm(3,x)) 

    如果我们的函数返回一个二维matrix,则sapply将做基本相同的事情,将每个返回的matrix看作一个单独的长向量:

     sapply(1:5,function(x) matrix(x,2,2)) 

    除非我们指定了simplify = "array" ,在这种情况下,它将使用单个matrix构build一个multidimensional array:

     sapply(1:5,function(x) matrix(x,2,2), simplify = "array") 

    这些行为中的每一个当然都取决于我们的函数返回相同长度或维度的向量或matrix。

  • vapply当你想使用sapply但可能需要挤出更多的代码速度。

    对于vapply ,你基本上给R一个例子,你的函数将返回什么样的东西,这可以节省一些时间胁迫返回的值,以适应一个单一的primefaces向量。

     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) abc 1 3 91 
  • 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 
  • MapSIMPLIFY = 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当你想要将一个函数应用到嵌套列表结构的每个元素时,recursion地。

    为了给你一些不寻常的rapply想法,我第一次发布这个答案时,我忘了它! 显然,我相信很多人使用它,但是YMMV。 最好用一个用户定义的函数来说明应用:

     # 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当你想要将一个函数应用到一个向量的子 ,并且子集由其他向量定义时,通常是一个因子。

    *适用于家庭的害群之马。 帮助文件使用短语“ragged array”可能有点混乱 ,但实际上很简单。

    vector:

     x <- 1:20 

    定义组的一个因素(长度相同!):

     y <- factor(rep(letters[1:5], each = 4)) 

    在由y定义的每个子组中,将x的值y

     tapply(x, y, sum) abcde 10 26 42 58 74 

    更复杂的例子可以通过几个因素列表的独特组合来定义。 在精神上类似于R中常见的分裂应用合并函数( aggregatebyaveddply等),因此它的害群之马地位。

在旁边注意,这里是各种plyr函数如何对应于基本*apply函数(从plyr网页http://had.co.nz/plyr/的介绍到plyr文档);

 Base function Input Output plyr function --------------------------------------- aggregate dd ddply + colwise apply aa/l aaply / alply by dl dlply lapply ll llply mapply aa/l maply / mlply replicate ra/l raply / rlply sapply la laply 

plyr的目标之一是为每个函数提供一致的命名约定,在函数名称中对input和输出数据types进行编码。 它还提供了输出的一致性,因为dlply()输出很容易传递给ldply()以产生有用的输出。

从概念上讲,学习plyr并不比理解基础*applyfunction更困难。

在我日常使用中, plyrreshape函数几乎替代了所有这些函数。 但是,也从介绍到Plyr文件:

plyr相关函数tapplysweep没有相应的function,并保持有用。 merge对于汇总和原始数据的结合非常有用。

http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy的幻灯片21:;

应用,sapply,lapply,通过,聚合

(希望很明显, apply对应于@哈德利的aaplyaggregate相当于@哈德利的ddply等幻灯片20相同的幻灯片将澄清,如果你不从这个图像得到它。

(左边是input,上面是输出)

首先从乔兰的出色答案开始 – 怀疑任何事情都可以做得更好。

那么以下助记符可能有助于记住每个之间的区别。 有些是显而易见的,另外一些则可能不那么明显 – 因为这些你会在乔兰的讨论中find理由。

助记符

  • lapply是一个列表,适用于列表或向量,并返回一个列表。
  • sapply是一个简单的 lapply (函数默认返回一个向量或matrix)
  • vapply是一个validation的适用 (允许返回对象types被预先指定)
  • rapplyrecursion应用于嵌套列表,即列表内的列表
  • tapply是一个标签应用标签标识的子集
  • apply通用的 :将函数应用于matrix的行或列(或者更一般地说,应用于数组的维度)

build立正确的背景

如果使用apply家庭仍然觉得你有点陌生,那么可能是你错过了一个关键的观点。

这两篇文章可以帮助。 它们提供了必要的背景来激励function性编程技术 ,这些function性编程技术apply的function族提供。

Lisp的用户会立即认识到这个范例。 如果你对Lisp不熟悉,那么一旦你开始熟悉FP,你将会在R中获得一个强大的观点 – 而且apply会更有意义。

  • 高级R:函数式编程 ,作者:Hadley Wickham
  • R中的简单函数式编程 ,Michael Barton

因为我意识到(这个非常好的)这个post的答案缺乏aggregate解释。 这是我的贡献。

通过

正如文档中所述, by函数可以作为tapply的“包装”。 当我们想要计算一个tapply不能处理的任务时by就会产生这个能力。 一个例子是这个代码:

 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 

如果我们打印这两个对象, ctcb ,我们“基本上”具有相同的结果,唯一的区别在于它们如何显示以及不同的class属性,分别by针对cbct array

正如我所说,当我们不能使用tapply , 以下代码是一个例子:

  tapply(iris, iris$Species, summary ) Error in tapply(iris, iris$Species, summary) : arguments must have same length 

R表示参数必须具有相同的长度,例如“我们要计算iris中所有variables的总和”,但是R不能这样做,因为它不知道如何处理。

by函数R调度data frame类的具体方法,然后让summary函数即使在第一个参数的长度(以及types)不同的情况下也能正常工作。

 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 (比如说,每个Species )计算每个variables的summary

请注意,如果第一个参数是data frame ,则派发的函数必须具有该类对象的方法。 例如,我们使用这个代码与meanfunction,我们将有这个代码是没有任何意义的:

  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可以将aggregate视为另一种不同的使用方式。

 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 

两个直接的区别是aggregate的第二个参数必须是一个列表,而tapply 可以 (不是强制的)是一个列表, aggregate的输出是一个数据框,而tapply是一个array

aggregate的威力在于,它可以用subset参数容易地处理数据的subset ,并且它也具有用于ts对象和formula方法。

在某些情况下,这些元素可以使aggregate更容易使用。 以下是一些示例(可在文档中find):

 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 

还有一些时候,我们不能使用或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 ,因为aggregate函数的formula方法有默认的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我们只是不能实现,实际上下面的函数调用返回一个错误(但最有可能它是提供的function, 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) 

之前的代码实现了相同的目标和结果,在某些方面使用的工具只是个人的口味和需求的问题; 前两个对象在子集方面有着非常不同的需求。

有很多很好的答案,讨论每个函数的用例差异。 没有一个答案讨论performance的差异。 这是合理的,各种function要求各种input和产生各种输出,但大多数有一个共同的目标,以串联/组进行评估。 我的答案是关注性能。 由于上述从vector的input创build包括在时间中,所以也不测量applyfunction。

我一次testing了两个不同的函数sumlength 。 testing的音量是50Minput和50K输出。 我还收录了两个目前stream行的软件包, data.table问题时, data.tabledplyr都没有被广泛使用。 如果你的目标是取得好成绩的话,那么这两者绝对值得一看。

 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 

也许值得一提的是aveavetapply的友好表弟。 它以一种可以直接插入数据框的forms返回结果。

 dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4)) means <- tapply(dfr$a, dfr$f, mean) ## ABCDE ## 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 ## afm 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 ## ... 

基础包中没有任何东西像整个dataframe一样工作(就像dataframe一样)。 但是你可以捏造它:

 dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) { x <- dfr[x,] sum(x$m*x$m2) }) dfr ## afm 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 ## ... 

尽pipe这里有很好的答案,但还有两个基本函数值得一提,有用的outer函数和eapply函数

outer是一个非常有用的function隐藏作为一个更普通的。 如果你阅读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], ...). 

这使得它看起来像这只对线性代数types的东西有用。 但是,它可以用于很多像mapply一样将函数应用于两个input向量。 不同的是, 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

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 

坦率地说,我并没有太多的使用它,但是如果你正在构build大量的包或者创build很多的环境,它可能会派上用场。

我最近发现了相当有用的sweepfunction,为了完整起见,在这里添加它:

基本思想是通过一个数组以行或列的方式扫描并返回一个修改后的数组。 一个例子将清楚(source: datacamp ):

假设你有一个matrix,并想列标准化 :

 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 

注意:对于这个简单的例子,当然可以通过更容易地实现相同的结果
apply(dataPoints, 2, scale)