GNU R: Finden von Programmfehlern und Debugging

Aus Wikibooks
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. Unter value 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 ein character-Vektor, besteht aber in Wahrheit aus Elementen vom Typ numeric. Dies ist insbesondere beim Datenimport aus Dateien (z.B. mit read.csv) problematisch, wenn Spalten mit Nummern (z.B. wegen zusätzlicher Buchstaben) als Strings importiert und auf Grund der Einstellungen automatisch ein factor erzeugt werden. Eine Konvertierung in as.numeric gibt dann unter Umständen nicht die erwarteten Werte zurück, sondern die interne Repräsentation des factor. 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 der frame-Auswahl kann durch Eingabe der entsprechenden Nummern in den entsprechenden frame gesprungen werden.
  • frame-Editor: Im frame-Editor können alle Funktionen und Kommandos wie gewöhnlich benutzt werden. Insbesondere werden Objekte durch einfache Eingabe ihres Namens ausgegeben. Durch eine Leereingabe RETURN 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]