Die apply-Familie in einem Satz
Jedes Mitglied der apply-Familie erledigt dieselbe Aufgabe: eine Funktion nehmen und sie über jedes Element einer Struktur ausführen, dabei die Ergebnisse sammeln. Was variiert, sind Eingabeform und Ausgabeform. Die Übersicht:
apply(m, MARGIN, FUN)- Zeilen oder Spalten einer Matrix.lapply(x, FUN)- jedes Element einer Liste oder eines Vektors; gibt immer eine Liste zurück.sapply(x, FUN)- wielapply, vereinfacht danach zu einem Vektor oder einer Matrix, wenn möglich.vapply(x, FUN, FUN.VALUE)-sapplymit deklariertem, erzwungenem Rückgabetyp.mapply(FUN, x, y, ...)- läuft mehrere Eingaben parallel ab, elementweise.tapply(values, groups, FUN)- wendetFUNinnerhalb jeder Gruppe an: ein Ergebnis pro Gruppe.
Sie alle akzeptieren die Funktionen, die du selbst schreibst, auch anonyme - daher bezieht die Familie ihre Kraft.
apply(): Zeilen und Spalten einer Matrix
apply nimmt eine Matrix, ein MARGIN (1 für Zeilen, 2 für Spalten) und eine Funktion:
Die Matrix füllt spaltenweise, also lauten die Zeilen 1 3 5 und 2 4 6: Die Zeilensummen erscheinen als 9 12 und die Spaltensummen als 3 7 11. Für die beiden häufigsten Fälle bringt Base R eigene, schnellere Helfer mit - rowSums, colSums, rowMeans, colMeans -, reserviere apply also für Funktionen ohne solchen Helfer, etwa apply(m, 2, max).
Eine Falle: apply auf einem Data Frame wandelt diesen still zuerst in eine Matrix um, was jede Spalte auf einen gemeinsamen Typ konvertiert. Bei Data Frames behandle die Spalten als Liste und nutze stattdessen lapply/sapply.
lapply() gibt immer eine Liste, sapply() vereinfacht
lapply bildet eine Funktion über jedes Element ab und gibt eine Liste gleicher Länge zurück, komme was wolle:
Dieselbe Berechnung, zwei Formen: lapply gibt eine Liste mit 85 und 80 zurück; sapply bemerkt, dass jedes Ergebnis Länge 1 hat, und vereinfacht zu einem benannten numerischen Vektor - viel angenehmer zu lesen und in weitere Rechnung zu geben.
Die Bequemlichkeit hat allerdings eine scharfe Kante: Der Rückgabetyp von sapply hängt von den Daten ab. Erzeugt auch nur ein Element eine andere Länge, scheitert die Vereinfachung, und du bekommst still wieder eine Liste:
Interaktiv zuckt man mit den Schultern; in einem Skript bedeutet es, dass Code, der das ganze Jahr lief, an dem Tag bricht, an dem die Daten ihre Form ändern. Genau dafür existiert vapply.
vapply(): das typsichere sapply
vapply ergänzt ein drittes Argument, FUN.VALUE: eine Vorlage, die erklärt, wie ein Ergebnis aussehen muss. integer(1) bedeutet "jeder Aufruf gibt genau einen Integer zurück":
Du bekommst einen benannten Integer-Vektor - 5 6 6 - und, wichtiger, eine Garantie: Gäbe nchar je zwei Werte oder ein character zurück, würde vapply an der Aufrufstelle mit einem Fehler anhalten, statt ein fehlgeformtes Ergebnis weiterschwimmen zu lassen. Die Deklaration dient zugleich als Dokumentation:
vapply(words, nchar, character(1))
# Error in vapply(words, nchar, character(1)) : values must be type 'character'
Faustregel: sapply an der Konsole, vapply in Funktionen und Skripten, die unbeaufsichtigt laufen müssen.
mapply() und tapply(): parallele Eingaben und Gruppen
lapply läuft eine Struktur ab. Wenn jeder Aufruf ein Element aus mehreren Strukturen an passenden Positionen braucht, nutze mapply - beachte, dass die Funktion zuerst kommt:
Jedes Ausgabeelement paart die ersten Werte, dann die zweiten Werte und so weiter: 10 200 3000 40000.
tapply ist das gruppenweise Mitglied: Es teilt values nach groups auf und wendet die Funktion innerhalb jeder Gruppe an, mit einem Ergebnis pro Gruppe:
Gruppe a mittelt zu 30, Gruppe b zu 40. Das ist Base R's Antwort auf "Durchschnitt pro Kategorie" - dieselbe Art von Frage, die group_by + summarize für Data Frames beantwortet. Bist du also schon im dplyr-Gebiet, nimm das; tapply glänzt, wenn du zwei nackte Vektoren hast und eine Zeile willst.
Zwei Bequemlichkeiten funktionieren in der ganzen Familie. Zusätzliche Argumente nach der Funktion werden bei jedem Aufruf an sie weitergereicht:
Und anonyme Funktionen passen überall dort, wo keine fertige Funktion passt - sapply(x, \(v) max(v) - min(v)) berechnet eine Spannweite pro Element, ohne vorher einen Helfer zu benennen.
apply-Familie vs. for-Schleifen
Du wirst hören, dass for-Schleifen in R langsam und die apply-Familie schnell sei. Das ist größtenteils ein Mythos. Wirklich langsam ist es, ein Ergebnis in einer Schleife wachsen zu lassen - result <- c(result, new_value) kopiert bei jedem Durchlauf den ganzen Vektor. Eine Schleife, die ihre Ausgabe vorbelegt, läuft gut:
Entscheide also nach Lesbarkeit, nicht nach Geschwindigkeit. Die apply-Fassung sagt das Was in einer Zeile - "quadriere jedes Element" - und übernimmt die Speicherverwaltung für dich, weshalb sie das Idiom für unkomplizierte elementweise Arbeit ist. Eine for-Schleife verdient sich ihr Geld, wenn Iterationen von früheren Ergebnissen abhängen, wenn du frühe Ausstiege mit break brauchst oder wenn der Rumpf so lang ist, dass ein Lambda mehr schadet als hilft. Beides ist legitimes R.
Was du mitnimmst
- Die ganze Familie ist eine Idee: eine Funktion über jedes Element ausführen, die Ergebnisse sammeln.
applyist für Matrixzeilen (MARGIN = 1) und -spalten (MARGIN = 2); bevorzugerowSums/colMeans, wo es sie gibt.lapplygibt immer eine Liste zurück;sapplyvereinfacht, wenn es kann - was bedeutet, dass sein Rückgabetyp mit den Daten wechseln kann.vapplynagelt den Rückgabetyp fest; nutze es in Code, der dich nicht überraschen darf.mapplyreißverschlusst mehrere Eingaben;tapplyaggregiert Werte innerhalb von Gruppen.- for-Schleifen sind nicht per se langsam - wachsende Vektoren sind es. Wähle die Schreibweise, die sich am besten liest.
Als Nächstes: Pipes - der Operator, der diese Aufrufe zu lesbaren, schrittweisen Pipelines verkettet.
Häufig gestellte Fragen
Was ist der Unterschied zwischen lapply und sapply in R?
lapply gibt immer eine Liste zurück, ohne Ausnahme. sapply führt dieselbe Berechnung aus und versucht dann, das Ergebnis zu vereinfachen - zu einem Vektor, wenn jedes Element Länge 1 hat, zu einer Matrix, wenn sie eine Länge teilen, und zurück zu einer Liste, wenn es nicht geht. sapply ist interaktiv angenehmer; lapply (oder vapply) ist in Skripten sicherer, weil sich sein Rückgabetyp nie ändert.
Was macht apply() in R?
apply(m, MARGIN, FUN) führt FUN über eine Matrix aus: MARGIN = 1 wendet es auf jede Zeile an, MARGIN = 2 auf jede Spalte. apply(m, 1, sum) liefert Zeilensummen, apply(m, 2, mean) Spaltenmittelwerte. Es ist für Matrizen und Arrays gedacht - für Listen und Vektoren nutze stattdessen lapply/sapply.
Ist die apply-Familie schneller als eine for-Schleife in R?
Meist nicht nennenswert - das ist ein Mythos. Eine gut geschriebene for-Schleife mit vorbelegtem Ergebnisvektor ist vergleichbar schnell. Die Schleifen, die for in Verruf brachten, lassen ihr Ergebnis Element für Element wachsen und kopieren bei jedem Durchlauf alles. Wähle apply-Funktionen wegen Kürze und Absicht, nicht wegen Geschwindigkeit.
Wofür wird vapply in R verwendet?
vapply ist sapply mit Vertrag: Du deklarierst Typ und Länge jedes Ergebnisses, z. B. vapply(x, nchar, integer(1)). Gibt die Funktion etwas anderes zurück, wirft R sofort einen Fehler, statt dir still eine unerwartete Struktur zu reichen. Bevorzuge es in Code, der unbeaufsichtigt weiterlaufen muss.