Suche…


Bemerkungen

Eine Funktion in der *apply Familie ist eine Abstraktion einer for Schleife. Gegenüber den for Loops-Funktionen haben *apply Funktionen folgende Vorteile:

  1. Erfordert weniger Code zum Schreiben.
  2. Hat keinen Iterationszähler.
  3. Verwendet temporäre Variablen nicht zum Speichern von Zwischenergebnissen.

for Schleifen sind jedoch allgemeiner und geben uns mehr Kontrolle, sodass komplexe Berechnungen durchgeführt werden können, die mit *apply Funktionen nicht immer trivial sind.

Die Beziehung zwischen for Schleifen und *apply Funktionen wird in der Dokumentation für for Schleifen erläutert.

Mitglieder der *apply Familie

Die *apply Funktionsfamilie enthält mehrere Varianten desselben Prinzips, die sich hauptsächlich in der Art der ausgegebenen Ausgabe unterscheiden.

Funktion Eingang Ausgabe
apply matrix , data.frame oder array Vektor oder Matrix (abhängig von der Länge jedes zurückgegebenen Elements)
sapply Vektor oder list Vektor oder Matrix (abhängig von der Länge jedes zurückgegebenen Elements)
lapply Vektor oder list list
vapply Vektor oder `Liste Vektor oder Matrix (abhängig von der Länge jedes zurückgegebenen Elements) der benutzerdefinierten Klasse
mapply mehrere Vektoren, lists oder eine Kombination list

Siehe "Beispiele", um zu sehen, wie jede dieser Funktionen verwendet wird.

Verwenden Sie anonyme Funktionen mit apply

apply wird verwendet, um eine Funktion (möglicherweise eine anonyme) über die Ränder eines Arrays oder einer Matrix auszuwerten.

Verwenden Sie das iris Dataset, um diese Idee zu veranschaulichen. Der iris Datensatz hat Messungen von 150 Blumen von 3 Arten. Mal sehen, wie dieser Datensatz aufgebaut ist:

> 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

Stellen Sie sich vor, Sie möchten den Mittelwert jeder dieser Variablen kennen. Eine Möglichkeit, dieses Problem zu lösen, könnte die Verwendung einer for Schleife sein, aber R-Programmierer bevorzugen oft die Verwendung von " apply (Gründe, siehe Anmerkungen):

> apply(iris[1:4], 2, mean)

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333
  • Im ersten Parameter setzen wir die Untermenge iris , dass sie nur die ersten 4 Spalten enthält, da der mean nur für numerische Daten gilt.
  • Der zweite Parameterwert von 2 gibt an, dass wir nur an den Spalten arbeiten möchten (der zweite Index des Arrays r × c). 1 würde die Zeile bedeuten.

Auf die gleiche Weise können wir aussagekräftigere Werte berechnen:

# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)

Einschränkung : R hat einige eingebaute Funktionen, die besser zur Berechnung von Spalten- und Reihensummen und colMeans rowMeans sind: colMeans und rowMeans .

Nun machen wir eine andere und sinnvollere Aufgabe: Berechnen Sie den Mittelwert nur für die Werte, die größer als 0.5 . Dafür werden wir unsere eigene erstellen mean Funktion.

> 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

(Beachten Sie den Unterschied im Mittelwert von Petal.Width )

Was aber, wenn wir diese Funktion nicht im Rest unseres Codes verwenden möchten? Dann können wir eine anonyme Funktion verwenden und unseren Code folgendermaßen schreiben:

apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })

Wie wir gesehen haben, können wir mit apply die gleiche Operation für Spalten oder Zeilen eines Datasets mit nur einer Zeile ausführen.

Einschränkung : Da apply je nach Länge der Ergebnisse der angegebenen Funktion sehr unterschiedliche Ausgabearten liefert, ist dies möglicherweise nicht die beste Wahl, wenn Sie nicht interaktiv arbeiten. Einige der anderen *apply Familienfunktionen sind etwas vorhersagbarer (siehe Anmerkungen).

Massendatei wird geladen

für eine große Anzahl von Dateien, die möglicherweise in einem ähnlichen Prozess und mit gut strukturierten Dateinamen bearbeitet werden müssen.

Zunächst muss ein Vektor der Dateinamen erstellt werden, auf die zugegriffen werden soll. Dazu gibt es mehrere Optionen:

  • Vektor manuell mit paste0()

     files <- paste0("file_", 1:100, ".rds")
    
  • Mit list.files() mit einem regex Suchbegriff für den Dateityp, erfordert die Kenntnis von regulären Ausdrücken ( regex ) , wenn andere Dateien gleichen Typs im Verzeichnis befinden.

     files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
    

Dabei ist X ein Vektor eines Teils des verwendeten Dateinamensformats.

lapply gibt jede Antwort als Element einer Liste aus.

readRDS ist spezifisch für .rds Dateien und ändert sich je nach Anwendung des Prozesses.

my_file_list <- lapply(files, readRDS)

Dies ist nicht notwendigerweise schneller als eine for-Schleife von testing, sondern ermöglicht, dass alle Dateien ein Element einer Liste sind, ohne sie explizit zuzuweisen.

Schließlich müssen wir häufig mehrere Pakete gleichzeitig laden. Mit diesem Trick können Sie library() auf alle Bibliotheken anwenden, die wir importieren möchten:

lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)

Mehrere `data.frames` kombinieren (` lapply`, `mapply`)

In dieser Übung werden vier lineare Bootstrap-Regressionsmodelle generiert und die Zusammenfassungen dieser Modelle in einem einzigen Datenrahmen kombiniert.

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)))

An dieser Stelle können wir zwei Methoden zum Einfügen der Namen in den data.frame verwenden.

#* 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)

Wenn Sie ein Fan von magrittr Stil-Pipes sind, können Sie die gesamte Aufgabe in einer einzelnen Kette magrittr (dies ist jedoch möglicherweise nicht ratsam, wenn Sie Zwischenobjekte wie die Modellobjekte selbst benötigen):

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", .)

Eingebaute Funktionale verwenden

Integrierte Funktionen: lapply (), sapply () und mapply ()

R verfügt über integrierte Funktionen, von denen die bekannteste wahrscheinlich die anwendbare Funktionsfamilie ist. Hier finden Sie eine Beschreibung einiger der häufigsten Anwendungsfunktionen:

  • lapply() = nimmt eine Liste als Argument und wendet die angegebene Funktion auf die Liste an.
  • sapply() = dasselbe wie lapply() , versucht jedoch die Ausgabe in einen Vektor oder eine Matrix zu vereinfachen.
    • vapply() = eine Variante von sapply() in der der Typ des Ausgabeobjekts angegeben werden muss.
  • mapply() = wie lapply() , kann jedoch mehrere Vektoren als Eingabe an die angegebene Funktion übergeben. Kann wie sapply() vereinfacht werden.
    • Map() ist ein Alias ​​für mapply() mit SIMPLIFY = FALSE .

lapply ()

lapply() kann mit zwei verschiedenen Iterationen verwendet werden:

  • 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() versucht, seine Ausgabe in einen Vektor oder eine Matrix aufzulösen.

# 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() funktioniert ähnlich wie lapply() außer dass es mehrere Vektoren als Eingabe lapply() kann (daher das m für multivariate).

mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply

Verwendung benutzerdefinierter Funktionen

Benutzerdefinierte Funktionen

Benutzer können ihre eigenen Funktionen in unterschiedlichem Umfang erstellen. Die folgenden Beispiele stammen von Functionals by Hadley Wickham:

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
}

Im ersten Fall akzeptiert randomise ein einzelnes Argument f und ruft es für eine Stichprobe von Uniform-Zufallsvariablen auf. Um die Äquivalenz zu demonstrieren, rufen wir set.seed unten auf:

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

Das zweite Beispiel ist eine Neuimplementierung von base::lapply , die mithilfe von Funktionen eine Operation ( f ) auf jedes Element in einer Liste ( x ) anwendet. Mit dem Parameter ... kann der Benutzer zusätzliche Argumente an f , z. B. die Option na.rm in der Funktion 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow