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:

  1. Wymagaj mniej kodu do napisania.
  2. Nie ma licznika iteracji.
  3. 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 co lapply() ale próbuje uprościć wyjście do wektora lub macierzy.
    • vapply() = wariant sapply() w którym należy określić typ obiektu wyjściowego.
  • mapply() = like lapply() ale może przekazywać wiele wektorów jako dane wejściowe do określonej funkcji. Można uprościć jak sapply() .
    • Map() to alias do mapply() z mapply() 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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow