Функции группировки (tapply, by, aggregate) и семейство * apply
всякий раз, когда я хочу сделать что-то "map"py в R, я обычно пытаюсь использовать функцию в apply
семья.
однако я никогда до конца не понимал различий между ними-как {sapply
, lapply
, etc.} примените функцию к входу / сгруппированному входу, как будет выглядеть выход или даже каким может быть вход-поэтому я часто просто просматриваю их все, пока не получу то, что хочу.
может кто-нибудь объяснить, как использовать, какой, когда?
мой текущее (возможно, неправильное/неполное) понимание...
sapply(vec, f)
: входной сигнал является вектором. выход-это вектор / Матрица, где elementi
isf(vec[i])
, давая вам матрицу, еслиf
имеет многоэлементный выходlapply(vec, f)
: какsapply
, но вывод список?-
apply(matrix, 1/2, f)
: вход является матрицей. выход-это вектор, где elementi
является f (строка / col I матрицы) -
tapply(vector, grouping, f)
: выход-это матрица / массив, где элементом матрицы / массива является значениеf
в группуg
вектор, аg
получает толкнул в строке / col имена -
by(dataframe, grouping, f)
: даg
быть группировка. применитьf
для каждого столбца группы/Таблицы данных. довольно распечатать группировку и значениеf
в каждом столбце. -
aggregate(matrix, grouping, f)
: аналогичноby
, но вместо того, чтобы довольно печатать вывод, aggregate вставляет все в фрейм данных.
боковой вопрос: я до сих пор не изучил plyr или reshape -- would plyr
или reshape
заменить все это полностью?
9 ответов
R имеет много * применить функции, которые умело описаны в файлах справки (например,?apply
). Однако их достаточно, чтобы начинающим пользователям было трудно решить, какой из них подходит для их ситуации, или даже запомнить их все. У них может быть общее ощущение ,что" я должен использовать функцию *apply здесь", но сначала может быть трудно держать их все прямо.
несмотря на то, что (отмечено в других ответах) большая часть функциональности * применить семья охвачена чрезвычайно популярным plyr
пакет, базовые функции остаются полезными и стоит знать.
этот ответ призван действовать как своего рода указатель для новых пользователей, чтобы помочь направить их к правильной * применить функцию для их конкретной проблемы. Обратите внимание, это не предназначен для простого отрыгивания или замены документации 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
если вы хотите, чтобы строка / столбец означали или суммы для 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
-
поставка - когда вы хотите применить функцию к каждому элементу список, в свою очередь, но вы хотите вектор назад, а не список.
если вы обнаружите, что набираете
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 - для Когда вы имеете несколько структуры данных (например, векторы, списки), и вы хотите применить функцию к 1-м элементам каждого, а затем 2-го элемента каждого и т. д., принуждая результат к вектору / массиву, как в
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 - когда вы хотите применить функцию к каждому элементу вложенного списка структура, рекурсивно.
чтобы дать вам некоторое представление о том, как редкость
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 - когда вы хотите применить функцию к подмножеств в вектор и подмножества определяются некоторым другим вектором, обычно a факторный.
паршивая овца * применить семьи, своего рода. Использование файла справки фраза "ragged array" может быть немного заблуждение, но это на самом деле совсем простой.
A вектор:
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
is подобно по духу функциям split-apply-combine, которые общие в R (aggregate
,by
,ave
,ddply
, etc.) Следовательно свой статус паршивой овцы.
на боковой ноте, вот как различные 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()
произвести полезный выход, etc.
концептуально, обучение plyr
не сложнее, чем понять базу *apply
функции.
plyr
и reshape
функции заменили почти все эти функции в моем ежедневном использовании. Но, также из вступления к документу Plyr:
соответствующие функции
tapply
иsweep
не имеют соответствующей функции вplyr
, и остаются полезными.merge
полезно для объединения резюме с исходными данными.
из слайда 21 http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy:
(надеюсь, это ясно, что apply
соответствует @ и aggregate
соответствует @ etc. Слайд 20 того же slideshare прояснит, если вы не получите его из этого изображения.)
(слева-вход, вверху-выход)
начинается с отличный ответ Джорана -- сомнительно, что что-нибудь может лучше этого.
тогда следующая мнемоника может помочь запомнить различия между ними. Хотя некоторые из них очевидны, другие могут быть менее очевидны-для них вы найдете оправдание в дискуссиях Джорана.
Мнемоника
-
lapply
Это список применить, который действует от список или вектор и возвращает список. -
sapply
это простойlapply
(функция по умолчанию возвращает вектор или матрицу, когда это возможно) -
vapply
- это проверил применить (позволяет заранее определить тип возвращаемого объекта) -
rapply
это рекурсивные применить для вложенных списков, т. е. списков в списках -
tapply
это тегом применить, где теги идентифицируют подмножества -
apply
is generic: применяет функцию к строкам или столбцам матрицы (или, в более общем плане, к размерам массива)
создание правильного фона
при использовании apply
семья все еще чувствует себя немного чуждым для вас, то это может быть, что вы не хватает ключевой точки зрения.
эти две статьи могут помочь. Они обеспечивают необходимую основу для мотивации методы функционального программирования что предоставляются apply
семейство функций.
пользователи Lisp сразу распознают парадигму. Если вы не знакомы с Lisp, как только вы получите свою голову вокруг FP, вы получите мощную точку зрения для использования в R - и apply
будет иметь гораздо больше смысла.
- Расширенный R: Функциональное Программирование, by Hadley Wickham
- простое функциональное программирование в R, Майкл Бартон!--24-->
так как я понял, что (очень отличные) ответы на этот пост не хватает by
и aggregate
объяснения. Вот мой вклад.
BY
на 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
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
мы можем добиться того же с 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)
в других случаях результаты одинаковы, и различия только в классе (а затем, как он показан / напечатан, а не только -- пример, как его подмножество) object:
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
предыдущий код достигает той же цели и результатов, в некоторые моменты Какой инструмент использовать просто вопрос личных вкусов и потребностей; предыдущие два объекта имеют очень разные потребности с точки зрения подстановки.
есть много отличных ответов, которые обсуждают различия в вариантах использования для каждой функции. Ни один из ответов не обсуждает различия в производительности. Это разумная причина, по которой различные функции ожидают различных входных данных и производят различные выходные данные, однако большинство из них имеют общую цель для оценки по рядам/группам. Мой ответ будет сосредоточен на производительности. Из-за выше создание входных данных из векторов включено в сроки, а также
это, возможно, стоит упомянуть 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)