Zastosuj funkcję do każdego wiersza macierzy lub ramki danych
Załóżmy, że mam macierz n przez 2 i funkcję, która przyjmuje 2-wektor jako jeden ze swoich argumentów. Chciałbym zastosować funkcję do każdego rzędu macierzy i otrzymać n-wektor. Jak to zrobić w R?
Na przykład, chciałbym obliczyć gęstość 2D standardowego rozkładu normalnego w trzech punktach:
bivariate.density(x = c(0, 0), mu = c(0, 0), sigma = c(1, 1), rho = 0){
exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+x[2]^2/sigma[2]^2-2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
}
out <- rbind(c(1, 2), c(3, 4), c(5, 6))
Jak zastosować funkcję do każdego wiersza out
?
Jak przekazać wartości dla innych argumentów oprócz Punktów do funkcji w sposób określony?
6 answers
Wystarczy użyć funkcji apply()
:
R> M <- matrix(1:6, nrow=3, byrow=TRUE)
R> M
[,1] [,2]
[1,] 1 2
[2,] 3 4
[3,] 5 6
R> apply(M, 1, function(x) 2*x[1]+x[2])
[1] 4 10 16
R>
To pobiera macierz i stosuje (głupie) funkcje do każdego wiersza. Przekazujesz dodatkowe argumenty do funkcji jako czwarty, piąty, ... argumenty do apply()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-21 04:14:27
W przypadku, gdy chcesz zastosować typowe funkcje, takie jak sum lub mean, powinieneś użyć rowSums
lub rowMeans
, ponieważ są one szybsze niż podejście apply(data, 1, sum)
. W przeciwnym razie trzymaj się apply(data, 1, fun)
. Możesz przekazać dodatkowe argumenty po zabawnym argumencie (jak Dirk już zasugerował):
set.seed(1)
m <- matrix(round(runif(20, 1, 5)), ncol=4)
diag(m) <- NA
m
[,1] [,2] [,3] [,4]
[1,] NA 5 2 3
[2,] 2 NA 2 4
[3,] 3 4 NA 5
[4,] 5 4 3 NA
[5,] 2 1 4 4
Wtedy możesz zrobić coś takiego:
apply(m, 1, quantile, probs=c(.25,.5, .75), na.rm=TRUE)
[,1] [,2] [,3] [,4] [,5]
25% 2.5 2 3.5 3.5 1.75
50% 3.0 2 4.0 4.0 3.00
75% 4.0 3 4.5 4.5 4.00
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-21 18:14:25
Oto krótki przykład zastosowania funkcji do każdego rzędu macierzy. (Tutaj zastosowana funkcja normalizuje każdy wiersz do 1.)
Uwaga: wynik z apply()
musiał być przetransponowany używając t()
, aby uzyskać taki sam układ jak macierz wejściowa A
.
A <- matrix(c(
0, 1, 1, 2,
0, 0, 1, 3,
0, 0, 1, 3
), nrow = 3, byrow = TRUE)
t(apply(A, 1, function(x) x / sum(x) ))
Wynik:
[,1] [,2] [,3] [,4]
[1,] 0 0.25 0.25 0.50
[2,] 0 0.00 0.25 0.75
[3,] 0 0.00 0.25 0.75
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-04 12:37:14
Pierwszym krokiem byłoby stworzenie obiektu funkcji, a następnie zastosowanie go. Jeśli chcesz mieć obiekt matrix, który ma taką samą liczbę wierszy, możesz go predefiniować i użyć postaci obiektu [] jak na ilustracji (w przeciwnym razie zwracana wartość zostanie uproszczona do wektora):
bvnormdens <- function(x=c(0,0),mu=c(0,0), sigma=c(1,1), rho=0){
exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+
x[2]^2/sigma[2]^2-
2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) *
1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
}
out=rbind(c(1,2),c(3,4),c(5,6));
bvout<-matrix(NA, ncol=1, nrow=3)
bvout[] <-apply(out, 1, bvnormdens)
bvout
[,1]
[1,] 1.306423e-02
[2,] 5.931153e-07
[3,] 9.033134e-15
Jeśli chcesz użyć innych parametrów niż domyślne, wywołanie powinno zawierać nazwane argumenty po funkcji:
bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6)
Apply () może być również używane na wyższych tablicach wymiarowych, a argument MARGIN może być zarówno wektorem, jak i pojedynczą liczbą całkowitą.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-21 15:01:39
Apply sprawdza się dobrze, ale jest dość powolny. Użycie sapply i vapply może być przydatne. rowwise dplyra może być również przydatne Zobaczmy przykład jak zrobić wiersz mądry produkt dowolnej ramki danych.
a = data.frame(t(iris[1:10,1:3]))
vapply(a, prod, 0)
sapply(a, prod)
Zauważ, że przypisanie zmiennej przed użyciem vapply / sapply / apply jest dobrą praktyką, ponieważ znacznie skraca czas. Zobaczmy wyniki microbenchmark
a = data.frame(t(iris[1:10,1:3]))
b = iris[1:10,1:3]
microbenchmark::microbenchmark(
apply(b, 1 , prod),
vapply(a, prod, 0),
sapply(a, prod) ,
apply(iris[1:10,1:3], 1 , prod),
vapply(data.frame(t(iris[1:10,1:3])), prod, 0),
sapply(data.frame(t(iris[1:10,1:3])), prod) ,
b %>% rowwise() %>%
summarise(p = prod(Sepal.Length,Sepal.Width,Petal.Length))
)
Przyjrzyj się uważnie, jak t() jest używane
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-29 15:32:29
Innym podejściem, jeśli chcesz użyć różnej części zbioru danych zamiast pojedynczej wartości, jest użycie rollapply(data, width, FUN, ...)
. Użycie wektora szerokości pozwala na zastosowanie funkcji w zmieniającym się oknie zbioru danych. Użyłem tego do zbudowania procedury filtrowania adaptacyjnego, choć nie jest to zbyt wydajne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-30 06:34:27