Einführung in R: Zahlen und Variablen
In R gibt es eigentlich keine elementaren Datentypen (wie Zahlen, logische Werte oder Zeichen), sondern nur Vektoren. Um die Rechenoperationen (Grundrechenarten, insbesondere Division mit Rest, wissenschaftliche Funktionen) für Zahlen besser kennenzulernen, werden hier Zahlen wie elementare Datentypen behandelt. Zur Unterscheidung von ganzen Zahlen und Gleitkommazahlen benötigt man die Konzepte Modus und Speicher-Modus. Es werden erste Beispiele gezeigt, wie man in R mit Variablen umgeht.
- Einordnung des Artikels
- Einführung
- Zahlen
- Arithmetische Operationen
- Gültige Stellen
- Wissenschaftliche Funktionen
- Funktionen zum Runden
- Vergleichsoperatoren
- Variablen
- Variablen in der Mathematik
- Variablen in Programmiersprachen
- Deklaration, Initialisierung und Gültigkeitsbereich einer Variable
- Diagnose-Funktionen
- Übersicht
- Die Funktion print()
- Die checker-Funktionen is-dot
- Die Struktur eines Objektes str()
- Typumwandlungen
- Ganze Zahlen und Gleitkommazahlen
- Typumwandlungen zwischen ganzen Zahlen und Gleitkommazahlen
- Umwandlung einer Gleitkommazahl in eine ganze Zahl
- Umwandlung einer ganzen Zahl in eine Gleitkommazahl
- Der Vergleich von Zahlen
- Zusammenfassung
- Arithmetische Operationen
- Vergleichsoperatoren
- Wissenschaftliche Funktionen
- Konstante
- Trigonometrischen Funktionen
- Exponential- und Logarithmus-Funktion
- Weitere mathematische Funktionen
- Funktionen zum Runden von Zahlen
- Diagnose-Funktionen
Einordnung des Artikels
- Einführung in die Informatik
- Einführung in die Programmiersprache R
- Einfache Datentypen in R
- Einführung in R: Zahlen und Variablen
- Einfache Datentypen in R
- Einführung in die Programmiersprache R
Einführung
In der Übersicht über Datentypen in R wurde schon gesagt, dass es in R — anders als in vielen anderen Programmiersprachen — keine prinzipielle Unterscheidung zwischen Zahlen und Vektoren gibt: Zahlen sind Vektoren der Länge 1. Um mit der Syntax von R schneller vertraut zu werden, sollen Zahlen hier so behandelt werden, als wären sie ein fundamentaler Datentyp und nicht Spezialfälle von Vektoren.
Gezeigt werden insbesondere:
- Was sind Variablen und wie werden ihnen Werte zugewiesen?
- Welche Operationen können mit Zahlen ausgeführt werden?
- Wie unterscheidet man zwischen ganzen Zahlen und Gleitkommazahlen? Und wann ist diese Unterscheidung wichtig?
- Welche Funktionen gibt es für Zahlen?
Für den Anfänger ist es wichtig, sehr schnell die dabei verwendete Syntax kennenzulernen — die hier vorgestellten Konzepte werden später ständig eingesetzt und nicht nochmals erklärt. Mit einer Ausnahme: Wenn später Vektoren untersucht werden, müssen alle hier vorgestellten Konzepte überdacht werden, um zu verstehen, ob und wie sie von Zahlen auf Vektoren übertragen werden können.
Zahlen
Arithmetische Operationen
In R sind natürlich die Grundrechenarten +
, -
, *
, /
sowie die Potenz ^
als Operationen für die fundamentalen Datentypen verfügbar. Fehleranfällig ist dabei der Umgang mit der Division, da man zwischen der Division für Gleitkommazahlen und der Ganzzahl-Division unterscheiden muss. Das folgende kleine R-Skript zeigt die Eigenschaften der Grundrechenarten:
# <work>\01_BASICS\02_FUND_DAT_TYP\ArithmOp.R
# Arithmetische Operationen
a <- 20
b <- 3
c <- a + b
c
c <- a - b
c
c <- a * b
c
c = a / b
c
# Division mit Rest: 20 / 3 = 6 Rest 2
# Operationen, um 6 bzw. 2 zu erhalten:
# %/%: Ganzzahl-Division
# %%: Rest
c <- a %/% b
c
c <- a %% b
c
# Achtung:
b <- 7 / 5
b
# jetzt: a = 20, b = 1.4
c <- a %/% b
c
# zum Vergleich:
c <- a / b
c
Die Ausführung des Skripts liefert folgende Ausgaben:
> # <work>\01_BASICS\02_FUND_DAT_TYP\ArithmOp.R
> # Arithmetische Operationen
> a <- 20
> b <- 3
> c <- a + b
> c
[1] 23
> c <- a - b
> c
[1] 17
> c <- a * b
> c
[1] 60
> c = a / b
> c
[1] 6.666667
> # Division mit Rest: 20 / 3 = 6 Rest 2
> # Operationen, um 6 bzw. 2 zu erhalten:
> # %/%: Ganzzahl-Division
> # %%: Rest
> c <- a %/% b
> c
[1] 6
> c <- a %% b
> c
[1] 2
> # Achtung:
> b <- 7 / 5
> b
[1] 1.4
> # jetzt: a = 20, b = 1.4
> c <- a %/% b
> c
[1] 14
> # Beachte: das Ergebnis ist ganzzahlig
> # zum Vergleich:
> c <- a / b
> c
[1] 14.28571
In R wird bei der Definition von Variablen nicht zwischen ganzen Zahlen und Gleitkommazahlen unterschieden; intern werden alle Zahlen wie Gleitkommazahlen behandelt. In fast allen Programmiersprachen wird streng zwischen diesen Zahlentypen unterschieden, da sie intern anders gespeichert werden. In den meisten Programmiersprachen würde das folgende Skript zwei unterschiedliche Ergebnisse liefern, nicht so in R:
> 5 / 2
[1] 2.5
> 5 / 2.0
[1] 2.5
In Programmiersprachen wie C++ richtet sich die Interpretation des Divisions-Symbols /
nach dem verwendeten Datentyp und daher gilt in C++:
// Achtung: C++
cout << 5 / 2 << endl; // 2 (Ganzzahl-Division)
cout << 5 / 2.0 << endl; // 2.5 (Division für Gleitkommazahlen)
In R wird also bei der Division nicht zwischen den Datentypen unterschieden, sondern es gibt ein eigenes Symbol für Ganzzahl-Division und Division für Gleitkommazahlen. Hier nochmal die drei Divisions-Symbole im Überblick:
Art der Division | Symbol | Beispiel |
Division | /
| 20 / 3 # 6.666667
|
Ganzzahl-Division | %/%
| 20 %/% 3 # 6
|
Rest | %%
| 20 %% 3 # 2
|
Potenzen werden mit dem Symbol ^
gebildet:
2^10 # 1024
Wird als Exponent nur eine kleine Zahl wie 2 oder 3 verwendet, ist es besser den Ausdruck als Produkt zu schreiben, da die Berechnung dann schneller abläuft — insbesondere wenn dies in langen Schleifen erfolgt, kann man damit merklich Rechenzeit einsparen. Man sollte also statt x^2
oder x^3
besser schreiben x * x
oder x * x * x
.
Gültige Stellen
Wie man an den Beispielen oben sieht, werden die Ergebnisse mit 7 gültigen Stellen ausgegeben. 7 Stellen ist die default-Einstellung; mit Hilfe der Funktion options() kann man das default-Verhalten ändern — man sieht aber auch, dass sich die Genauigkeit nicht beliebig erhöhen lässt (sie ist beschänkt durch den Speicherplatz, der für eine Gleitkommazahl zur Verfügung steht):
x <- 2 / 3
x
options(digits = 3)
x
options(digits = 8)
x
options(digits = 15)
x
options(digits = 16)
x
options(digits = 17)
x
options(digits = 18)
x
Die Ausgaben lauten:
> x <- 2 / 3
> x
[1] 0.66666666666666663
> options(digits = 3)
> x
[1] 0.667
> options(digits = 8)
> x
[1] 0.66666667
> options(digits = 15)
> x
[1] 0.666666666666667
> options(digits = 16)
> x
[1] 0.6666666666666666
> options(digits = 17)
> x
[1] 0.66666666666666663
> options(digits = 18)
> x
[1] 0.66666666666666663
Wissenschaftliche Funktionen
R bietet natürlich zahlreiche wissenschaftliche Funktion; bei ihrer Verwendung ist wichtig zu wissen:
- Trigonometrische Funktionen rechnen immer im Bogenmaß (ins Winkelmaß muss man selber mit einem Dreisatz umrechnen).
- Der Zahlenbereich, in dem R rechnen kann, ist natürlich beschänkt; soll ein Funktionswert berechnet werden, so dass der Rückgabewert nicht mehr im Zahlenbereich liegt, wird Inf angegeben. Dabei kann Inf sogar mir einem Vorzeichen versehen sein; man sollte sich aber nicht darauf verlassen, dass dieses Vorzeichen im Sinne eines mathematischen Grenzwertes verwendet werden kann.
- Gibt es einen anderen Grund, aus dem der Rückgabewert nicht berechnet werden kann (etwa die Berechnung der Wurzel aus einer negativen Zahl), wird NaN (= Not a Number) erzeugt.
Mehr Informationen zu Inf und NaN folgen in einem eigenen Abschnitt Zahlenbereiche und spezielle Werte (Inf, NaN, NA und NULL); dort wird auch ausführlich besprochen, wie man feststellen kann, welcher Zahlenbereich für ganze Zahlen und Gleitkommazahlen zulässig ist.
Das folgende Skript zeigt die Verwendung einiger Funktionen:
# <work>\01_BASICS\03_FUND_DAT_TYP\WissFkt.R
# Wissenschaftliche Funktionen
pi
x <- 0
1/x
sin(x)
asin(x)
cos(x)
acos(x)
tan(x)
atan(x)
exp(x)
log(x)
x <- 1
sin(x)
asin(x)
cos(x)
acos(x)
tan(x)
atan(x)
exp(x)
log(x)
x <- 10
# andere Basen:
log2(x)
log10(x)
logb(x, base = 5)
# Wurzel:
sqrt(x)
sqrt(-x)
# Betrag
abs(-x)
Es erzeugt die Ausgabe:
> # <work>\01_BASICS\03_FUND_DAT_TYP\WissFkt.R
> # Wissenschaftliche Funktionen
> pi
[1] 3.141593
> x <- 0
> 1/x
[1] Inf
> sin(x)
[1] 0
> asin(x)
[1] 0
> cos(x)
[1] 1
> acos(x)
[1] 1.570796
> tan(x)
[1] 0
> atan(x)
[1] 0
> exp(x)
[1] 1
> log(x)
[1] -Inf
> x <- 1
> sin(x)
[1] 0.841471
> asin(x)
[1] 1.570796
> cos(x)
[1] 0.5403023
> acos(x)
[1] 0
> tan(x)
[1] 1.557408
> atan(x)
[1] 0.7853982
> exp(x)
[1] 2.718282
> log(x)
[1] 0
> x <- 10
> # andere Basen:
> log2(x)
[1] 3.321928
> log10(x)
[1] 1
> logb(x, base = 5)
[1] 1.430677
> # Wurzel:
> sqrt(x)
[1] 3.162278
> sqrt(-x)
[1] NaN
Warnmeldung:
In sqrt(-x) : NaNs wurden erzeugt
> # Betrag
> abs(-x)
[1] 10
Funktionen zum Runden
In R gibt es mehrere Funktionen zum Auf- und Abrunden von Zahlen:
- floor(x): Abrunden
- ceiling(x): Aufrunden
- trunc(x): Abschneiden
- round(x, digits): Runden mit digits = Anzahl der Nachkommastellen (default-Wert: digits = 0),
- signif(x, digits): Runden mit digits = Anzahl der signifikanten Stellen (default-Wert: digits = 6).
Die ersten drei Funktionen der Liste besitzen nur den Zahlenwert x als Eingabewert, bei den letzten beiden Funktionen kann zusätzlich angegeben werden, auf wieviele Stellen gerundet werden soll.
Das folgende Skript zeigt die Verwendung von floor(), ceiling() und trunc() — man beachte dabei den Unterschied zwischen floor() und trunc():
# Abrunden, Aufrunden
floor(0.5) # 0
ceiling(0.5) # 1
trunc(0.5) # 0
floor(1.5) # 1
ceiling(1.5) # 2
trunc(1.5) # 1
floor(-0.5) # -1
ceiling(-0.5) # 0
trunc(-0.5) # 0
Das default-Verhalten von round() und signif() zeigt folgendes Skript:
# Runden
# default-Verhalten
options(digits = 7)
x <- 4 / 3
x # 1.333333
round(x) # 1
signif(x) # 1.33333
Zur Erklärung:
Zeile 4: Die Anzahl der digits werden auf ihren default-Wert von 7 zurückgesetzt (falls sie vorher verstellt wurden).
Zeile 5: Die Ausgabe von x erfolgt mit 7 Stellen.
Zeile 6: Durch den Aufruf von round(x) wird x ohne Nachkommastellen ausgegeben.
Zeile 7: Durch signif(x) wird x mit 6 signifikanten Stellen ausgegeben.
Das Verhalten wenn digits gesetzt wird, zeigt folgendes Skript:
# Runden mit Setzen von digits
round(1.667, digits = 2) # 1.67
signif(1.667, digits = 2) # 1.7
round(1.555, digits = 2) # 1.55
signif(1.555, digits = 2) # 1.6
round(144, digits = 2) # 144
signif(144, digits = 2) # 140
Man beachte das Verhalten von signif() in Zeile 10.
Aufgabe: Untersuchen Sie das Verhalten der Funktionen beim Runden von negativen Zahlen und konsultieren Sie dazu die Dokumentation.
Vergleichsoperatoren
Zwischen Zahlen können auch Vergleiche angestellt werden; das Ergebnis ist immer ein Wahrheitswert, also TRUE oder FALSE.
Das folgende Skript zeigt die Anwendung der Vergleichsoperatoren:
# <work>\01_BASICS\03_FUND_DAT_TYP\VerglOp.R
# Vergleichsoperatoren
a <- 5
b <- 17
identical(a, b) # Test auf Gleichheit
a == b # Test auf Gleichheit; nicht zu verwecheln mit der Zuweisung =
a != b # Test auf Ungleichheit
a > b
a < b
a >= b
a <= b
> # <work>\01_BASICS\03_FUND_DAT_TYP\VerglOp.R
> # Vergleichsoperatoren
> a <- 5
> b <- 17
identical(a, b)
[1] FALSE
> a == b
[1] FALSE
> a != b
[1] TRUE
> a > b
[1] FALSE
> a < b
[1] TRUE
> a >= b
[1] FALSE
> a <= b
[1] TRUE
Die Funktion identical(x, y)
und der Test x == y
scheinen identisch zu sein, es gibt aber drei wichtige Unterschiede:
- Mit
x == y
werden nur die Zahlenwerte von x und y verglichen, dagegen prüftidentical(x, y)
auf exakte Gleichheit der Argumente. Was dies bedeutet wird bei den Typumwandlungen unter Der Vergleich von Zahlen erläutert. - Sobald man für x und y Vektoren zulässt, haben
x == y
undidentical(x, y)
eine völlig andere Bedeutung: Mitx == y
werden die einzelnen Komponenten der Vektoren x und y verglichen und man erhält einen Vektor von logischen Werten (die Länge dieses Vektors stimmt mit der Länge von x und y überein). Dagegen vergleichtidentical(x, y)
die beiden Vektoren und liefert als Ergebnis einen logischen Wert. - Zudem lassen sich bei
identical(x, y)
weitere Argumente setzen, die hier noch nicht erklärt werden können.
Variablen
Variablen in der Mathematik
Wenn in einer Programmiersprache von einer Variable die Rede ist, sollte man dies nicht mit der Bedeutung von Variable in der Mathematik verwechseln. Es bestehen zwar viele Gemeinsamkeiten, aber auch Unterschiede.
In der Mathematik wird zum Beispiel 2x + 1 als Term bezeichnet, der die Variable x enthält. Der Begriff Variable soll darauf hinweisen, dass x in diesem Term beliebige Werte aus einer Grundmenge G annehmen kann, zum Beispiel G = R (reelle Zahlen). Setzt man diesen Term einem anderen Term gleich, zum Beispiel dem Term x + 2, so entsteht eine Gleichung:
2x + 1 = x + 2
Diese Gleichung kann nun nach x aufgelöst werden; die Menge aller x-Werte, die die Gleichung erfüllen, nennt man die Lösungsmenge L der Gleichung:
2x + 1 = x + 2 ⇒ x = 1
⇒ L = {1}
Von den unendlich vielen Werten, die x in den Termen annehmen kann, gibt es also nur einen Wert, der die Gleichung erfüllt.
Es wäre irreführend, diese Begriffe in eine Programmiersprache zu übertragen, wenn dort von Variablen die Rede ist
Variablen in Programmiersprachen
Programmiersprachen sind dazu da, Algorithmen zu formulieren, also eine Abfolge von Befehlen. Und dabei kann es vorkommen, dass eine Größe keinen konstanten Wert besitzt, sondern in der Abfolge unterschiedliche (variable) Werte annimmt.
Beispielsweise soll eine ganze Zahl N gegeben sein, für die ein Algorithmus alle Teiler bestimmen soll. Man wird dazu eine weitere Zahl n definieren, die alle Zahlen von 1 bis N durchläuft und für jeden dieser Werte untersucht man, ob bei der Division N/n ein Rest bleibt je nachdem ist n ein Teiler von N oder nicht.
In diesem Beispiel ist N eine Konstante, also eine Größe, die einmal gesetzt wird und deren Wert sich danach nicht mehr ändert. Dagegen ist n eine Variable, die im Ablauf des Programmes verschiedene Werte annehmen wird:
n = 1, 2, ... , N
Realisiert wird dieses Verhalten im Computer dadurch, dass für den Wert von n eine (genügend große) Speicherzelle reserviert wird, in der der aktuelle Wert von n abgelegt ist. Angesprochen werden Speicherzellen eigentlich über ihre Adresse. Aber genau dafür hat man Variablen eingeführt: Der Name der Variable, hier n, steht für die Adresse der Speicherzelle. Und mit Hilfe des Namens der Variablen kann man gewisse Werte in die Speicherzelle schreiben (Zuweisungen) oder den Wert der Speicherzelle abfragen und für andere Zwecke verwenden.
In R lautet die Syntax dafür:
x <- 17
x
x <- x + 2
x
- In Zeile 1 wird der Variable x der Wert 17 zugewiesen (intern wird also ein Speicherplatz reserviert und mit dem Wert 17 beschrieben).
- In Zeile 2 wird der Wert von x ausgegeben (Zugriff auf die Speicherzelle).
- In Zeile 3 wird der Variable x der Wert 19 zugewiesen (da zuerst nachgesehen wird, welcher Wert aktuell in der Speicherzelle enthalten ist, dann erst wird der neue Wert in die Speicherzelle geschrieben).
- In Zeile 4 wird der neue Wert 19 von x ausgegeben.
Beachten Sie, dass Zeile 3 in der Mathematik eine Gleichung ist, die keine Lösung besitzt:
x = x + 2 ⇒ L = { }
Mit dem Zuweisungs-Operator wird daraus aber eine sinnvolle Operation die einen neuen Wert von x abspeichert.
Deklaration, Initialisierung und Gültigkeitsbereich einer Variable
In vielen Programmiersprachen wird streng zwischen der Deklaration und der Initialisierung einer Variable unterschieden. So kann man etwa die Zuweisung von 17 an die Variable x oben in zwei Schritte zerlegen:
// Achtung: C++
int x; // Deklaration
x = 17; // Initialisierung
cout << x << endl; // Zugriff auf die Variable
- In Zeile 2 wird die Variable x deklariert, das bedeutet, dass der Speicherplatz für den betreffenden Datentyp (hier integer) reserviert wird. Da unterschiedliche Datentypen anders im Speicher abgelegt werden, ist es nötig den Datentyp anzugeben.
- In Zeile 3 wird x initialisiert: in die zugehörige Speicherzelle wird der Wert 17 geschrieben.
- In Zeile 4 wird auf die Speicherzelle zugegriffen (und der gespeicherte Wert auf der Konsole ausgegeben).
In R sind Deklarationen nicht nötig; es reicht also völlig aus zu schreiben:
x <- 17
Da es nur numerische Variablen, logische Variablen und Variablen für Zeichen geben kann, wird automatisch erkannt, dass es sich hier um einen numerischen Datentyp handeln muss. Und numerische Datentypen werden in R immer wie Gleitkommazahlen (double) behandelt, wenn man nicht ausdrücklich sagt, dass es sich um eine ganze Zahl handeln soll.
Dass Datentypen dynamische erkannt werden und daher niemals Deklarationen erforderlich sind, hat bei kleinen Projekten fast nur Vorteile: man spart sich sehr viel Schreibarbeit und kann sich mehr auf die eigentlichen Aufgaben eines Programmes konzentrieren. Bei größeren Projekten kann dies aber leicht zum Nachteil werden: Man verliert schnell den Überblick, welche Variable welchen Datentyp besitzt — was gerade bei den zusammengesetzten Datentypen sehr wichtig ist. Es schleichen sich dann Programmierfehler ein, die in einer streng typisierten Sprache wie C++ vom Compiler erkannt werden — in R trägt der Programmierer hier sehr viel mehr Verantwortung für die Richtigkeit des Programmes.
Weiter soll hier schon der Gültigkeitsbereich (scope) einer Variable erwähnt werden: jeder Variable ist ein Bereich zugewiesen, innerhalb dessen man auf die Variable zugreifen kann.
So spricht man zum Beispiel von globalen Variablen und von lokalen Variablen. Erstere sind in der ganzen .R-Datei gültig (oder sogar im gesamten Projekt, das aus einer Vielzahl von Dateien bestehen kann), Letztere haben einen klar abgegrenzten Gültigkeitsbereich.
Wenn zum ersten Mal Funktionen definiert werden, wird der Gültigkeitsbereich einer Variable näher erklärt.
Diagnose-Funktionen
Übersicht
In diesem Kapitel wurde bisher gezeigt:
- Wie man Variablen Zahlenwerte zuweisen kann (Deklaration und Initialisierung erfolgen in einem Schritt mit Hilfe des Zuweisungs-Operators
<-
). - Wie man die Werte der Variablen ausgibt (indem man einfach den Namen der Variable in die Konsole eingibt und
RETURN
drückt). - Welche Operationen mit Zahlen ausgeführt werden können.
- Im Kapitel Übersicht über Datentypen wurden bereits der Modus und der Speicher-Modus eines Objektes erklärt; für Zahlen ist relevant: der Modus einer Zahl ist "numeric", der Speicher-Modus ist entweder "integer" oder "double", per default werden Zahlen immer im Speicher-Modus "double" angelegt.
Für Zahlen mag es daher überflüssig erscheinen, schon jetzt weitere Diagnose-Funktionen zu besprechen — welche zusätzliche Informationen über ein Objekt der Art x <- 5
soll es schon geben?
Für zusammengesetzte Datentypen ist es aber wichtig, schnell die relevanten Informationen über ein Objekt bereitzustellen (gerade dann, wenn man sich nach einem Funktionsaufruf nicht sicher ist, welches Objekt gerade vorliegt). Daher werden jetzt schon die wichtigsten Diagnose-Funktionen besprochen; im Lauf des Kurses werden weitere Funktionen hinzukommen.
Die Diagnose-Funktionen sind generisch implementiert, das heißt sie können im Prinzip für jedes beliebige Objekt aufgerufen werden und die Implementierung ist abhängig vom Datentyp des Objektes — und versuchen so für jeden Datentyp die relevante Information bereitzustellen.
Diejenigen Diagnose-Funktionen, die schon für Zahlen sinnvoll eingesetzt werden können, sollen hier kurz erläutert werden — im folgenden Beispiel werden sie für x <- 5
aufgerufen:
x <- 5
y <- 4.1111
x
# [1] 5
print(x)
# [1] 5
print(x, digits=3)
# [1] 5
y
# [1] 4.1111
print(y, digits = 3)
# 4.11
mode(x)
# [1] "numeric"
storage.mode(x)
# [1] "double"
is.integer(x)
# [1] FALSE
is.double(x)
# [1] TRUE
is.numeric(x)
# [1] TRUE
str(x)
# num 5
Grob kann man die Diagnose-Funktionen darin unterteilen, ob sie den Wert eines Objektes (Inhalt) beschreiben oder dessen Stuktur (Form); manche Diagnose-Funktionen vermischen auch beides. Man erkennt in diesem Skript vier Arten von Diagnose-Funktionen:
- Ausgabe des Wertes einer Variable oder eines Objektes (Inhalt)
- Ausgabe von Modus und Speicher-Modus (Form; diese Funktionen wurden bereits ausführlich in der Übersicht über Datentypen in R besprochen)
- checker-Funktionen mit is-dot (Form).
- Ausgabe der Struktur mit str() (Form und Inhalt).
In den folgenden Abschnitten werden diese Diagnose-Funktionen im Detail beschrieben.
Die Funktion print()
Wird in der Funktion print() ein Objekt x als Argument angegeben, wird der Wert von x ausgegeben. Da diese Funktion sehr häufig benötigt wird, kann man dafür als Abkürzung nur das Objekt angeben; im folgenden Skript sind daher Zeile 3 und 6 gleichwertig:
x <- 5
print(x)
# [1] 5
x
# [1] 5
Die Funktion print() besitzt ein weiteres Argument digits, das beim Umgang mit Gleitkommazahlen sehr wichtig ist: damit kann die Anzahl der signifikanten Stellen beeinflusst werden:
x <- 19.99
print(x, digits = 1)
# 20
print(x, digits = 2)
# 20
print(x, digits = 3)
# 20
print(x, digits = 4)
# 19.99
print(x, digits = 5)
# 19.99
Aufgabe:
In obigem Skript wird die Zahl x <- 19.99
mit ein bis fünf digits ausgegeben. Formulieren Sie, welche Bedeutung das Argument digits hat und welche Strategie dabei zum Runden verwendet wird. Falls die Frage mit diesem Skript nicht eindeutig zu beantworten ist, schreiben Sie ein geeignetes weiteres Skript. (Hinweis: In der Dokumentation steht dazu: digits: minimal number of significant digits.)
Die Funktion format() liefert noch sehr viel mehr Möglichkeiten, um Zahlen geeignet auszugeben — mehr dazu findet sich wie immer in der Dokumentation.
Die checker-Funktionen is-dot
Zu allen Datentypen in R gibt es sogenannte checker-Funktionen (oder besser: object-checking functions), mit denen abgefragt werden kann, ob ein gegebenes Objekt x einen gewissen Datentyp besitzt. (Die checker-Funktionen werden oft als die Familie der is-dot-Funktionen bezeichnet.) Der Rückgabewert ist dann entweder TRUE oder FALSE.
Für Zahlen gibt es diese checker-Funktionen, um den Modus numeric beziehungsweise den Speicher-Modus (integer oder double) abzufragen. Der Name der checker-Funktionen beginnt immer mit is. und daher lauten die drei genannten Funktionen (aufgerufen mit x):
is.numeric(x)
is.integer(x)
is.double(x)
.
Alle in den Basis-Paketen verfügbaren is.-Funktionen lassen sich durch
methods(is)
anzeigen. Nur wenige davon können jetzt schon sinnvoll eingesetzt werden.
Die Struktur eines Objektes str()
Die Funktion str() ist so implementiert, dass möglichst viel Information über ein Objekt in einer Zeile ausgegeben wird. Im Skript oben erkennt man zum Beispiel, dass für x <- 5
der Modus (num als Abkürzung für numeric) und der Zahlenwert 5 ausgegeben werden (Zeile 28 und 29). Bei sehr komplexen Datentypen reicht dann oft eine Zeile nicht mehr aus.
Wie man aus der Dokumentation erfährt (man beachte, dass sich str() im Paket utils befindet), gibt es für str() noch zahlreiche weitere Argumente, mit deren Hilfe man die Ausgabe konfigurieren kann.
Typumwandlungen
Ganze Zahlen und Gleitkommazahlen
Wie schon in der Übersicht über Datentypen in R besprochen, gibt es in R für Zahlen die beiden Speicher-Modi integer und double (der Inhalt einer Speicherzelle wird in den Speicher-Modi anders interpretiert):
- integer für ganze Zahlen und
- double für Gleitkommazahlen.
Der Name double steht wie immer als Abkürzung für double precision, was vermuten lässt, dass es auch Gleitkommazahlen mit einfacher Genauigkeit gibt. Diese gibt es in den meisten Programmiersprachen, nicht aber in R.
Für den Umgang mit Zahlen in R ist wichtig zu wissen:
- Wird eine Zahl mit einem Zuweisungs-Operator erzeugt, etwa mit
x <- 5
, so hat x den Speicher-Modus double, obwohl auf der rechten Seite eine ganze Zahl steht. - Möchte man x als Zahl im Speicher-Modus integer abspeichern, muss dies ausdrücklich angewiesen werden.
Typumwandlungen zwischen ganzen Zahlen und Gleitkommazahlen
Umwandlung einer Gleitkommazahl in eine ganze Zahl
Das folgende Skript wurde bereits unter Typumwandlungen (coercion) in der Übersicht über Datentypen in R besprochen, es zeigt die beiden Möglichkeiten, wie man eine double-Zahl in eine integer-Zahl verwandeln kann:
x <- 5
storage.mode(x)
# [1] "double"
storage.mode(x) <- "integer"
storage.mode(x)
# [1] "integer"
y <- 17
storage.mode(y)
# [1] "double"
y <- as.integer(y)
storage.mode(y)
# [1] "integer"
Man kann:
- Entweder den Speicher-Modus ausdrücklich setzen (mit Hilfe der replacement-Version von storage.mode(), Zeile 6).
- Oder man kann die Typumwandlung ausdrücklich anweisen (mit Hilfe der Funktion as.integer(), Zeile 16).
Dabei ist folgendes Missverständmis zu beachten: Die Anweisung as.integer(y)
erzeugt aus dem Wert von y eine Zahl vom Speicher-Modus integer, aber der Speicher-Modus von y bleibt dadurch unverändert. Erst durch die Zuweisung y <- as.integer(y)
(Zeile 16) erhält y den neuen Speicher-Modus.
Was passiert, wenn — anders als im Beispiel oben — eine echte Gleitkommazahl in eine integer-Zahl verwandelt wird?
x <- 2.5
x <- as.integer(x)
storage.mode(x)
# [1] "integer"
x
# [1] 2
Die Typumwandlung von einer Gleitkommazahl zu einer ganzen Zahl schneidet immer ab: es gibt keine Regel, wonach ab .5 aufgerundet wird.
Aufgabe:
Untersuchen Sie, welches Ergebnis man erhält, wenn eine negative Gleitkommazahl in eine integer-Zahl verwandelt wird!
Umwandlung einer ganzen Zahl in eine Gleitkommazahl
Die umgekehrte Umwandlung von einer integer-Zahl zu einer double-Zahl erfolgt dagegen problemlos — das folgende Skript zeigt ein Beispiel:
y <- 17
y <- as.integer(y)
storage.mode(y)
# [1] "integer"
x <- y
storage.mode(x)
# [1] "double"
Obwohl in Zeile 8 auf der rechten Seite eine Zahl steht, deren Speicher-Modus ausdrücklich auf integer gesetzt wurde, ist die neu erzeugte Zahl x vom Speicher-Modus double.
So wie mit den object-checking functions (is-dot) ein Datentyp abgefragt werden kann, werden mit den object-coercion functions (as-dot) Typumwandlungen ausgeführt. Eingeführt wurden diese Funktionen in Object-Checking Functions (is-dot) und Object-Coercion Functions (as-dot) in der Übersicht über Datentypen in R; dort wurde auch schon gesagt, dass sie für alle Datentypen existieren und dass man sich alle derartigen Funktionen mit methods(is)
und methods(as)
anzeigen lassen kann.
Der Vergleich von Zahlen
Oben wurde bereits gesagt, dass sowohl x == n
als auch die Funktion identical(x, n)
die Gleichheit der Zahlen x und n prüft. Doch wie ist das Ergebnis, wenn beide Zahlen den identischen Wert aber unterschiedlichen Speicher-Modus besitzen? Um das Ergebnis vorwegzunehmen:
x == n
prüft nur die Gleichheit der Zahlenwerte von x und n,identical(x, n)
prüft zusätzlich, ob der Speicher-Modus der beiden Zahlen übereinstimmt.
Das folgende Skript zeigt dies:
x <- 5
n <- as.integer(x)
x == n
# [1] TRUE
identical(x,n)
# [1] FALSE
Zusammenfassung
Arithmetische Operationen
Operation | Beschreibung |
-x
| Vorzeichenwechsel von x |
x + y
| Addition von x und y |
x - y
| Differenz von x und y |
x * y
| Produkt von x und y |
x^y
| Potenz (x hoch y) |
x / y
| Division (x geteilt durch y) |
n %/% m
| Ganzzahl-Division von n und m |
n %% m
| Rest (mod oder modulo) bei der Division von x geteilt durch y |
?Arithmetic
| Dokumentation: Weitere Informationen im package base unter Arithmetic |
Vergleichsoperatoren
Operation | Beschreibung |
identical(a, b)
| Test auf Gleichheit von a und b |
a == b
| Test auf Gleichheit von a und b |
a != b
| Test auf Ungleichheit von a und b |
a > b
| Test, ob a größer ist als b |
a < b
| Test, ob a kleiner ist als b |
a >= b
| Test, ob a größer oder gleich b |
a <= b
| Test, ob a kleiner oder gleich b |
?Comparison
| Dokumentation: Weitere Informationen im package base unter Comparison |
Wissenschaftliche Funktionen
Konstante
pi
| Kreiszahl p = 3.141593 |
exp(1)
| Eulersche Zahl e = 2.718282 |
?Constants
| Dokumentation: Weitere Informationen im package base unter Constants |
Trigonometrischen Funktionen
Bei den trigonometrischen Funktionen ist der Eingabewert im Bogenmaß, bei den inversen Funktionen der trigonometrischen Funktionen (Arcus-Funktionen) ist der Rückgabewert im Bogenmaß.
Funktion | Beschreibung |
cos(x)
| Kosinusfunktion von x |
sin(x)
| Sinusfunktion von x |
tan(x)
| Tangensfunktion von x |
acos(x)
| Arkus-Kosinusfunktion von x |
asin(x)
| Arkus-Sinusfunktion von x |
atan(x)
| Arkus-Tangensfunktion von x |
atan2(y, x)
| atan2(y, x) == atan(y / x)
|
cospi(x)
| cospi(x) == cos(pi * x)
|
sinpi(x)
| sinpi(x) == sin(pi * x)
|
tanpi(x)
| tanpi(x) == tan(pi * x)
|
?Trig
| Dokumentation: Weitere Informationen im package base unter Trig |
Exponential- und Logarithmus-Funktion
Funktion | Beschreibung |
log(x, base)
| Logarithmus-Funktion; default-Wert für das Argument base ist die Eulersche Zahl e: exp(1)
|
log10(x)
| Logarithmus-Funktion zur Basis 10 |
log2(x)
| Logarithmus-Funktion zur Basis 2 |
exp(x)
| Exponential-Funktion (Basis ist die Eulersche Zahl e) |
log1p(x)
| Abkürzung für log(1 + x)
: log1p(x) == log(1 + x)
(Hilfe: 1p = 1 plus) |
expm1(x)
| Abkürzung für exp(x) - 1
: expm1(x) == exp(x) - 1
(Hilfe: m1 = minus 1) |
?log
| Dokumentation: Weitere Informationen im package base unter log |
??Hyperbolic
| Dokumentation: Informationen über hyperpolische Funktionen im package base unter Hyperbolic |
Weitere mathematische Funktionen
Funktion | Beschreibung |
abs(x)
| Absolutbetrag von x |
sqrt(x)
| Quadratwurzel aus x |
factorial(x)
| Fakultät für ganze Zahlen: x! = x * (x - 1)! = x * (x -1) * ... * 1
|
lfactorial(x)
| natürlicher Logarithmus der Fakultät: lfactorial(x) == log(factorial(x)
|
choose(n, k)
| Binomialkoeffizient k aus n (oder n über k) |
lchoose(n, k)
| natürlicher Logarithmus des Binomialkoeffizienten k aus n (oder n über k) |
?MathFun
und ?Special
| Dokumentation: Weitere Informationen im package base unter MathFun und Special |
Funktionen zum Runden von Zahlen
Funktion | Beschreibung |
ceiling(x)
| berechnet die kleinste ganze Zahl oberhalb von x |
floor(x)
| berechnet die größte ganze Zahl unterhalb von x |
trunc(x)
| schneidet die Nachkommastellen ab und gibt die entsprechende ganze Zahl zurück (bei negativen x: in Richtung 0) |
round(x, digits)
| rundet die Zahl x auf digits Nachkommastellen (default-Wert: digits = 0) |
signif(x, digits)
| rundet die Zahl x auf digits gültige Stellen (default-Wert: digits = 6) |
?Round
| Dokumentation: Weitere Informationen im package base unter Round |
Diagnose-Funktionen
Die folgende Tabelle listet die oben besprochenen Diagnose-Funktionen auf; in der rechten Spalte wird kurz ihre Aufgabe beschrieben:
Funktion | Beschreibung |
print(x, digits)
| Ausgabe von x mit bis zu digits gültigen Stellen |
mode(x)
| Abfrage des Modus von x |
mode(x) <- value
| replacement-Version von mode(): Setzen des Modus von x |
storage.mode(x)
| Abfrage des Speichermodus von x |
storage.mode(x) <- value
| replacement-Version von storage.mode(): Setzen des Speichermodus von x |
is.numeric(x)
| Feststellen, ob x den Modus numeric besitzt |
is.integer(x)
| Feststellen, ob x den Speicher-Modus integer besitzt |
is.double(x)
| Feststellen, ob x den Speicher-Modus double besitzt |
str(object)
| Ausgabe der Struktur von object (Modus und Wert) |
Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und den Datentyp des Rückgabewertes:
Funktion | Rückgabewert | Datentyp |
print(x, digits)
| Ausgabe und Rückgabe von x mit bis zu digits gültigen Stellen | Datentyp von x (Modus und Speicher-Modus sind unverändert) |
mode(x)
| Modus von x | Zeichenkette |
mode(x) <- value
| kein Rückgabewert, dient zum Setzen des Modus von x | kein Rückgabewert (replacement form von mode()) |
storage.mode(x)
| Speichermodus von x | Zeichenkette |
storage.mode(x, digits) <- value
| kein Rückgabewert, dient zum Setzen des Speichermodus von x | kein Rückgabewert (replacement form von storage.mode()) |
is.numeric(x)
| TRUE oder FALSE je nach Modus von x | "logical" |
is.integer(x)
| TRUE oder FALSE je nach Speicher-Modus von x | "logical" |
is.double(x)
| TRUE oder FALSE je nach Speicher-Modus von x | "logical" |
str(object)
| Kein Rückgabewert, nur Ausgabe der Struktur eines Objektes. | "NULL": kein Rückgabewert |