R Language
* zastosuj rodzinę funkcji (funkcjonałów)
Szukaj…
Uwagi
Funkcja w rodzinie *apply
jest abstrakcją pętli for
. W porównaniu z pętlami for
*apply
funkcje *apply
mają następujące zalety:
- Wymagaj mniej kodu do napisania.
- Nie ma licznika iteracji.
- Nie używa zmiennych tymczasowych do przechowywania wyników pośrednich.
Jednak for
pętli są one bardziej ogólne i mogą dać nam większą kontrolę, pozwalając na uzyskanie złożonych obliczeń, które nie zawsze są trywialne przy użyciu funkcji *apply
.
Zależność między for
pętli i *apply
funkcje są wyjaśnione w dokumentacji for
pętli .
Członkowie *apply
rodzinę
Rodzina funkcji *apply
zawiera kilka wariantów tej samej zasady, które różnią się głównie w zależności od rodzaju zwracanych danych wyjściowych.
funkcjonować | Wejście | Wynik |
---|---|---|
apply | matrix , data.frame lub array | wektor lub macierz (w zależności od długości każdego zwracanego elementu) |
sapply | wektor lub list | wektor lub macierz (w zależności od długości każdego zwracanego elementu) |
lapply | wektor lub list | list |
vapply | wektor lub `lista | wektor lub macierz (w zależności od długości każdego zwracanego elementu) klasy wyznaczonej przez użytkownika |
mapply | wiele wektorów, lists lub kombinacji | list |
Zobacz „Przykłady”, aby zobaczyć, jak używana jest każda z tych funkcji.
Korzystaj z anonimowych funkcji
apply
stosuje się do oceny funkcji (być może anonimowej) na marginesach tablicy lub macierzy.
Użyjmy zestawu danych iris
aby zilustrować ten pomysł. Zestaw danych iris
ma pomiary 150 kwiatów z 3 gatunków. Zobaczmy, jak wygląda ten zestaw danych:
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
Teraz wyobraź sobie, że chcesz poznać średnią każdej z tych zmiennych. Jednym ze sposobów rozwiązania tego problemu może być użycie pętli for
, ale programiści z R często wolą stosować apply
(z powodów, dla których zobacz uwagi):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- W pierwszym parametrze podzieliliśmy
iris
aby uwzględnić tylko pierwsze 4 kolumny, ponieważmean
działa tylko na danych liczbowych. - Drugi parametr o wartości
2
wskazuje, że chcemy pracować tylko na kolumnach (drugi indeks dolny tablicy r × c);1
dałoby środki rzędu.
W ten sam sposób możemy obliczyć bardziej znaczące wartości:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Zastrzeżenie : R ma kilka wbudowanych funkcji, które są lepsze do obliczania sum kolumn i wierszy oraz oznacza: colMeans
i rowMeans
.
Teraz zróbmy inne i bardziej znaczące zadanie: obliczmy średnią tylko dla tych wartości, które są większe niż 0.5
. W tym celu stworzymy naszą własną mean
funkcję.
> our.mean.function <- function(x) { mean(x[x > 0.5]) }
> apply(iris[1:4], 2, our.mean.function)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.665347
(Zwróć uwagę na różnicę w średniej Petal.Width
)
Ale co, jeśli nie chcemy używać tej funkcji w pozostałej części naszego kodu? Następnie możemy użyć anonimowej funkcji i napisać nasz kod w następujący sposób:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Tak więc, jak widzieliśmy, możemy apply
aby wykonać tę samą operację na kolumnach lub wierszach zestawu danych, używając tylko jednej linii.
Zastrzeżenie : Ponieważ apply
zwraca bardzo różne rodzaje danych wyjściowych w zależności od długości wyników określonej funkcji, może nie być najlepszym wyborem w przypadkach, gdy nie pracujesz interaktywnie. Niektóre inne *apply
funkcje rodzinne są nieco bardziej przewidywalne (patrz Uwagi).
Zbiorcze ładowanie pliku
dla dużej liczby plików, które mogą wymagać obsługi w podobnym procesie i przy użyciu dobrze ustrukturyzowanych nazw plików.
po pierwsze należy utworzyć wektor nazw plików, które mają być dostępne, istnieje wiele opcji:
Ręczne tworzenie wektora za pomocą
paste0()
files <- paste0("file_", 1:100, ".rds")
Użycie
list.files()
z wyrażeniem regularnym dla typu pliku wymaga znajomości wyrażeń regularnych ( regex ), jeśli inne pliki tego samego typu znajdują się w katalogu.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
gdzie X
jest wektorem części używanego formatu nazewnictwa plików.
lapply
wyświetli każdą odpowiedź jako element listy.
readRDS
jest specyficzny dla plików .rds
i zmienia się w zależności od zastosowania procesu.
my_file_list <- lapply(files, readRDS)
To niekoniecznie jest szybsze niż testowanie pętli for, ale pozwala wszystkim plikom być elementem listy bez przypisywania ich jawnie.
Wreszcie często musimy załadować wiele pakietów jednocześnie. Ta sztuczka może to zrobić dość łatwo poprzez zastosowanie library()
do wszystkich bibliotek, które chcemy zaimportować:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Łączenie wielu `data.frames` (` lapply`, `mapply`)
W tym ćwiczeniu wygenerujemy cztery modele regresji liniowej bootstrap i połączymy podsumowania tych modeli w jedną ramkę danych.
library(broom)
#* Create the bootstrap data sets
BootData <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ])
#* Fit the models
Models <- lapply(BootData,
function(BD) lm(mpg ~ qsec + wt + factor(am),
data = BD))
#* Tidy the output into a data.frame
Tidied <- lapply(Models,
tidy)
#* Give each element in the Tidied list a name
Tidied <- setNames(Tidied, paste0("Boot", seq_along(Tidied)))
W tym momencie możemy zastosować dwa podejścia do wstawiania nazw do data.frame.
#* Insert the element name into the summary with `lapply`
#* Requires passing the names attribute to `lapply` and referencing `Tidied` within
#* the applied function.
Described_lapply <-
lapply(names(Tidied),
function(nm) cbind(nm, Tidied[[nm]]))
Combined_lapply <- do.call("rbind", Described_lapply)
#* Insert the element name into the summary with `mapply`
#* Allows us to pass the names and the elements as separate arguments.
Described_mapply <-
mapply(
function(nm, dframe) cbind(nm, dframe),
names(Tidied),
Tidied,
SIMPLIFY = FALSE)
Combined_mapply <- do.call("rbind", Described_mapply)
Jeśli jesteś fanem rur w stylu magrittr
, możesz wykonać całe zadanie w jednym łańcuchu (choć może to być rozsądne, jeśli potrzebujesz dowolnego z obiektów pośrednich, takich jak same obiekty modelu):
library(magrittr)
library(broom)
Combined <- lapply(1:4,
function(i) mtcars[sample(1:nrow(mtcars),
size = nrow(mtcars),
replace = TRUE), ]) %>%
lapply(function(BD) lm( mpg ~ qsec + wt + factor(am), data = BD)) %>%
lapply(tidy) %>%
setNames(paste0("Boot", seq_along(.))) %>%
mapply(function(nm, dframe) cbind(nm, dframe),
nm = names(.),
dframe = .,
SIMPLIFY = FALSE) %>%
do.call("rbind", .)
Korzystanie z wbudowanych funkcjonałów
Wbudowane funkcje: lapply (), sapply () i mapply ()
R ma wbudowane funkcje, z których być może najbardziej znanymi są rodzina zastosowanych funkcji. Oto opis niektórych z najczęściej stosowanych funkcji stosowania:
-
lapply()
= przyjmuje listę jako argument i stosuje określoną funkcję do listy. -
sapply()
= to samo colapply()
ale próbuje uprościć wyjście do wektora lub macierzy.-
vapply()
= wariantsapply()
w którym należy określić typ obiektu wyjściowego.
-
-
mapply()
= likelapply()
ale może przekazywać wiele wektorów jako dane wejściowe do określonej funkcji. Można uprościć jaksapply()
.-
Map()
to alias domapply()
zmapply()
SIMPLIFY = FALSE
.
-
lapply ()
lapply()
może być używany z dwiema różnymi iteracjami:
-
lapply(variable, FUN)
-
lapply(seq_along(variable), FUN)
# Two ways of finding the mean of x
set.seed(1)
df <- data.frame(x = rnorm(25), y = rnorm(25))
lapply(df, mean)
lapply(seq_along(df), function(x) mean(df[[x]))
sapply ()
sapply()
spróbuje sapply()
dane wyjściowe na wektor lub macierz.
# Two examples to show the different outputs of sapply()
sapply(letters, print) ## produces a vector
x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
sapply(x, quantile) ## produces a matrix
mapply ()
mapply()
działa podobnie jak lapply()
z tym że może pobierać wiele wektorów jako dane wejściowe (stąd m dla wielu odmian).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Korzystanie z funkcjonałów zdefiniowanych przez użytkownika
Funkcje funkcjonalne zdefiniowane przez użytkownika
Użytkownicy mogą tworzyć własne funkcjonale o różnym stopniu złożoności. Poniższe przykłady pochodzą z Funkcjonałów Hadleya Wickhama:
randomise <- function(f) f(runif(1e3))
lapply2 <- function(x, f, ...) {
out <- vector("list", length(x))
for (i in seq_along(x)) {
out[[i]] <- f(x[[i]], ...)
}
out
}
W pierwszym przypadku randomise
przyjmuje pojedynczy argument f
i wywołuje go na próbce jednolitych zmiennych losowych. Aby wykazać równoważność, wywołujemy set.seed
poniżej:
set.seed(123)
randomise(mean)
#[1] 0.4972778
set.seed(123)
mean(runif(1e3))
#[1] 0.4972778
set.seed(123)
randomise(max)
#[1] 0.9994045
set.seed(123)
max(runif(1e3))
#[1] 0.9994045
Drugi przykład to ponowna implementacja base::lapply
, która używa funkcjonałów do zastosowania operacji ( f
) do każdego elementu na liście ( x
). Parametr ...
pozwala użytkownikowi przekazać dodatkowe argumenty do f
, takie jak opcja na.rm
w funkcji mean
:
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
#
# [[2]]
# [1] NA
lapply(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4
lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
#
# [[2]]
# [1] 4