Menu

Häufige R-Fehler und wie du sie debuggst

Ein Entschlüsseler für R's klassische Fehlermeldungen - object not found, could not find function, non-numeric argument und Co. - dazu tryCatch, traceback() und ehrliches Print-Debugging.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Eine R-Fehlermeldung lesen

Ein R-Fehler hat zwei Teile, und beide sind nützlich:

Error in "10" + 5 : non-numeric argument to binary operator

Nach Error in folgt der Aufruf - genau das Stück Code, das scheiterte ("10" + 5). Nach dem Doppelpunkt folgt die Bedingung - was schiefging (non-numeric argument to binary operator). Lies zuerst den Aufruf: Er sagt dir das Wo, und sehr oft siehst du das Problem schon direkt im zitierten Code. Dann lies die Bedingung für das Warum.

Zwei Gewohnheiten trennen Menschen, die schnell debuggen, von Menschen, die leiden. Erstens: Lies die Meldung wirklich - R's Meldungen sind meist präzise, nur knapp formuliert. Zweitens: Debugge den ersten Fehler, nicht den letzten: Ein Fehlschlag früh im Skript kaskadiert in einen Haufen nachgelagerter "object not found"-Fehler, die alle verschwinden, sobald du das Original behebst. Der Rest dieser Seite ist ein Entschlüsseler für die häufigsten Meldungen, danach folgen die Werkzeuge für den Fall, dass Lesen nicht reicht.

Die Namensfehler: nicht gefunden

Error: object 'total' not found - R hat jede ihm bekannte Umgebung durchsucht, und keine Variable hat diesen Namen. Drei Ursachen decken nahezu jeden Fall ab:

  • Ein Tippfehler, einschließlich Groß-/Kleinschreibung. R unterscheidet Groß- und Kleinschreibung: Total, total und TOTAL sind drei verschiedene Namen, und R rät nicht, welchen du meintest.
  • Die definierende Zeile ist nicht gelaufen. Du hast total <- sum(x) ins Skript geschrieben, es aber in dieser Sitzung nie ausgeführt - verbreitet nach einem R-Neustart, wo die Skriptdatei die Zeile noch zeigt, die Sitzung sie aber nie gesehen hat. Führ das Skript von oben aus.
  • Falsche Umgebung. In einer Funktion erzeugte Variablen leben und sterben in diesem Aufruf. Eine davon außerhalb zu nutzen heißt, nach etwas zu fragen, das nicht mehr existiert - gib stattdessen den Wert zurück.

Error: could not find function "read_excel" - dieselbe Idee, aber für einen Funktionsnamen. In neun von zehn Fällen lebt die Funktion in einem Paket, das du installiert, aber in dieser Sitzung nicht geladen hast:

library(readxl)          # the fix: loading is per-session, installing is per-machine
df <- read_excel("data.xlsx")

Scheitert library(readxl) selbst, ist das Paket nicht installiert - zuerst install.packages("readxl"). Und wenn die Funktion zu Base R gehört, hast du dich vertippt (lenght() ist der Initiationsritus für alle).

Syntaxfehler: unexpected symbol

Error: unexpected symbol in "..." (und seine Verwandten unexpected ')', unexpected string constant) bedeutet, dass R den Code nicht einmal parsen konnte. Die Meldung zeigt auf die Stelle, an der R es bemerkt hat, was oft hinter dem eigentlichen Fehler liegt. Die üblichen Verdächtigen:

mean(x na.rm = TRUE)        # missing comma - should be mean(x, na.rm = TRUE)

name <- "Ada                # unclosed quote - swallows the following lines
total <- sum(c(1, 2, 3)     # unclosed paren - the error fires lines later

Wenn die markierte Zeile unschuldig aussieht, liegt der Fehler fast immer darüber: ein nicht geschlossenes Anführungszeichen, eine nicht geschlossene runde oder geschweifte Klammer weiter oben in der Datei. Ein Editor, der zusammengehörige Paare hervorhebt, findet das in Sekunden.

Typ- und Indexfehler, entschlüsselt

non-numeric argument to binary operator - du hast mit etwas gerechnet, das keine Zahl ist, meist mit einer Zahl, die als Text ankam (Importe sind die klassische Quelle - eine Spalte mit einem verirrten Wort kommt als character an, wie unter Datentypen behandelt). Die kaputte Fassung:

x <- "10"
x + 5
# Error in x + 5 : non-numeric argument to binary operator

Und die Lösung - konvertieren, dann rechnen:

subscript out of bounds - du hast mit [[ ]] nach Position n in etwas gefragt, das weniger als n Elemente hat:

scores <- list(ada = 92, grace = 88)
scores[[3]]
# Error in scores[[3]] : subscript out of bounds

Prüfe length() vor der Indexierung oder frag besser über den Namen (scores[["grace"]]), damit ein Umsortieren dich nicht bricht. Beachte die Asymmetrie: Einfache Klammern sind nachsichtiger - ein [ ] außerhalb des Bereichs eines Vektors gibt still NA zurück, statt zu scheitern, was einen lauten Bug gegen einen stillen tauscht.

$ operator is invalid for atomic vectors - $ gehört zu Listen und Data Frames. Bei einem benannten Vektor nimm Klammern:

([[ ]] gibt den nackten Wert; [ ] behält den Namen dran.) Dieser Fehler bedeutet oft, dass etwas weiter vorn einen Vektor zurückgegeben hat, wo du einen Data Frame erwartet hast - prüfe diese Annahme, statt bloß den Operator zu tauschen.

argument is of length zero - ein if () hat eine Bedingung ohne Inhalt bekommen, fast immer ein NULL, das sich aus einem fehlenden Listenelement oder einer Funktion ohne Rückgabe eingeschlichen hat:

threshold <- NULL
if (threshold > 5) print("big")
# Error in if (threshold > 5) print("big") : argument is of length zero

Sichere die Prüfung ab - und beachte, dass && die Auswertung abbricht, sobald die Antwort feststeht, sodass der Vergleich nie auf einem NULL läuft:

(Ein verwandter Fehler, missing value where TRUE/FALSE needed, ist derselbe Fehlschlag mit NA statt NULL - die Absicherung dort heißt is.na(), behandelt unter fehlende Werte.)

replacement has length zero - die Zuweisungsvariante derselben Krankheit: x[2] <- numeric(0) versucht, einen Platz mit null Werten zu füllen. Was auch immer die rechte Seite erzeugt hat, kam leer zurück; debugge das, nicht die Zuweisung.

Warnungen sind keine Fehler - und genau das ist die Gefahr

Ein Fehler stoppt die Ausführung; eine Warnung nicht. R beendet die Berechnung, reicht dir ein Ergebnis und erwähnt seine Vorbehalte hinterher. Dieses Ergebnis ist manchmal in Ordnung und manchmal still falsch:

Beide Zeilen laufen durch. Die erste recycelt den kürzeren Vektor und warnt longer object length is not a multiple of shorter object length - und einen Vektor der Länge 2 gegen Länge 3 zu recyceln ist fast nie das, was jemand meinte. Die zweite warnt NAs introduced by coercion und liefert einen Vektor mit einem Loch darin, das jedes nachgelagerte mean() NA zurückgeben lässt. Behandle beide Warnungen als zu untersuchende Bugs, nicht als Rauschen zum Wegscrollen. In Skripten kannst du diese Haltung mit options(warn = 2) erzwingen, das jede Warnung zu einem Fehler befördert, sodass nichts durchrutscht.

Fehlschläge behandeln mit tryCatch()

Manchmal ist ein Fehler erwartet - eine beschädigte Datei in einem Ordner mit Hunderten, eine schlechte Zeile - und du willst ihn behandeln und weitermachen, statt zu sterben. tryCatch() umschließt einen riskanten Ausdruck mit Handlern:

Die Mechanik: Gelingt der Hauptblock, ist sein Wert das Ergebnis. Scheitert er, läuft stattdessen der error =-Handler, und dessen Rückgabewert (hier NA) wird zum Ergebnis - das Skript läuft weiter. conditionMessage(e) holt die ursprüngliche Meldung fürs Protokoll zurück. finally = läuft in jedem Fall, ob Erfolg oder Fehlschlag, und dorthin gehört Aufräumarbeit wie das Schließen von Verbindungen - du siehst es oben vor jedem Ergebnis ausgegeben.

Es gibt auch einen warning =-Handler - tryCatch(as.numeric(x), warning = function(w) NA) fängt die Konvertierungswarnung aus dem vorherigen Abschnitt ab, statt sie durchgehen zu lassen. Eine Warnung: Ein Handler, der einen Ersatzwert zurückgibt, ohne irgendetwas zu protokollieren, ist eine Art, Fehlschläge zu verbergen, nicht sie zu behandeln. Erfasse immer conditionMessage() - dein zukünftiges Ich braucht es.

Den Fehlschlag lokalisieren: traceback(), browser() und ehrliches Ausgeben

Kommt der Fehler aus tief verschachtelten Funktionsaufrufen, sagt die Meldung allein nicht, welche Aufrufkette dich dorthin gebracht hat. Führ traceback() unmittelbar nach dem Fehler aus:

f <- function(x) g(x)
g <- function(x) stop("boom")

f(1)
# Error in g(x) : boom
traceback()
# 2: g(x)
# 1: f(1)

Es gibt den Aufrufstapel im Moment des Fehlschlags aus - dein Aufruf am einen Ende, der scheiternde Aufruf am anderen. Es muss das Nächste sein, was du ausführst; der Stapel wird verworfen, sobald ein weiterer Fehler auftritt.

Für einen Live-Blick pausiert browser() die Ausführung dort, wo du es pflanzt, und wirft dich in eine interaktive Eingabeaufforderung innerhalb der Funktion - Variablen inspizieren, mit n schrittweise weiter, mit c fortfahren, mit Q beenden. debug(f) tut dasselbe ohne Codeänderung: Es markiert f, sodass sich der nächste Aufruf im Browser öffnet (rückgängig mit undebug(f)).

Und dann gibt es die Technik, die niemand auf Konferenzfolien setzt, aber alle nutzen: Ausgeben. Streu print() oder cat() an Kontrollpunkte, führ aus und sieh, wo die Realität aufhört, deinen Erwartungen zu entsprechen. Es ist legitim, es ist schnell, und in Skripten ist es oft das praktischste Werkzeug. Sein bester Freund ist str(), das die Frage hinter vielleicht der Hälfte aller R-Fehler beantwortet - "Was ist dieses Objekt eigentlich?":

Eine kompakte Anzeige: Es ist eine Liste, zwei Elemente, eines ein Integer-Vektor, eines ein Data Frame mit diesen Spalten und Typen. Wenn ein $ scheitert oder Rechnungen sich danebenbenehmen, str() das Objekt, bevor du theoretisierst - die Antwort steht meist genau dort ("... ach, es ist eine Liste der Länge 1, die meinen Data Frame enthält").

Was du mitnimmst

  • Lies die Meldung: Der Teil nach Error in sagt wo, der Teil nach dem Doppelpunkt sagt warum. Behebe den ersten Fehler, nicht den lautesten.
  • object not found = Tippfehler, noch nicht ausgeführter Code oder eine Variable, die nur in einer Funktion existierte. could not find function = fehlender library()-Aufruf, fast immer.
  • unexpected symbol bedeutet nicht parsbare Grammatik - und der echte Fehler ist oft ein nicht geschlossenes Anführungszeichen oder eine offene Klammer vor der markierten Stelle.
  • Die Typ-/Index-Klassiker - non-numeric argument, subscript out of bounds, $ auf atomaren Vektoren, argument is of length zero - deuten jeweils auf eine falsche Annahme darüber, was ein Objekt ist; str() prüft die Annahme in einem Aufruf.
  • Warnungen stoppen die Ausführung nicht, und genau deshalb verdienen sie Aufmerksamkeit - das Ergebnis kann still falsch sein.
  • tryCatch(error =, warning =, finally =) behandelt erwartete Fehlschläge, ohne zu sterben (protokolliere immer conditionMessage()); traceback() direkt nach einem Fehler zeigt die Aufrufkette; browser()/debug() pausieren darin; Print-Debugging ist ehrliche Arbeit.

Als Nächstes: Viele "Fehler", die gar keine sind, führen zurück auf einen Wert aus zwei Buchstaben - NA, und wie fehlende Werte durch alles fließen, was R berechnet.

Häufig gestellte Fragen

Was bedeutet "object 'x' not found" in R?

R hat nach einer Variablen namens x gesucht, und in keiner durchsuchten Umgebung existiert dieser Name. Die Ursachen, nach Wahrscheinlichkeit: ein Tippfehler im Namen (R unterscheidet Groß- und Kleinschreibung - Total ist nicht total), die Zeile, die x erzeugt, wurde in dieser Sitzung noch nicht ausgeführt, oder x wurde in einer Funktion erzeugt und du versuchst es außerhalb zu nutzen.

Was bedeutet "could not find function" in R?

Die Funktion existiert in einem Paket, das du in dieser Sitzung nicht geladen hast. Ein Paket zu installieren geschieht einmal pro Rechner; library() einmal pro Sitzung, und den library()-Aufruf zu vergessen ist die übliche Ursache. Scheitert library() selbst, ist das Paket nicht installiert. Ein Tippfehler im Funktionsnamen erzeugt denselben Fehler.

Wie behandelt man in R Fehler mit tryCatch?

Umschließe den riskanten Ausdruck: tryCatch(expr, error = function(e) fallback, warning = function(w) fallback, finally = cleanup). Scheitert expr, läuft der passende Handler statt das Skript sterben zu lassen, und was der Handler zurückgibt, wird zum Ergebnis. conditionMessage(e) im Handler liefert dir die ursprüngliche Meldung fürs Protokoll.

Was macht traceback() in R?

Direkt nach einem Fehler ausgeführt, gibt traceback() die Kette der Funktionsaufrufe aus, die beim Auftreten des Fehlers aktiv war - dein Aufruf am einen Ende, der scheiternde Aufruf am anderen. Es behebt nichts; es sagt dir, wo du suchen musst, und das ist der Großteil der Arbeit, wenn der Fehler aus tief verschachtelten Funktionen kam.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S