分组功能(tapply,by,总计)和 *应用家庭
题
每当我想在r中做一些“映射” py时,我通常会尝试在 apply
家庭。
但是,我从来没有完全了解它们之间的差异 - {sapply
, lapply
, ,等等。}将功能应用于输入/分组输入,输出的外观,甚至输入可能是什么 - 因此,我经常只能通过它们,直到得到想要的东西为止。
有人可以解释如何使用哪个?
我目前的理解(可能不正确/不完整)是...
sapply(vec, f)
: :输入是向量。输出是向量/矩阵,其中元素i
是f(vec[i])
, ,如果给您矩阵f
具有多元素输出lapply(vec, f)
: : 如同sapply
, ,但是输出是列表吗?apply(matrix, 1/2, f)
: :输入是矩阵。输出是向量,其中元素i
是f(矩阵的行/Col I)tapply(vector, grouping, f)
: :输出是矩阵/数组,其中矩阵/数组中的元素是f
在分组g
矢量,以及g
被推到行/col名称by(dataframe, grouping, f)
: : 让g
成为分组。申请f
到组/数据框的每一列。漂亮打印分组和价值f
在每列。aggregate(matrix, grouping, f)
: : 如同by
, ,但是,聚合不会将所有内容都插入数据框架中,而不是漂亮的打印输出。
附带问题:我仍然没有学会Plyr或Reshape-会 plyr
或者 reshape
完全替换所有这些?
解决方案
R有许多 *应用功能,这些功能在帮助文件中得到了很好的描述(例如 ?apply
)。但是,有足够的时间,开始用户可能难以确定哪个适合他们的情况,甚至记住所有这些情况。他们可能有一个普遍的感觉:“我应该在这里使用 *应用功能”,但是一开始就很难保持它们。
尽管事实(在其他答案中指出), *应用家庭的许多功能都受到了极为流行的范围 plyr
包装,基本功能仍然有用且值得了解。
这个答案旨在作为一种 路标 为了使新用户帮助他们转到正确的 *适用于其特定问题的功能。注意,这是 不是 打算简单地反思或替换R文档!希望此答案可以帮助您确定哪种 *应用功能适合您的情况,然后由您进行进一步研究。除了一个例外,绩效差异将无法解决。
申请 - 当您想将函数应用于矩阵的行或列(和较高维的类似物)时;通常不建议使用数据框架,因为它将首先迫使矩阵。
# 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 - 当您要依次将函数应用于列表的每个元素并恢复列表时。
这是许多其他 *应用功能的主力。剥离他们的代码,您经常会发现
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))
如果我们的函数返回2维矩阵,
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
但是,也许需要从代码中挤出更多速度。为了
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
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
地图 - 包装纸
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
愉快 - 当您想将函数应用于一个 嵌套列表 结构,递归。
给您一些关于多么罕见的想法
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")
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
可以处理更复杂的示例,如果亚组由几个因素列表的唯一组合定义。
tapply
在精神上与r中常见的分裂合并函数相似(aggregate
,by
,ave
,ddply
, 等等)因此,它的黑羊身份。
其他提示
在旁注,这是各种 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
目标之一 plyr
是为每个功能提供一致的命名约定,并在功能名称中编码输入和输出数据类型。它还提供了输出的一致性,在该输出中 dlply()
很容易传递给 ldply()
产生有用的输出,等等。
从概念上讲,学习 plyr
比理解基础更困难 *apply
职能。
plyr
和 reshape
在我每天使用中,功能几乎替换了所有这些功能。但是,也从简介到Plyr文档:
相关功能
tapply
和sweep
没有相应的功能plyr
, ,并保持有用。merge
对于将摘要与原始数据相结合非常有用。
从幻灯片21 http://www.slideshare.net/hadley/plyr-one-data-analyalytic-strategy:
(希望很明显 apply
对应于 @Hadley的 aaply
和 aggregate
对应于 @Hadley的 ddply
等等。如果您不从此图像中获得同一幻灯片的20张滑梯将澄清。)
(左侧是输入,顶部输出)
首先 乔兰的出色答案 - 可疑的一切都会更好。
那么以下助记符可能有助于记住每种助记符。虽然有些显而易见,但有些人可能不那么 - - 因为这些在Joran的讨论中可以找到理由。
mnemonics
lapply
是一个 列表 应用在列表或向量上的作用并返回列表。sapply
是一个 简单的lapply
(函数默认为返回向量或矩阵)vapply
是一个 已验证的适用 (允许预先指定返回对象类型)rapply
是一个 递归 申请嵌套列表,即列表中的列表tapply
是一个 标记 应用标签标识子集的地方apply
是 通用的: :将函数应用于矩阵的行或列(或更一般地,将其函数用于数组的尺寸)
建立正确的背景
如果使用 apply
家庭对您仍然有点陌生,那么您可能缺少关键的观点。
这两篇文章可以提供帮助。他们提供了必要的背景来激励 功能编程技术 由 apply
功能家族。
LISP的用户将立即识别范式。如果您不熟悉LISP,一旦您围绕FP,您就会获得有力的观点,可用于R-和 apply
将变得更有意义。
由于我意识到(非常出色的)这个帖子的答案缺乏 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
如果我们打印这两个对象, 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说,论点必须具有相同的长度,说“我们要计算 summary
所有变量 iris
沿着因素 Species
”:但是R无法做到这一点,因为它不知道如何处理。
与 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
它确实有效,结果非常令人惊讶。这是类的对象 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
两个直接区别是第二个论点 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
功能未测量。
我已经测试了两个不同的功能 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
# 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
也许值得一提 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
对于整个数据帧(AS 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
## ...
尽管这里有很多很棒的答案,但还有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
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,"-")
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
NB:对于这个简单的示例,当然可以更轻松地实现相同的结果
apply(dataPoints, 2, scale)