GNU R: Finden von Programmfehlern und Debugging
Ursache von Programmfehlern sind zumeist Fehlannahmen über die von (Unter-)Funktionen zurückgegebenen Inhalte oder Datentypen der übergebenen Objekte. Oft ist es deshalb wünschenswert, den Zustand beim Programmabbruch genauer zu analysieren. R stellt hierfür verschiedene Methoden und Optionen zur Verfügung. |
R unterstützt das Auffinden von Fehlern mit mehreren Hilfsfunktionen und Optionen. Welche bei der Fehlersuche eingesetzt werden, hängt letztlich von Vorlieben der ProgrammiererInnen ab. Dieser Abschnitt soll einen Überblick über die Möglichkeiten geben.
Manuelles Einfügen von Ausgabewerten
[Bearbeiten]R gibt zumeist relativ präzise Fehlermeldungen, warum ein Programmabbruch erfolgt ist, aber leider keine Zeilenangaben. Dies erschwert es manchmal, die genaue Stelle zu finden, an der der Fehler auftrat. Daher ist es oft sinnvoll, mehrere print
oder cat
-Funktionen mit unterscheidbarer Ausgabe an verschiedenen Stellen zu setzen. Falls die entsprechende Ausgabe erfolgt, ist der Fehler an dieser Stelle noch nicht aufgetreten.
Wenn eingegrenzt werden konnte, wo der Fehler auftritt, macht es Sinn, sich die Zustände der der Funktion übergebenen Objekte an charakteristischen Stellen mit print
ausgeben zu lassen. Je nach Fehlermeldung sind auch die Funktionen length
und class
sehr nützlich bei der Fehlersuche.
Gängige Fehler
[Bearbeiten]Wie bereits erwähnt treten Fehler häufig deshalb auf, weil Funktionen andere Objekte übergeben werden als diese erwarten. Wer verstanden hat, wie Objektklassen funktionieren, dürfte auch die meisten der Fehlermeldungen verstehen. Dennoch kann die Interpretation von Fehlermeldungen und die Fehlersuche in R
nicht nur Anfängern immer wieder Schwierigkeiten bereiten.
Hier sollen ein paar häufige Fehler und Lösungen beschrieben werden:
- Manchmal ist es nicht ganz einfach zu verstehen, welche Klasse eine Funktion erwartet. Auskunft hierüber gibt immer die Hilfe-Seite einer Funktion. Hier wird meist genau unter
arguments
beschrieben was die Funktion an Eingabeobjekten erwartet. Untervalue
sind die Objekte und deren Klasse beschrieben, die die Funkton zurückgibt. - Problematisch ist z.B. die Klasse
factor
. Sie verhält sich aus Nutzersicht wie eincharacter
-Vektor, besteht aber in Wahrheit aus Elementen vom Typnumeric
. Dies ist insbesondere beim Datenimport aus Dateien (z.B. mitread.csv
) problematisch, wenn Spalten mit Nummern (z.B. wegen zusätzlicher Buchstaben) als Strings importiert und auf Grund der Einstellungen automatisch einfactor
erzeugt werden. Eine Konvertierung inas.numeric
gibt dann unter Umständen nicht die erwarteten Werte zurück, sondern die interne Repräsentation desfactor
. Eine korrekte Behandlung erfordert in diesem Fall kontraintuitives Vorgehen.as.numeric ( as.character ( variable ) )
. - Listenreferenzierungen sorgen bei Anfängern ebenfalls oft für Verwirrung.
Error-Recovery mit dem Browser
[Bearbeiten]Eine der nützlichsten Einstellungen betrifft die Frage, was passiert, wenn ein Fehler aufgetreten ist. Dies kann über options(error)
festgelegt werden:
Standardmässig wird eine Fehlermeldung dargestellt und die Programmausführung abgebrochen: options(error=print)
Stattdessen kann aber mit options(error=recover)
im Fehlerfall auch automatisch ein so genannter Browser
gestartet werden.
Dies soll in einem Beispiel verdeutlicht werden. Einem data.frame
werden statt Vektoren oder Listen der Code der Funktion sum
übergeben:
> options(error=print) > data.frame(1:10,sum) Fehler in as.data.frame.default(x[[i]] , optional = TRUE) : kann Klasse '"function"' nicht in data.frame umwandeln
Wenn options(error=recover)
gesetzt wird, springt R
in den browser:
> options(error=recover) > data.frame(1:10,sum) Fehler in as.data.frame.default(x[[i]], optional = TRUE) : kann Klasse '"function"' nicht in data.frame umwandeln Enter a frame number, or 0 to exit 1: data.frame(1:10, sum) 2: as.data.frame(x[[i]], optional = TRUE) 3: as.data.frame.default(x[[i]], optional = TRUE) Auswahl: _
Der Browser dient dazu durch die Hierarchien des Funktionsaufrufs (sogenannte frames) zum Zeitpunkt des Fehlers zu navigieren. In diesem Fall gibt es drei frames: Den Funktionsaufruf (1:
) und zwei von der Funktion data.frame aufgerufene Unterfunktionen (2:
und 3:
).
Die Browser hat zwei Zustände:
frame
-Auswahl: In derframe
-Auswahl kann durch Eingabe der entsprechenden Nummern in den entsprechenden frame gesprungen werden.frame
-Editor: Imframe
-Editor können alle Funktionen und Kommandos wie gewöhnlich benutzt werden. Insbesondere werden Objekte durch einfache Eingabe ihres Namens ausgegeben. Durch eine LeereingabeRETURN
springt der Browser zurück in die frame-Auswahl.
Wählen wir beispielsweise frame
1, unseren ursprünglichen Funktionsaufruf, in dem wir 1
eingeben:
Auswahl: 1 Called from: data.frame(1:10, sum)
Wir befinden uns nun in frame
1 und können die Objekte dort betrachten. Um alle Objekte des frame
aufzulisten, benutzen wir die Funktion ls()
:
ls() [1] "check.names" "check.rows" "data.row.names" "i" [5] "mrn" "n" "namesi" "ncols" [9] "no.vn" "nrows" "object" "row.names" [13] "stringsAsFactors" "tmpname" "vlist" "vnames" [17] "x" "xi"
Dies sind alles Variablen, die die Funktion data.frame()
intern bei der Erstellung neuer Objekte generiert. Uns interessieren hier beispielhaft die der Funktion data.frame
übergegebenen Werte, die in x
gespeichert sind:
[[1]] [1] 1 2 3 4 5 6 7 8 9 10 [[2]] function (..., na.rm = FALSE) .Primitive("sum")
Erkennbar ist, dass eine Liste mit zwei Werten übergeben wurde. Das Listenelement [[1]]
enthält die Werte 1:10, das Listenelement [[2]]
die Funktion sum
.
Wir verlassen den Browser durch eine Leereingabe (RETURN
) (zum Wechseln in die frame
-Auswahl) und die Eingabe einer 0
(zum Beenden des Browsers)
Derart lassen sich alle Werte zum Zeitpunkt des Programmabbruchs untersuchen. Wenn ein Fehler auftritt und nicht klar ist, welche Funktionen zum Fehler führten und welche Werte vorher übergeben wurden, sollte generell options(error=recover)
gesetzt werden. Die error-recovery kann nach der Korrektur der Fehler mit options(error=print)
wieder deaktiviert werden.
Verfolgen von Funktionen mit trace
[Bearbeiten]
Inhaltsverzeichnis[Bearbeiten]
|