12  Arithmetik und Variablen

12.1 Arithmetik

Öffnet man ein R Terminal, so kann man dieses zunächst einmal als Taschenrechner nutzen, wie folgendes Beispiele zeigt

1+1                                                     # Addition
[1] 2

Im Terminal erscheint dabei einerseits das Ergebnis \(2\), andererseits mit [1] ein Hinweis darauf, dass das Ergebnis der erste Eintrag in dem von R erzeugten Ergebnisvektor ist. Vektoren werden wir in Kapitel 14 im Detail behandeln. Weitere Beispiele für den Gebrauch eines R Terminals als Taschenrechner sind folgende.

2*3                                                     # Multiplikation
[1] 6
sqrt(2)                                                 # Quadratwurzel (square root)
[1] 1.414214
exp(0)                                                  # Exponentialfunktion
[1] 1
log(1)                                                  # Logarithmusfunktion
[1] 0

Tabelle 12.1 gibt einen ersten Überblick über die in Base R verfügbaren arithmetischen Operatoren. Neben den vertrauten Operationen der Addition, Subtraktion, Multiplikation, Division und Potenz hält Base R zum Beispiel mit der Matrixmultiplikation, der ganzzahligen Teilung (5 %\% 2 = 2) und der Modulooperation, die den ganzzahligen Rest bei ganzzahliger Teilung ausgibt (5 %% 2 = 2), auch Operatoren für spezielle arithmetische Operationen bereit.

Tabelle 12.1: Beispiele arithmetischer Operatoren in Base R
Operation Operator
Addition +
Subtraktion -
Multiplikation *
Division \
Potenz ^
Matrimultiplikation %*%
Ganzzahlige Teilung %/%
Modulo %%

Neben den arithmetischen Operatoren zur Verknüpfung zweier Zahlen bietet Base R natürlich auch die Möglichkeit mathematische Standardfunktionen auszuwerten, einen ersten Überblick gibt Tabelle 12.2.

Tabelle 12.2: Beispiele mathematischer Funktionen in Base R
Funktion Aufruf
Exponentialfunktion exp(x)
Logarithmusfunktion log(x)
Betrag abs(x)
Wurzel sqrt(x)
Aufrunden ceiling(x)
Abrunden floor(x)
Mathematisches Runden round(x)

Wenn auch die in Tabelle 12.1 aufgeführten Operatoren und die in Tabelle 12.2 aufgeführten Funktionen auf den ersten Blick einen etwas anderen Charakter haben, so unterscheidet Base R formal nicht zwischen Operatoren und Funktionen. Insbesondere können Operatoren mithilfe der sogenannten Infixnotation auch also Funktionen mehrer Argumente genutzt und verstanden werden. Untenstehender R Code zeigt, wie die arithmetische Verknüpfung \(2 + 3\) als Ausführung einer Funktion der Form \(+ : \mathbb{R}^2 \to \mathbb{R}\) mit dem Namen “+” verstanden werden kann

`+`(2,3)                                                # Infixnotation für 2 + 3
[1] 5

Schließlich bietet Base R wie jede Programmiersprache auch die Möglichkeit Ausdrücke auf ihren logischen Gehalt hin auszuwerten. Dabei ist Logik hier im Sinne der aus Kapitel 1 Aussagenlogik zu verstehen, in der Aussagen einen von zwei Werten annehmen können, wahr (TRUE) oder falsch (FALSE). Die in Tabelle 12.3 aufgeführten Relationsoperatoren <, <= ,>, und >= werden zumeist auf numerische Werte angewendet, die Gleichheitsoperatoren == und != können auf beliebige Datenstrukturen angewendet werden und die Operatoren zur Verknüpfung logischer Werte & und | entsprechen den aus Kapitel 1 bekannten Begiffen der logischen Konjunktion (“und”) und Disjunktion (“nicht-exklusive oder”)

Tabelle 12.3: Beispiele logischer Operatoren in R
Logische Verknüpfung Operator
Gleich ==
Ungleich !=
Konjunktion &
Disjunktion |
Kleiner <
Kleiner-gleich <=

Die in Tabelle 12.1, Tabelle 12.2 und Tabelle 12.3 aufgelisteten Operatoren znd Funktionen stellen nur einen kleinen Auszug der von Base R bereitgestellten Operatoren und Funktionen dar. Einen vollständigen Überblick bietet folgender R Aufruf, der die Namen aller von Base R bereitgestellten Funktionen auflistet. Wir werden viele dieser Funktionen im weiteren Verlauf kennenlernen.

names(methods:::.BasicFunsList)
  [1] "$"                    "$<-"                  "["                   
  [4] "[<-"                  "[["                   "[[<-"                
  [7] "%*%"                  "crossprod"            "tcrossprod"          
 [10] "xtfrm"                "c"                    "all"                 
 [13] "any"                  "sum"                  "prod"                
 [16] "max"                  "min"                  "range"               
 [19] "cummax"               "rep"                  "^"                   
 [22] "cos"                  "levels<-"             "cumsum"              
 [25] "asin"                 "anyNA"                "<="                  
 [28] "Conj"                 "exp"                  "is.matrix"           
 [31] "log10"                "as.environment"       "ceiling"             
 [34] "asinh"                "abs"                  "as.raw"              
 [37] "is.infinite"          "is.array"             "floor"               
 [40] "=="                   "sign"                 "cummin"              
 [43] "|"                    "round"                "Re"                  
 [46] "!"                    "is.numeric"           "acosh"               
 [49] "!="                   "names<-"              "&"                   
 [52] "atanh"                "sinh"                 ">="                  
 [55] "sin"                  "*"                    "atan"                
 [58] "+"                    "length"               "length<-"            
 [61] "sinpi"                "-"                    "is.nan"              
 [64] "/"                    "sqrt"                 "%/%"                 
 [67] "as.numeric"           "seq.int"              "trunc"               
 [70] "digamma"              "acos"                 "<"                   
 [73] "as.logical"           "cosh"                 "Mod"                 
 [76] "tanpi"                "dimnames<-"           "log2"                
 [79] ">"                    "tanh"                 "is.na"               
 [82] "dim"                  "signif"               "gamma"               
 [85] "is.finite"            "as.integer"           "dim<-"               
 [88] "as.double"            "lgamma"               "Arg"                 
 [91] "log1p"                "trigamma"             "%%"                  
 [94] "tan"                  "cumprod"              "Im"                  
 [97] "expm1"                "as.call"              "log"                 
[100] "as.complex"           "cospi"                "as.character"        
[103] "dimnames"             "names"                "("                   
[106] ":"                    "="                    "@"                   
[109] "{"                    "~"                    "&&"                  
[112] ".C"                   "baseenv"              "quote"               
[115] "::"                   "<-"                   "is.name"             
[118] "if"                   "||"                   "attr<-"              
[121] "untracemem"           ".cache_class"         "substitute"          
[124] "interactive"          "is.call"              "switch"              
[127] "function"             "is.single"            "is.null"             
[130] "is.language"          "is.pairlist"          ".External.graphics"  
[133] "declare"              "globalenv"            "class<-"             
[136] ".Primitive"           "is.logical"           "enc2utf8"            
[139] "UseMethod"            ".subset"              "proc.time"           
[142] "enc2native"           "repeat"               ":::"                 
[145] "<<-"                  "@<-"                  "missing"             
[148] "nargs"                "isS4"                 ".isMethodsDispatchOn"
[151] "forceAndCall"         "Exec"                 ".primTrace"          
[154] "storage.mode<-"       ".Call"                "unclass"             
[157] "gc.time"              ".subset2"             "environment<-"       
[160] "emptyenv"             "seq_len"              ".External2"          
[163] "is.symbol"            "class"                "on.exit"             
[166] "is.raw"               "for"                  "is.complex"          
[169] "list"                 "invisible"            "is.character"        
[172] "oldClass<-"           "is.environment"       "attributes"          
[175] "break"                "return"               "attr"                
[178] "tracemem"             "next"                 ".Call.graphics"      
[181] "standardGeneric"      "is.atomic"            "retracemem"          
[184] "expression"           "is.expression"        "call"                
[187] "is.object"            "pos.to.env"           "attributes<-"        
[190] ".primUntrace"         "...length"            "Tailcall"            
[193] ".External"            "oldClass"             ".Internal"           
[196] ".Fortran"             "browser"              "is.double"           
[199] ".class2"              "while"                "nzchar"              
[202] "is.list"              "lazyLoadDBfetch"      "unCfillPOSIXlt"      
[205] "...elt"               "...names"             "is.integer"          
[208] "is.function"          "is.recursive"         "seq_along"           
[211] "unlist"               "as.vector"            "lengths"             

12.2 Präzedenz

Eine wichtige Eigenschaft bei der Benutzung von Operatoren in der Programmierung ist ihre durch die jeweilige Programmiersprache festgelegte Präzedenz. Dabei handelt es sich um von den Entwicklern der Programmiersprache festgelegten Regeln, in welcher Rangfolge Operatoren in Ausdrücken, in denen mehrere von ihnen vorkommen, ausgeführt werden. Aus der Mathematik ist man insbesondere die Präzedenzregel “Punktrechnung geht vor Strichrechnung” gewöhnt. Diese besagt, dass in Ausdrücken, in denen sowohl Produkte oder Divisionen als auch Summen oder Differenzen vorkommen, die Produkte oder Divisionen zunächst ausgeführt werden und ihre Ergebnisse dann in die Summen- oder Differenzbildung eingehen. So ergibt sich zum Beispiel \[ 2 \cdot 5 - 3 = 10 - 3 = 7 \tag{12.1}\] und nicht etwa \[ 2 \cdot 5 - 3 \neq 2 \cdot 2 = 4. \tag{12.2}\] Wir setzen als bekannt voraus, dass die Präzedenzregeln durch Klammerbildung betont bzw. überschrieben werden können. So ist beispielsweise der Ausdruck in Gleichung 12.1 äquivalent zu \[ (2 \cdot 5) - 3 = 10 - 3 = 7 \tag{12.3}\] und die in Gleichung 12.2 beabsichtigte Rechnung kann durch Klammersetzung als \[ 2 \cdot (5 - 3) = 2 \cdot 2 = 4 \tag{12.4}\] richtig gestellt werden. Diese vertrauten Rechenregeln und ihre Überschreibung durch Klammern finden sich in R entsprechend implementiert:

2*5 - 3
[1] 7
2*(5 - 3)
[1] 4

Eine weitere als bekannt voraussgesetzte Präzendenzregel ist, dass Potenzen eine höhere Präzedenz als Produkte oder Divisionen und Summen oder Differenzen haben. Es ergibt sich also beispielsweise \[\begin{equation} 2^3 + 3 = (2\cdot 2 \cdot 2) + 3 = 8 + 3 = 11 \end{equation}\] und nicht etwa \[\begin{equation} 2^3 + 3 \neq 2^{3 + 3} = 2^6 = 64 \end{equation}\] Entsprechend gelten in R

2^3 + 3
[1] 11
2^(3 + 3)
[1] 64

Eine wichtige Besonderheit hinsichtlich der Präzedenzregeln in R ist, das auch unitäre Vorzeichen, also Vorzeichen, die eine Zahl als eine negative Zahl identifizieren in den Bereich der Operatorpräzedenz fallen. Wenn man geneigt sein sollte, einen Ausdruck der Form \(-1^2\) als \((-1)^2 = 1\) zu interpretieren, so folgt R der Regel, dass in \(-1^2\) zunächst die Potenz und dann das Vorzeichen ausgewertet werden, dass also \(-1^2 = -(1^2) = -1\) gilt, wie folgender R Code demonstriert.

-1^2                                # -(1^2)    = -1
[1] -1
(-1)^2                              # (-1)^2    =  1
[1] 1

Weiterhin werden in R Potenzen von rechts nach links abgearbeitet, Produkte, Divisionen, Summen und Differenzen dagegen von links nach rechts. So gelten beispielsweise

2^2^3                               # 2^(2^3)   = 2^8 = 256
[1] 256
(2^2)^3                             # (2^2)^3   = 4^3 = 64
[1] 64

und

2+3/4*5                             # 2+(3/4)*5 = 2+(0.75*5) = 2+3.75 = 5.75
[1] 5.75
2+3/(4*5)                           # 2+3/(4*5) = 2+3/20 = 2+0.15 = 2.15
[1] 2.15

Generell gilt, dass man in der Programmierung Präzedenzregeln nicht raten sollte oder versuchen sollte, sie logisch herzuleiten, und dann darauf vertrauen sollte, dass die Entwickler der Programmiersprache die Präzedenzregeln schon gemäß des eigenen Verständnisses implementiert haben werden. Stattdessen sollte man Berechnungen immer kritisch prüfen und zur Sicherheit die beabsichtigte Rechnung lieber einmal zuviel mit Klammern betonen als zu wenig.

Neben den hier betrachteten Präzedenzregeln für die Grundrechenarten gibt es in R eine ganze Reihe weiterer Regeln zum Umgang mit vielen weiteren Operatoren, von denen wir bisher nur sehr wenige kennengelernt haben. Wenn man sich beim Erlernen einer Programmiersprache mit einem neuen Operator vertraut macht, sollte man also für sich auch unbedingt die Präzedenz dieses Operators klären. Der in Abbildung 12.1 gezeigte und mit dem Befehl ?Syntax aufzurufende Text der R Dokumentation diskutiert die in R geltenden Präzedenzregeln vollumfänglich und sollte bei der Arbeit mit R häufig konsultiert werden.

Abbildung 12.1: Präzedenzregeln in R.

12.3 Variablen

In der Programmierung sind Variablen abstrakte Behälter für Daten, deren Inhalt im Verlauf einer Programmausführung geändert werden kann. Variablen werden im Programmcode mit Namen bezeichnet und besitzen bei Programmausführung eine Adresse im Arbeitsspeicher. Variablen können verschiedenen Typs sein, d.h. unterschiedliche Arten von Daten, und nur diese, repräsentieren. In traditionellen Programmiersprachen wird der Typ einer Variable explizit deklariert, d.h. es wird zu Beginn der Programmausführung festgelegt, welche Art von Daten eine bestimmte Variable repräsentiert. Die Deklaration einer Variable A, die ganze Zahlen (und nur diese) repräsentiert hat etwa die Form

VAR A : INTEGER     # A ist eine Variable vom Typ Integer (ganze Zahl)

In der Folge kann der Variable A dann ein Wert vom Typ ganze Zahl zugewiesen werden, beispielsweise in der Form

A := 1              # Der Variable A wird der numerische Wert 1 zugewiesen

In 4GL Sprachen wir R wird der Variablentyp überlicherweise direkt durch eine Initialisierungsanweisung festgelegt. So deklariert folgender R Code die Variable a gleichzeitig als vom Typ double (Dezimalzahl) mit dem Wert 1,

a = 1               # a ist eine Variable vom Typ double, ihr Wert ist 1

In den allermeisten Programmiersprachen ist der Zuweisungsbefehl =. R ist dahingehend besonders, als es mit -> und <- Zuweisungsbefehle für Zuweisungen von Werten von links nach rechts oder von links nach rechts erlaubt, wobei letzteres auch durch = erreicht wird. Offiziell empfohlener Zuweisungsbefehl für R ist <-, da dies aber sehr idiosynkratisch ist, benutzen wir = wie in jeder anderen Programmiersprache. Ein erstes Beispiel soll den Umgang mit Variablen vedeutlichen.

Beispiel

Wir betrachten folgende Textaufgabe:

“Lina geht ins Schreibwarengeschäft und kauft vier Hefte und zwei Stifte. Ein Heft koste 1 Euro und ein Stift koste 3 Euro. Wieviele Dinge kauft Lina insgesamt und wieviel Euro muss Lina insgesamt bezahlen?”

Um die Aufgabe zu lösen, definieren wir zunächst die Variablen anzahl_hefte und anzahl_stifte und weisen ihnen dabei ihre jeweiligen Wert aus der Aufgabe zu.

anzahl_hefte        = 4        
anzahl_stifte       = 2      

Nach Zuweisung existieren die Variablen mit den ihnen zugewiesenen Werte nun im Arbeitsspeicher, dem sogenannten Workspace. Der Befehl ls() zeigt die existierenden benutzbaren Variablen im Arbeitsspeicher an.

ls()
[1] "anzahl_hefte"  "anzahl_stifte"

Entscheidend ist, dass die Variablennamen jetzt wie Zahlen in Berechnungen genutzt werden können print() gibt dabei Variablenwerte in einem R Terminal aus.

anzahl_dinge = anzahl_hefte + anzahl_stifte 
print(anzahl_dinge) 
[1] 6

Um den Gesamtpreis zu berechnen, definieren wir als nächsted die Variablen preis_heft und preis_stift anhand der Aufgabenspezifikation.

preis_heft        = 1     
preis_stift       = 2        

Der Gesamtpreis berechnet sich dann wie folgt.

gesamtpreis = anzahl_hefte*preis_heft + anzahl_stifte*preis_stift 
print(gesamtpreis)
[1] 8

Variablen im Workspace könnne auch wieder gelöscht werden. rm() (remove) erlaubt das Löschen einzelner Variablen

rm(gesamtpreis)              
ls()
[1] "anzahl_dinge"  "anzahl_hefte"  "anzahl_stifte" "preis_heft"   
[5] "preis_stift"  

rm(list=ls()) löscht alle Variablen

rm(list = ls())              
ls()
character(0)

Variablennamen

Prinzipiell ist man in der Wahl der Namen für Variablen mit kleineren Einschränkungen frei. Zulässige Variablennamen in R

  • bestehen aus Buchstaben, Zahlen, Punkten (.) und Unterstrichen (_),
  • beginnen mit einem Buchstaben oder einem Punkt, dann allerdings nicht gefolgt von einer Zahl und
  • dürfen keine in R schon belegten Ausdrücke wie for, if, NaN, … sein

Hilfestellung zu den Einschränkungen von Variablennamen geben ?reserved. Eine Funktion, um aus beliebigen Vorschlägen einen syntaktisch zulässigen Variablennamen zu generieren, ist make.names(). Ihre Hilfe beschreibt auch die hier skizzierten Regeln zur Variablenbenennung in weiteren Details. Generell sind sinnvolle Variablen kurz, um im Code Platz zu sparen und aussagekräftig, um das menschliche Verständnis des Codes zu erhöhen. Meist bestehen Variablen nur aus Kleinbuchstaben und Unterstrichen.

Variablenrepräsentation

Wir wollen uns noch etwas genauer mit dem Verhältnis von Variablennamen (Bezeichnern) und Variableninhalten (Objekten im Arbeitsspeicher) befassen. Die folgenden Begriffe und Zusammenhänge sind insbesondere dann wichtig, wenn Berechnungen an die Grenzen des verfügbaren Arbeitsspeichers gelangen und optimiert werden müssen.

Wir beginnen mit dem Begriff des Binding. Dazu betrachten wir zunächst die folgende Variableninitialisierung:

x = 1

Intuitiv wird durch dieses Statement eine Variable genannt x mit dem Wert 1 erzeugt. Auf technischer Ebene geschehen dabei de-facto geschehen zwei Dinge (vgl. Abbildung 12.2):

  1. R erzeugt ein Objekt (Vektor mit Wert 1) mit Arbeitsspeicheradresse `lobstr::obj_addr(x).
  2. R verbindet dieses Objekt mit dem Namen x (binding), der das Objekt im Arbeitsspeicher referenziert.
Abbildung 12.2: Binding eines Namens mit einem Objekt im Arbeitsspeicher.

Betrachten wir nun folgendes Statement :

y = x

Intuitiv wird dabei eine Variable, genannt y, mit Wert gleich dem Wert von x erzeugt. Auf technischer Ebene wird dabei neuer Name y erzeugt, der dasselbe Objekt im Arbeitsspeicher referenziert wie x (vgl .@fig-binding-2).

Abbildung 12.3: Binding zweier Namen mit einem Objekt im Arbeitsspeicher.

Man kann sich mithilfe von lobstr::obj_addr(x) und lobstr::obj_addr(y) davon überzeugen, dass die Addressen des Objekts im Arbeitsspeicher identisch sind

print(lobstr::obj_addr(x))
[1] "0x1f597765d98"
print(lobstr::obj_addr(y))
[1] "0x1f597765d98"

Das betreffende Objekt wird also bei Allokation zu y nicht kopiert und dadurch Arbeitsspeicher eingespart.

Weiterhin nutzt R das sogenannte Copy-on-modify Prinzip. Dieses Prinzip besagt, dass ein Objekt im Arbeitsspeicher kopiert wird und eine neue Addresse erhält, sobald es modifiziert wird. Das ursprüngliche Object im Arbeitsspeicher bleibt dabei unverändert. Abbildung 12.4 und folgender R Code verdeutlichen zunächst das Copy-on-Modify Prinzip

x = 1       # Objekt (z.B. 0x74b) erzeugt, x referenziert Speicheradresse des Objektes
y = x       # y referenziert dieselbe Speicheradresse wie x (0x74b)
y = 3       # y modifiziert, eine modifizierte Kopie (z.B. 0xcd2) wird gespeichert
y           # y referenziert jetzt (0xcd2)
[1] 3
x           # x referenziert weiterhin (0x74b)
[1] 1
Abbildung 12.4: Copy-on-modify.

Die Tatsache, dass Objekte bei Veränderung im Arbeitsspeicher kopiert werden, die ursprünglichen Objekte aber im Prinzip unverändert erhalten bleiben, wird auch als die Immutability von Objekten in R bezeichnet. Allerdings greift die Immutability von Objekten in R nicht besonders weit, wie folgendes Beispiel zeigt. Dabei wird zunächst ein Objekt einer 1 im Arbeitsspeicher erzeugt, das mit dem Variablennamen x referenziert werden kann. Per Indexing (vgl. ?sec-Datenstrukturen-und-Datenmanagement) wird das Objekt dann bei einer beabsichtigten Modifikation im Arbeitsspeicher neu addressiert.

x   = 1                     # Objekt erzeugt, x referenziert Speicheradresse des Objektes
print(lobstr::obj_addr(x))  # Speicheraddresse des Objekts
[1] "0x1f59c031150"
x[1] = 2                    # Objekt verändert
print(lobstr::obj_addr(x))  # neue Speicheraddresse
[1] "0x1f59c47f120"

Das Copy-on-modify-Prinzip gilt auch in umgekehrter Reihenfolge, d.h. wird ein Objekt, auf das ein weiterer Variablenname zeigt, verändert, so ändert sich das ursprünglich referenzierte Objekt nicht. Folgender R Code mag dies verdeutlichen.

x = 1       # Objekt (z.B. 0x74b) erzeugt, x referenziert Speicheradresse  des Objektes
y = x       # y referenziert dieselbe Speicheradresse wie x (0x74b)
x = 3       # Ein neues Objekt (z.B. 0x2a08) wird erzeugt, x referenziert nun 0x2a08
y           # y referenziert weiterhin das ursprüngliche Objekt (0x74b)
[1] 1

Schließlich ist auch ein Unbinding von Objekten im Arbeitsspeicher möglich, z.B. als Folge folgenden R Codes (vgl. Abbildung 12.5):

x = 1       #  x referenziert Objekt (0x74b)
x = "a"     #  x referenziert Objekt (0x2a08), Objekt (0x74b) jetzt ohne Referenz
Abbildung 12.5: Unbinding.

Das Löschen von nicht-referenzierten im Objekten im Arbeitsspeicher wird als Garbage collection bezeichnet. R löscht nicht referenzierte Objekte im Arbeitsspeicher automatisch, allerdings meist erst dann, wenn der Speicher benötigt wird, es also wirklich nötig ist. Generell funktioniert dies aber gut und es ist nicht nötig, aktiv die von R bereitgestellte Garbage Collection Funktion gc() zu benutzen.

Repräsentation fehlender Werte

In der datenanalytischen Programmierung hat man es desöfteren mit fehlenden Werten zu tun, z.B. wenn eine experimentelle Messung bei einer Versuchsperson abgebrochen wurde. Zur Repräsentation fehlender Werte nutzt R NA (not applicable). Versucht man mit NA zu rechnen, so ergibt dies meist wieder NA:

3 * NA                  # Multiplikation eines NA Wertes ergibt NA
[1] NA
NA < 2                  # Relationaler Vergleich eines NA Wertes ergibt NA
[1] NA
NA^0                    # NA hoch 0 ergibt 1, weil jeder Wert hoch 0 eins ergibt (?)
[1] 1
NA & FALSE              # NA UND FALSE  ergibt FALSE, weil jeder Wert UND FALSE FALSE ergibt
[1] FALSE

Auf NA testet man mit is.na():

x = c(NA, 5, NA, 10)    # Vektor mit NAs
x == NA                 # Kein Testen auf NAs : 5 == NA ist NA, nicht FALSE
[1] NA NA NA NA
is.na(x)                # Logisches Testen auf NA
[1]  TRUE FALSE  TRUE FALSE

Neben NA besitzt R mit NaN auch eine Möglichkeit zur Darstellung mathematisch nicht definierter Werte. Im Gegensatz zu NA ist NaN immer vom numerischen Typ. Die Ergebnisse beim Rechnen mit NaN ähnlen denen mit NA.

3 * NaN                 # Multiplikation eines NaNA Wertes ergibt NaN
[1] NaN
NaN < 2                 # Relationaler Vergleich eines NaN Wertes ergibt NA
[1] NA
NaN^0                   # NaN hoch 0 ergibt 1, weil jeder Wert hoch 0 eins ergibt (?)
[1] 1
NaN & FALSE             # NaN UND FALSE ergibt FALSE, weil jeder Wert UND FALSE FALSE ergibt
[1] FALSE

Auf NaN testet man mit is.nan()

x = c(NaN, 5, NaN, 10)  # Vektor mit NaNs
is.nan(x)               # Logisches Testen auf NaN