Vektoren in R: Anwendungen

Grundlegend für das Verständnis von Operationen, die mit Vektoren ausgeführt werden können, sind die punktweise Ausführung (eine Operation wird an die Komponenten weitergereicht) und der recycling-Mechanismus, der festlegt, wie Vektoren mit unterschiedlichen Längen verknüpft werden. Ausgehend hiervon werden zahlreiche Operationen vorgestellt, die mit Vektoren ausgeführt werden können (wie zum Beispiel statistische Funktionen, Sortier-Algorithmen, Mengen-Operationen).

Einordnung des Artikels

Dieses Kapitel setzt die Kenntnis der Eigenschaften von Vektoren voraus, die in Vektoren in R: der Datentyp vector erklärt wurden; lediglich Attribute sind für einen ersten Durchgang unerheblich.

Einführung

Mit den bisher im Kapitel Vektoren in R: der Datentyp vector vorgestellten Funktionen und Operationen lassen sich:

Es gibt aber noch eine Vielzahl weiterer Funktionen um:

Dabei ist insbesondere der in R verwendete recycling-Mechanismus bei Vektor-Operationen wichtig, um deren Verhalten richtig vorherzusagen. Gerade wer mit Mathematik besser vertraut ist als mit Programmierung, wird überrascht sein, wie hier Vektor-Operationen in dem Fall definiert sind, dass die Vektoren aufgrund ihrer unterschiedlichen Länge eigentlich nicht verknüpft werden dürfen.

Diesen recycling-Mechanismus und wie Operationen in R punktweise ausgeführt werden, sollte man beim ersten Durcharbeiten dieses Kapitels unbedingt verstehen und genügend Zeit darauf verwenden, um damit vertraut zu werden — auf diese Eigenschaften von R wird im Folgenden immer wieder zurückgegriffen und man benötigt sie für das Verständnis aller anderen zusammengesetzten Datentypen.

Dagegen reicht es bei den Funktionen für Vektoren, sich vorerst einen groben Überblick zu verschaffen, welche Funktionen es gibt und wie sie eingesetzt werden. Spätestens wenn Sie eigene Anwendungen mit den hier vorgestellten Funktionen schreiben, werden Sie diese Teile ausführlich durcharbeiten müssen.

Punktweise Ausführung von Operationen und der recycling-Mechanismus

Überblick

Um einen ersten Eindruck zu gewinnen, wie in R Rechenoperationen ausgeführt werden, wurden im Kapitel Einführung in R: Zahlen und Variablen zunächst die Grundrechenarten und Potenzen für Zahlen eingeführt. Um jetzt mit Vektoren besser vertraut zu werden, werden diese Operationen von Zahlen auf Vektoren verallgemeinert. Dabei sind zwei Punkte für das Verständnis von Vektor-Operationen in R von entscheidender Bedeutung:

Die Diskussion wird dann auch zeigen, dass sich diese beide Punkte eigentlich nicht voneinander trennen lassen. Um dennoch schrittweise vorzugehen, werden zunächst nur solche Fälle untersucht, bei denen der recycling-Mechanismus noch nicht eingesetzt wird. Erst wenn dafür die punktweise Ausführung von Operationen erklärt ist, werden die Fälle mit recycling-Mechanismus untersucht.

♦♦♦

Um dies gleich an einem typischen Beispiel aufzuzeigen:

Werden die Vektoren

u = (1, 2, 3, 4, 5) und v = (1, 1, 1, 1, 1)

addiert, so wird diese Addition komponentenweise (oder wie man auch sagt punktweise) ausgeführt:

u + v = (1+1, 2+1, 3+1, 4+1, 5+1) = (2, 3, 4, 5, 6).

Doch was passiert, wenn man die Vektoren

u = (1, 2, 3, 4, 5) und w = (1, 0)

addieren möchte?

Wer die Vektor-Rechnung aus der Mathematik kennt, wird hier vermutlich erwarten:

Der Vektor w wird solange mit Nullen aufgefüllt, bis er die Länge von u hat und dann wird wieder punktweise addiert, also

u + w = (1, 2, 3, 4, 5) + (1, 0, 0, 0, 0) = (2, 2, 3, 4, 5)

In R wird nicht so gerechnet! Der recycling-Mechanismus sorgt nämlich dafür, dass der Vektor w solange wiederholt (oder aneinandergehängt) wird, bis er 5 Komponenten hat. In R lautet daher die Addition von u und w:

u + w = (1, 2, 3, 4, 5) + (1, 0, 1, 0, 1) = (2, 2, 4, 4, 6)

Mit diesem Beispiel sollte auch klar sein, dass man sehr genau Bescheid wissen muss, wann der recycling-Mechanismus eingesetzt wird und welche Konsequenzen er hat — andernfalls werden viele Berechnungen unerwartete Ergebnisse liefern.

Das folgende Skript zeigt obige Berechnung:

u <- (1:5)
w <- c(1, 0)

u + w
# [1] 2 2 4 4 6

Punktweise Ausführung von Operationen

In R sind zahlreiche Operatoren für Vektoren definiert, man muss dabei aber beachten, dass sie meist punktweise (oder komponentenweise) ausgeführt werden; mit punktweise ist gemeint, dass zum Beispiel die Addition zweier Vektoren auf die einzelnen Komponenten übertragen wird:

u <- (1:5)                  # 1 2 3 4 5
v <- rep(1, times = 5)      # 1 1 1 1 1

w <- u + v
w                           # 2 3 4 5 6

Dies gilt ebenso für Subtraktion, Multiplikation, Division und Potenz:

u <- (1:5)                  # 1 2 3 4 5
v <- rep(1, times = 5)      # 1 1 1 1 1

w <- u - v                  # 0 1 2 3 4
w   <- u * v                # 1 2 3 4 5
w <- v / u                  # 1.00000 0.50000 0.33333 0.25000 0.20000
u^u                         # 1    4   27  256 3125

In der letzten Zeile wird also jeweils die Potenz einer Zahl n mit sich selbst genommen: nn, wobei n von 1 bis 5 läuft.

Im Kapitel Einführung in R: Zahlen und Variablen wurde ausführlich besprochen, dass es in R drei Operationen gibt, die mit der Division zusammenhängen:

  1. Die Division, die bis auf Rundungsfehler den exakten Wert liefert.
  2. Die Ganzzahl-Division, die den ganzzahligen Anteil bei der Division durch eine ganze Zahl liefert.
  3. Der Rest der bei einer Ganzzahl-Division entsteht.

Die Operatoren für diese drei Operationen sind:

Operation Operator-Symbol
Division /
Ganzzahl-Division %/%
Division mit Rest %%

Wie die Division werden auch die Ganzzahl-Division und die Berechnung des Restes punktweise ausgeführt; das folgende Skript zeigt ein Beispiel:

u <- (1:6)                          # 1 2 3 4 5 6
v <- rep(x = 2, times = 6)          # 2 2 2 2 2 2

# Division:
u / v
# [1] 0.5 1.0 1.5 2.0 2.5 3.0

# Ganzzahl-Division:
u %/% v
# [1] 0 1 1 2 2 3

# Rest:
u %% v
# [1] 1 0 1 0 1 0

Der recycling-Mechanismus in R

Im letzten Unterabschnitt Punktweise Ausführung von Operationen wurden die Vektoren stets von der Länge gewählt, dass bei punktweiser Ausführung einer Operation kein Zweifel besteht, wie diese Operation auszuführen ist. Im Beispiel im Unterabschnitt Überblick wurde schon gezeigt, wie der recycling-Mechanismus wirkt: haben die Vektoren nicht identische Länge, wird der kürzere Vektor so lange wiederholt bis die Längen beider Vektoren übereinstimmen. Bei der letzten Wiederholung wird eventuell nur ein Teil des kürzeren Vektors benötigt.

Hier nochmal einige Beispiele; das Skript zeigt auch die Fehlermeldung, die man dabei eventuell erhält:

u <- (1:5) 
w <- c(1, 0)
x <- 2

u + w
# [1] 2 2 4 4 6
# Warning message:
#   In u + w : longer object length is not a multiple of shorter object length

u + x
# [1] 3 4 5 6 7

x / u
# [1] 2.0000000 1.0000000 0.6666667 0.5000000 0.4000000

Man erkennt an diesem Beispiel, dass es drei Möglichkeiten gibt, wie der recycling-Mechanismus ausgeführt wird (die dritte Möglichkeit tritt hier noch nicht auf, ein Beispiel mit dem Skalarprodukt wird aber folgen):

  1. Die Operation wird mit dem recycling-Mechanismus ausgeführt, aber es wird keine Warnung oder Fehlermeldung ausgegeben (Zeile 10 und 13).
  2. Die Operation wird mit dem recycling-Mechanismus ausgeführt und zusätzlich wird eine Warnung auf der Konsole ausgegeben (Zeile 5).
  3. Die Operation kann nicht ausgeführt werden und man erhält die entsprechende Fehlermeldung (ein Beispiel folgt).

Der erste Fall tritt immer ein, wenn die Länge des längsten Vektors ein ganzzahliges Vielfaches der Länge des kürzeren Vektors ist (siehe Zeile 10 und 13).

Der zweite Fall tritt ein, wenn dies nicht gilt (Zeile 5).

Und manche Operationen (dritter Fall) sind derart abgesichert, dass sie nur mit Vektoren der richtigen Länge durchgeführt werden können (etwa Skalarprodukt mit dem Operator %*% ).

Besonders interessant im obigen Beispiel ist die Addition u + x (Zeile 10): Die Zahl x = 2 wird zum Vektor x = (2, 2, 2, 2, 2) erweitert und so zu u addiert.

Man könnte diese Operation auch einfacher (dennoch gleichwertig) interpretieren: Die Zahl x = 2 wird zu jeder Komponente von u addiert.

Ein Vorteil des automatischen Aneinaderhängens von Vektoren ist, dass man mehrere Formeln leicht zu einer Formel zusammenfassen kann; möchte man etwa

a1/2 = b ± 1

rechnen, so kann dies tatsächlich in einer Anweisung erledigt werden:

a <- b + c(-1, 1)

In diesem Fall ist a ein Vektor mit den beiden Komponenten b-1 und b+1.

Es fehlt noch die genauere Erklärung der Potenz:

Wird ein Vektor potenziert, so wird jede Komponente potenziert, wie das folgende Skript zeigt.

v <- (1:5)

v^2
# [1]  1  4  9 16 25

Ist die Potenz auch ein Vektor, mit identischer Länge wie die Basis, so wird wieder punktweise potenziert:

v <- (1:5)
p <- c(1, 2, 1, 2, 1)

v^p
# [1]  1  4  3 16  5

Stimmen die Längen von Basis und Potenz nicht überein, wird einer der beiden Vektoren durch den recycling-Mechanismus verlängert. Das folgende Skript zeigt die beiden Möglichkeiten:

v <- (1:5)
p <- c(1, 2)

v^p             # p wird erweitert zu 1 2 1 2 1
# [1]  1  4  3 16  5
# Warning message:
#   In v^p : longer object length is not a multiple of shorter object length

p^v             # p wird erweitert zu 1 2 1 2 1
# [1]  1  4  1 16  1
# Warning message:
#   In p^v : longer object length is not a multiple of shorter object length

Man erkennt, dass der Vektor v unverändert verwendet wird, dagegen wird p immer zu (1, 2, 1, 2, 1) erweitert.

Verknüpfung von Vektoren ungleicher Länge

Das folgende Beispiel soll zeigen, dass es nicht ratsam ist, Vektoren unterschiedlicher Länge miteinander zu verknüpfen und darauf zu vertrauen, dass man den recycling-Mechanismus verstanden hat und das Ergebnis vorhersagen kann. Werden nämlich drei (oder mehr) Vektoren addiert, hängt das Ergebnis von der Reihenfolge ab, in welcher die beiden Additionen durchgeführt werden.

In der Mathematik ist es unerheblich, ob eine Summe

v = a + b + c

in der Reihenfolge

v = (a + b) + c

oder in der Reihenfolge

v = a + (b + c)

berechnet wird. Im ersten Fall wird zuerst die Addition (a + b) ausgeführt und anschließend wird zum Ergebnis c addiert; man sagt kurz: die Addition wird von links nach rechts ausgeführt. Im zweiten Fall wird die Addition von rechts nach links ausgeführt.

Sind aber die drei Summanden Vektoren unterschiedlicher Länge, so lassen sich leicht Beispiele konstruieren, in denen die beiden Arten zu addieren, andere Ergebnisse liefern. Gibt man die Addition ohne Klammern ein, muss man wissen, in welcher Reihenfolge in R die Addition durchgeführt wird. (Das Verhalten gilt in R für alle Operationen, die gleichrangig sind.)

Das folgende Skript zeigt ein Beispiel mit unterschiedlichen Ergebnissen und es zeigt, dass in R von links nach rechts gerechnet wird:

a <- (1:5)
b <- c(1, 0, 1)
c <- c(1,2)

# Addition von links nach rechts:
(a + b) + c
# [1] 3 4 5 7 6
# Warning messages:
#   1: In a + b :
#   longer object length is not a multiple of shorter object length
# 2: In (a + b) + c :
#   longer object length is not a multiple of shorter object length

# Addition von rechts nach links:
 a + (b + c) 
# [1] 3 4 5 6 7
# Warning messages:
#   1: In b + c :
#   longer object length is not a multiple of shorter object length
# 2: In a + (b + c) :
#   longer object length is not a multiple of shorter object length

# Addition ohne Vorgabe der Reihenfolge:
a + b + c
# [1] 3 4 5 7 6
# Warning messages:
# 1: In a + b :
#   longer object length is not a multiple of shorter object length
# 2: In a + b + c :
#   longer object length is not a multiple of shorter object length

Test auf Gleichheit

Der Vergleichsoperator und die Funktion identical()

Schon bei Zahlen wurde kurz darauf hingewiesen, dass bei Vektoren ein deutlicher Unterschied besteht, ob man die Gleichheit zweier Vektoren mit Hilfe des Vergleichsoperators == oder der Funktion identical(x, y) testet. Das folgende Skript zeigt den Unterschied:

v <- (1:6)
w <- (1:6)
u <- c(v, v)  

v == w
# [1] TRUE TRUE TRUE TRUE TRUE TRUE

identical(x = v, y = w)
# [1] TRUE

v == u
# [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

identical(x = v, y = u)
# [1] FALSE
# hier gibt es keine Warnung

v == (1:10)
# [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE
# Warning message:
#   In v == (1:10) :
#   longer object length is not a multiple of shorter object length

Zur Erklärung:

Zeile 1 bis 3: Die Vektoren v und w sind nach Länge und dem Wert aller Komponenten identisch. Wendet man bei einem Vergleich von v und u den recycling-Mechanismus an, so haben beide Vektoren die Länge 12 und stimmen in allen Komponenten überein — es ist also nicht leicht vorherzusagen, welches Ergebnis ein Vergleich von v und u liefern wird.

Zeile 5 und 6: Der Vergleich von v und w mit dem Operator == liefert einen logischen Vektor mit 6 Komponenten, die alle gleich TRUE sind.

Zeile 8 und 9: Vergleich von v und w mit identical() liefert einen logischen Wert, nämlich TRUE — es gibt keine Eigenschaft, in der sich die Vektoren unterscheiden.

Zeile 11 und 12: Beim Vergleich von v und u mit dem Operator == wird durch den recycling-Mechanismus v zu einem Vektor mit 12 Komponenten ergänzt; dieser stimmt in jeder Komponente mit u überein und deshalb erhält man einen logischen Vektor mit 12 Komponenten (alle gleich TRUE). Es gibt hier keine Warnung bezüglich unterschiedlicher Vektor-Längen, da v bei Wiederholung in u passt (2*6 = 12).

Zeile 14 und 15: Beim Vergleich von v und u mit identical() erhält man nur FALSE und keinerlei Warnung (die Vektoren haben unterschiedliche Länge und sind somit verschieden).

Zeile 18 bis 22: Vergleicht man dagegen v mit (1:10) mittels == , erhält man eine logischen Vektor mit 10 Komponenten und zusätzlich die bekannte Warnung, wonach die Vektor-Längen kein Vielfaches voneinander sind.

Test auf näherungsweise Gleichheit: die Funktion all.equal()

In der Mathematik werden viele Entscheidungen, ob eine gewisse Eigenschaft vorliegt oder nicht, durch Kriterien der folgenden Art formuliert:

Falls x = 0 gilt die Aussage A, falls x ungleich null ist, gilt die Aussage A nicht.

Beispiele dafür sind etwa:

Die Liste lässt sich beliebig fortsetzen. Aber es sollte klar sein, dass sich solch scharf formulierte Kriterien in einem Programm niemals überprüfen lassen. Denn dadurch dass für eine Gleitkommazahl nur endlich viel Speicherplatz zur Verfügung steht, wird es immer zu Rundungsfehlern kommen. Und ein Ergebnis der Art x = 2 · 10-10 ist zwar in der Mathematik eindeutig interpretierbar (x ist ungleich null), aber als Ergebnis einer Berechnung in einem Programm zweideutig:

Da die in einem Programm durchgeführten Berechnung nur in Ausnahmefällen daraufhin nachvollziehbar sind, welcher der beiden Fälle vorliegt, sollte man die Kriterien aus der Mathematik nur sehr vorsichtig anwenden.

Hat man die Möglichkeit abzuschätzen, in welcher Größenordnung die Rundungsfehler liegen, so kann man zumindest überprüfen, ob zwei Zahlen näherungsweise gleich sind; näherungsweise heisst hier: bis auf eine Differenz, die kleiner ist als eine vorgegebene Toleranz.

In R kann diese Prüfung auf näherungsweise Gleichheit mit Hilfe der Funktion all.equal() durchgeführt werden. Wie schon aus dem Namen zu schließen ist, lassen sich nicht nur einzelne Zahlen, sondern auch Objekte mit zusammengesetzten Datentypen vergleichen. Die Funktion ist generisch implementiert, das heißt, dass je nach Datentyp der Eingabewerte ein andere Implementierung aufgerufen wird.

Die Funktion all.equal() besitzt drei Eingabewerte (in Wirklichkeit weitere, die hier nicht besprochen werden):

Der default-Wert von tolerance lässt sich abfragen:

# default-Wert der Toleranz:
sqrt(.Machine$double.eps)
# [1] 1.490116e-08

Es folgen einige Beispiele:

# Vergleich zweier Zahlen:
a <- 2
b <- 1.99

# Vergleich mit default-Wert von tolerance:
all.equal(target = a, current = b)
# [1] "Mean relative difference: 0.005"

# Vergleich mit tolerance = 0.1:
all.equal(target = a, current = b, tolerance = 0.1)
# [1] TRUE

# Vergleich mit tolerance = 0.001:
result <- all.equal(target = a, current = b, tolerance = 0.001)
# [1] "Mean relative difference: 0.005"

str(result)
# chr "Mean relative difference: 0.005"

# Vergleich zweier Vektoren:
x <- c(2, 2)
y <- c(1.95, 1.995)

all.equal(target = x, current = y, tolerance = 0.1)
# [1] TRUE

all.equal(target = x, current = y, tolerance = 0.01)
# [1] "Mean relative difference: 0.01375"

all.equal(target = x, current = y, tolerance = 0.001)
# [1] "Mean relative difference: 0.01375"

Folgende Eigenschaften der Funktion all.equal() sollen ausdrücklich betont werden:

Funktionen für Vektoren

Versuch einer Einteilung

In R gibt es natürlich schon eine Vielzahl von Funktionen, die Vektoren verarbeiten. Und es ist immer besser, diese Funktionen einzusetzen als sie selber zu programmieren. Der Grund liegt darin, dass viele dieser Funktionen eine lange Liste von Eingabewerten haben und man durch Setzen der Eingabewerte sehr viele Spezialfälle abfangen kann, die einen hohen Programmier-Aufwand erfordern würden.

Aufgrund ihrer Vielzahl fällt es aber schwer, die Funktionen sinnvoll in Gruppen einzuteilen:

Hier wird grob folgende Einteilung der Funktionen vorgenommen:

  1. Funktionen, die man eigentlich als Eingabewert eine Zahl besitzen, denen in R aber auch ein Vektor übergeben werden kann.
  2. Funktionen, die eigentlich dafür vorgesehen sind, einen Vektoren weiterzuverarbeiten.
  3. Funktionen, die eigentlich dafür vorgesehen sind, zwei oder mehrere Vektoren weiterzuverarbeiten.
  4. Funktionen, mit denen Vektoren gefiltert oder umgeordnet werden können oder ähnliche Aufgaben erledigt werden können. Manche dieser Aufgaben lassen sich einfacher erledigen, wenn man mit Faktoren (anstelle von Vektoren) arbeitet — Faktoren werden in einem eigenen Kapitel vorgestellt.

Es wird sich allerdings zeigen, dass sich nicht alle Funktionen so eindeutig einer dieser Gruppen zuordnen lassen — eine bessere Übersicht über die hier beschriebenen Funktionen liefert vielleicht die Zusammenfassung am Ende des Kapitels.

Die vier genannten Gruppen sollen sofort durch typische Beispiele veranschaulicht werden:

Beispiel: Wurzelfunktion

Die Wurzelfunktion ist in der Mathematik für einen Eingabewert definiert. Da in R nicht zwischen Zahlen und Vektoren unterschieden wird, ist zu erwarten, dass man als Eingabewert auch einen Vektor verwenden darf. Das Beispiel der Potenz hat schon gezeigt, dass die Berechnung dann eben komponentenweise durchgeführt wird.

Das folgende Skript zeigt die Anwendung der Wurzelfunktion auf einen Vektor:

v <- c(1, 4, 9, 16, 25)

w <- sqrt(v)
w
# [1] 1 2 3 4 5

Man kann diese verallgemeinerte Definition von Funktionen in R zum Beispiel dafür nutzen, um Wertetabellen zu erzeugen.

Beispiel: Mittelwert

In Zahlenbereiche und spezielle Werte in R (Inf, NaN, NA und NULL) wurde die Bedeutung vn NA (not available) diskutiert. Bei der Berechnung eines Mittelwertes eines Vektors kann es vorkommen, dass nicht alle Komponenten als Zahlen gegeben sind, sondern einige als NA gekennzeichnet sind. Bei vielen Funktionen in R lässt sich leicht bestimmen, wie mit den NA-Werten umgegangen werden soll.

Der Mittelwert eines Vektors wird in R mit Hilfe der Funktion mean() berechnet; das Beispiel unten zeigt, dass man in dieser Funktion zusätzlich bestimmen kann, ob NA-Werte im Eingabe-Vektor ignoriert werden oder nicht. Werden sie nicht ignoriert, ist auch der Mittelwert NA (not available):

v <- (1:5)
v <- c(v, NA)

length(v)
# [1] 6

v
# [1]  1  2  3  4  5 NA

mean(v, na.rm = TRUE)
# [1] 3

mean(v, na.rm = FALSE)
# [1] NA

mean(v)
# [1] NA

Beispiel: Berechnung des Korrelationskoeffizienten

Für typische Aufgaben aus der Mathematik — insbesondere aus der Statistik — sind in R geeignete Funktionen implementiert. Hier wird ein Beispiel gezeigt, wie zu zwei Vektoren der Korrelationskoeffizient berechnet wird:

v <- (1:6)
w <- -2 * v + 3

c <- cor(x = v, y = w)
c
# [1] -1

Beispiel: Auswahl eines Teilvektors

Im folgenden Beispiel werden aus einem Vektor v diejenigen Komponenten ausgewählt, die größer sind als der Mittelwert von v. Der Teilvektor wird unter w abgespeichert. Die Auswahl erfolgt dadurch, dass die Bedingung formuliert wird, die die Komponenten des Teilvektors erfüllen müssen.

v <- (1:9)
w <- v[v > mean(v)]

w
# [1] 6 7 8 9

Wissenschaftliche Funktionen

Im Abschnitt Wissenschaftliche Funktionen im Kapitel Einführung in R: Zahlen und Variablen wurden zahlreiche wissenschaftliche Funktionen vorgestellt, die in R implementiert sind. In der Mathematik werden sie üblicherweise mit einem Eingabewert und einem Rückgabewert definiert. Da in R Zahlen nur Spezialfälle von Vektoren sind, werden diese Funktionen in R so implementiert, dass der Eingabewert ein beliebiger Vektor sein kann. Die Funktion wird dann komponentenweise auf die Eingabewerte angewendet und der Rückgabewert ist wieder ein Vektor (dessen Länge mit der des Eingabe-Vektors übereinstimmt).

Bei den Beispielen oben wurde dies für die Wurzelfunktion bereits demonstriert. Eine wichtige Anwendung dieser Implementierung der wissenschaftlichen Funktionen ist, dass man sehr leicht Wertetabellen erstellen kann. Das folgende Beispiel zeigt dies; zusätzlich wird ein Plot erstellt, was hier aber noch nicht näher erklärt wird.

v <- (0:24)
x <- pi * v / 12

y.sin <- sin(x)
print(x = y.sin, digits = 3)

y.cos <- cos(x)
print(x = y.cos, digits = 3)

plot(x, y.sin, "p")
plot(x, y.cos, "p")

Die x-Werte laufen von 0 bis 2 π im Abstand von π/12 (Zeile 1 und 2).

Die Ausgabe der y-Werte ist hier weniger spannend (Zeile 5 und 8); aussagekräftiger sind die Plots, die in Zeile 10 und 11 aus den berechneten Werten erzeugt werden und unten wiedergegeben sind.

Abbildung 1: Die erste Periode der Sinusfunktion berechnet an den oben definierten Stützstellen (Vektor x).Abbildung 1: Die erste Periode der Sinusfunktion berechnet an den oben definierten Stützstellen (Vektor x).

Abbildung 2: Die erste Periode der Kosinusfunktion berechnet an den oben definierten Stützstellen (Vektor x).Abbildung 2: Die erste Periode der Kosinusfunktion berechnet an den oben definierten Stützstellen (Vektor x).

Funktionen eines Vektors

In R sind zahlreiche Funktionen für Vektoren aufrufbar, ohne weitere Pakete laden zu müssen. Die meisten dieser Funktionen erklären sich selbst durch ihren Namen. Manche von ihnen berechnen einen einzigen Wert, manche einen Vektor (meist mit der Länge des Eingabe-Vektors).

Das folgende Skript zeigt die wichtigsten Beispiele:

v <- (1:5)          # 1 2 3 4 5

length(v)           # 5         Länge des Vektors

sum(v)              # 15        Summe der Komponenten
mean(v)             # 3         Mittelwert
var(v)              # 2.5       Varianz
sd(v)               # 1.5811    Standard-Abweichung (Wurzel aus der Varianz)

prod(v)             # 120       Produkt der Komponenten

diff(v)             # 1 1 1 1       Differenzen zwischen aufeinanderfolgenden Komponenten von v (die Länge von diff(v) ist um 1 kleiner als die von v)

cumsum(v)           #  1  3  6 10 15            Vektor, der die kumulierten Summen von v enthält
cumprod(v)          #  1   2   6  24 120        Vektor, der die kumulierten Produkte von v enthält
cummax(v)           #  1 2 3 4 5                Vektor, der die kumulierten Maxima von v enthält
cummin(v)           #  1 1 1 1 1                Vektor, der die kumulierten Minima von v enthält

min(v)              # 1     Minimum von v
max(v)              # 5     Maximum von v

Es lässt sich leicht feststellen, ob die oben berechnete Varianz die empirische Varianz ist oder nicht:

v <- (1:5)                      # 1 2 3 4 5
w <- (v - mean(v))^2            # 4 1 0 1 4

# Varianz:
sum(w)/length(v)                # 2

# empirische Varianz:
sum(w)/(length(v) - 1)          # 2.5

Mit var() wird also die empirische Varianz berechnet.

Das Skript, in dem die verschiedenen Funktionen zur Auswertung eines Vektors vorgestellt wurden, ist an einigen Stellen vielleicht nicht eindeutig, da mit einem sehr speziellen Vektor v <- (1:5) gearbeitet wurde. Daher wird das Skript nochmals für einen unregelmäßigen Vektor ausgeführt:

v <- c(3, 8, 1, 3, 7, 5, 2, 8, 9, 6)            # 3 8 1 3 7 5 2 8 9 6

length(v)           # 10            Länge des Vektors

sum(v)              # 52            Summe der Komponenten
mean(v)             # 5.2           Mittelwert
var(v)              # 7.955556      empirische Varianz
sd(v)               # 2.820559      Standard-Abweichung (Wurzel aus der empirischen Varianz)

prod(v)             # 2177280       Produkt der Komponenten

diff(v)             # 5 -7  2  4 -2 -3  6  1 -3     Differenzen zwischen aufeinanderfolgenden Komponenten von v (die Länge von diff(v) ist um 1 kleiner als die von v)

cumsum(v)           #  3 11 12 15 22 27 29 37 46 52         Vektor, der die kumulierten Summen von v enthält
cumprod(v)          #  3      24      24      72     504    2520    5040   40320  362880 2177280        Vektor, der die kumulierten Produkte von v enthält
cummax(v)           #  3 8 8 8 8 8 8 8 9 9              Vektor, der die kumulierten Maxima von v enthält
cummin(v)           #  3 3 1 1 1 1 1 1 1 1              Vektor, der die kumulierten Minima von v enthält

min(v)              # 1     Minimum von v
max(v)              # 9     Maximum von v

Die Funktion diff() berechnet für jeweils aufeinanderfolgende Komponenten die Differenz — aus dem Vektor v mit 10 Komponenten wird somit ein Vektor mit 9 Differenzen. Diese Funktion besitzt noch zwei weitere Argumente: lag (lag = Verzögerung, Zeitunterschied) und differences, die kurz erklärt werden sollen.

Der default-Wert für lag ist gleich 1: es werden Differenzen direkter Nachbarn im Eingabe-Vektor berechnet — der Eingabe-Vektor wird dadurch um 1 verkürzt. Setzt man lag gleich 2, werden die Differenzen von übernächsten Nachbarn genommen — jetzt wird der Eingabe-Vektor um 2 verkürzt. Und so weiter:

v <- c(3, 8, 1, 3, 7, 5, 2, 8, 9, 6)            # 3 8 1 3 7 5 2 8 9 6

diff(x = v)                         # 5 -7  2  4 -2 -3  6  1 -3
diff(x = v, lag = 2)                # -2 -5  6  2 -5  3  7 -2
diff(x = v, lag = 3)                # 0 -1  4 -1  1  4  4

Das Argument differences besitzt ebenfalls den default-Wert 1; differences gibt an, wie oft die Operation Differenzen bilden ausgeführt werden soll. Ist also differences = 2 , so wird die Operation diff(v) zweimal nacheinander ausgeführt:

v <- c(3, 8, 1, 3, 7, 5, 2, 8, 9, 6)            # 3 8 1 3 7 5 2 8 9 6

diff(x = v)                             # 5 -7  2  4 -2 -3  6  1 -3

diff(v, differences = 2)                # -12   9   2  -6  -1   9  -5  -4
diff(diff(v))                           # -12   9   2  -6  -1   9  -5  -4

diff(v, differences = 3)                # 21  -7  -8   5  10 -14   1
diff(diff(diff(v)))                     # 21  -7  -8   5  10 -14   1

Bei den hier vorgestellten Funktionen wurde suggeriert, dass sie in der Form f(x) angewendet werden müssen, wobei x ein Vektor ist. Dies ist zwar die Form, wie sie meist angewendet werden, aber viele Funktionen können allgemeiner verwendet werden, nämlich indem anstelle eines Vektors x beliebig viele Vektoren als Eingabewerte gesetzt werden. Dies wird durch die dot-dot-dot-Notation f(...) ausgedrückt. Wie diese Notation genau zu lesen ist und wie diese Funktionen dann angewendet werden, wird weiter unten am Beispiel der Funktionen all() und any() ausführlich erklärt. Ebenso ist in der Zusammenfassung unten eindeutig zu erkennen, welche Funktionen nur einen Vektor als Eingabewert haben und für welche die dot-dot-dot-Notation zutrifft.

Nähere Betrachtung der Funktion mean()

Wegen ihrer Wichtigkeit soll die Funktion mean() etwas genauer vorgestellt werden. Diese Betrachtung soll zugleich zeigen:

In der einfachsten Version wird mean() mit einem Argument x aufgerufen: x ist dabei ein Vektor, von dessen Komponenten der Mittelwert berechnet wird. Es wäre falsch, die einzelnen Komponenten als einzelne Argumente einzugeben, wie das folgende Beispiel zeigt:

mean(1, 3)          # syntaktisch korrekt
# [1] 1             # aber das Ergebnis ist unerwartet

mean(c(1, 3))
# [1] 2

Der Funktionsaufruf in Zeile 1 ist zwar syntaktisch korrekt, führt aber nicht zu dem erwarteten Ergebnis. In der Dokumentation ist nachzulesen, wie mean() aufgerufen werden muss, nämlich mit:

mean(x, ...)

Dabei ist x das Objekt, von dem der Mittelwert berechnet werden soll — meist ein Vektor. Und hinter ... verbergen sich weitere Argumente, die man an mean() übergeben kann — welche das sind, wird sofort erklärt.

Beim Aufruf mean(1, 3) wird die 1 interpretiert als x = 1 , woraus sich der Mittelwert 1 ergibt.

Dagegen zeigt Zeile 3 den — syntaktisch und semantisch — korrekten Aufruf von mean(): Der Mittelwert wird aus einem Vektor berechnet; um dies deutlicher auszudrücken, sollte man besser schreiben:

mean(x = c(1, 3))

Eine Funktion wie mean() könnte man leicht auf die Funktionen sum() und length() zurückführen oder sogar in wenigen Rechenschritten selbst implementieren. Wenn möglich, sollte man aber immer auf die vordefinierten Funktionen zurückgreifen. Denn mean() besitzt — wie viele andere Funktionen — ein weiteres Argument na.rm, mit dem angeben kann, wie nicht vorhandene Werte (NA, also not available) behandelt werden sollen. Das folgende Beispiel zeigt dies für mean():

v <- (1:5)
v <- c(v, NA)

length(v)
# [1] 6

v
# [1]  1  2  3  4  5 NA

mean(x = v, na.rm = TRUE)
# [1] 3

mean(x = v, na.rm = FALSE)
# [1] NA

mean(x = v)
# [1] NA

Werden nicht zugängliche Werte (NA) in die Berechnung mit aufgenommen, ist der Mittelwert nicht eindeutig definiert und wird dann auch mit NA angegeben (Zeile 13 und 14). Mit dem Argument na.rm lässt sich dies steuern; man sieht auch, dass der default-Wert von na.rm gleich FALSE ist (Zeile 16 und 17).

Die Funktion mean() besitzt noch ein weiteres Argument, das nützlich sein kann, um auszuwertende Daten aufzubereiten: Oft enthalten lange Messreihen sogenannte Ausreisser, die man nicht in die Auswertung aufnehmen möchte. Mit dem Argument trim lässt sich steuern, welcher Prozentsatz an Daten an beiden Seiten aus dem Vektor x entfernt werden. Besitzt x etwa 10 Komponenten und wird trim = 0.1 gesetzt, so wird der kleinste und größte Wert von x beseitigt und dann erst der Mittelwert berechnet:

v <- (1:8)

mean(x = v)
# [1] 4.5

w <- c(0, v, 10)

mean(x = w)
# [1] 4.6

mean(x = w, trim = 0.1)
# [1] 4.5

mean(x = w, trim = 0.09)
# [1] 4.6

Zur Erklärung:

Zeile 3 und 4: Der Mittelwert des Vektors v ist (1 + 2 + ... 8) / 8 = 4.5. Die Messwerte liegen symmetrisch um den Mittelwert.

Zeile 6 bis 9: Der Vektor w erweitert den Vektor um die Zahlen 0 und 10; die Messwerte liegen jetzt nicht mehr symmetrisch um den Mittelwert — der Mittelwert ist (0 +1 + 2 + ... 8 + 10) / 10 = 4.6.

Zeile 11 und 12: Durch die Angabe von trim = 0.1 werden 0 und 10 aus w entfernt; der Mittelwert stimmt wieder mit dem Mittelwert von v überein.

Zeile 14 und 15: Ist trim kleiner als 0.1, wird keine Komponente aus w entfernt; der Mittelwert ist wieder gleich 4.6.

Funktionen von zwei oder mehreren Vektoren

Bisher wurden Funktionen besprochen, die einen Vektor verarbeiten — und zusätzliche Argumente haben können. In diesem Unterabschnitt werden einige Funktionen vorgestellt, die zwei oder beliebig viele Vektoren verarbeiten — und wiederum zusätzliche Eingabewerte haben können:

  1. Die Funktionen pmin() und pmax(); dabei steht p für parallel.
  2. Das Skalarprodukt zweier Vektoren
  3. Kovarianz und Korrelationskoeffizient
  4. Mengen-Operationen.

Die Funktionen pmin() und pmax()

Die Funktionen pmin() und pmax() berechnen das parallele Minimum oder Maximum mehrerer Vektoren; mit parallel ist gemeint, dass für jede Komponente alle Eingabe-Vektoren untersucht werden und aus allen entsprechenden Komponenten das Minimum beziehungsweise Maximum ausgewählt wird:

v <- (1:9)
w <- (9:1)

pmin(v, w)          # 1 2 3 4 5 4 3 2 1
pmax(v, w)          # 9 8 7 6 5 6 7 8 9

Stimmen die Vektoren in ihrer Länge nicht überein, werden die kürzeren Vektoren solange wiederholt bis die Länge des längsten Vektors erreicht ist:

v <- 1:3
w <- 9:1

pmin(v, w)          # 1 2 3 1 2 3 1 2 1
pmax(v, w)          # 9 8 7 6 5 4 3 2 3

Dass man pmin() und pmax() mit beliebig vielen Vektoren aufrufen kann, zeigt das folgende Skript:

u <- rep(4, times = 9)
v <- 1:9
w <- 9:1

pmin(u, v, w)               # 1 2 3 4 4 4 3 2 1
pmax(u, v, w)               # 9 8 7 6 5 6 7 8 9

Das Skalarprodukt zweier Vektoren

Eine der am häufigsten benötigten Operationen im Zusammenhang mit Vektoren ist das Skalarprodukt (oder inneres Produkt). Abbildung 3 zeigt die Formel für zwei n-dimensionale Vektoren.

Abbildung 3: Formel zur Berechnung des Skalarproduktes für zwei n-dimensionale Vektoren.Abbildung 3: Formel zur Berechnung des Skalarproduktes für zwei n-dimensionale Vektoren.

Mit Hilfe von R kann es mit Hilfe der punktweisen Multiplikation zweier Vektoren und anschließender Addition der Komponenten realisiert werden — hier ein Beispiel für den Fall von 3 Dimensionen:

u <- c(1, 3, 5) v <- c(2, 0, -2)

sum(u * v) # -8

Diese Art der Berechnung des Skalarproduktes kann aber leicht zu unerwarteten Ergebnissen führen: Stimmen die Längen der Vektoren nicht überein, wird der kürzere Vektor solange wiederholt bis beide Länge übereinstimmen (recycling-Mechanismus). Die Berechnung des Skalarproduktes mittels sum(u * v) liefert dann ein anderes Ergebnis als man in der Mathematik erwartet: dort würde man das Skalarprodukt als nicht definiert betrachten oder den kürzeren Vektor mit Nullen auffüllen.

Im Zusammenhang mit der Matrizenmultiplikation (siehe Matrizen in R: der Datentyp matrix) wird eine weitere Möglichkeit vorgestellt, das Skalarprodukt zweier Vektoren zu berechnen. Es nutzt die Matrizenmultiplikation %*% (das Skalarprodukt ist ja lediglich ein Spezialfall davon) und testet damit automatisch, ob die Längen der Vektoren übereinstimmen.

u <- c(1, 3, 5)
v <- c(2, 0, -2)

u %*% v         # -8

w <- rep(x = 1, times = 2)

u %*% w         # Fehlermeldung
# Error in u %*% w : non-conformable arguments

Der Nachteil dieser Art der Berechnung des Skalarproduktes ist, dass der Rückgabewert von u %*% w eine Matrix ist — hier der Spezialfall einer 1 × 1-Matrix, also eigentlich einer Zahl.

Man kann daher folgende Empfehlungen aussprechen:

Kovarianz und Korrelationskoeffizient

Die Berechnung der Kovarianz und des Korrelationskoeffizienten wird hier nur kurz vorgeführt; ausführliche Erläuterungen finden sich in der Dokumentation im Paket stats unter cor.

Im folgenden Beispiel werden drei Vektoren definiert, wobei der zweite und dritte Vektor eindeutig aus dem ersten hervorgehen:

v <- (1:6)

v1 <- -2 * v + 3
v1
# [1]  1 -1 -3 -5 -7 -9

v2 <- v^2
v2
# [1]  1  4  9 16 25 36

# Zusammenhang zwischen v und v1:
cov(x = v, y = v1)
# [1] -7
cor(x = v, y = v1)
# [1] -1

# Zusammenhang zwischen v und v2:

cov(x = v, y = v2)
# [1] 24.5
cor(x = v, y = v2)
# [1] 0.9789173

Mengen-Operationen

Im Paket base unter sets sind die in R verfügbaren Mengen-Operationen beschrieben. Welche Aufgaben diese Funktionen erfüllen, sollten sich sofort aus ihrem Namen erschließen — sie werden hier nicht sehr ausführlich erklärt:

Funktion Beschreibung
is.element(el, set) Testet, ob el in der Menge set enthalten ist
setequal(x, y) Testet, ob die beiden Mengen x und y identisch sind
union(x, y) Berechnet die Vereinigungsmenge der Mengen x und y
intersect(x, y) Berechnet die Durchschnittsmenge der Mengen x und y
setdiff(x, y) Berechnet die Differenz der Mengen x und y (Beachte: die Operation ist nicht symmetrisch bei Vertauschung der Argumente)

Um diese Funktionen anwenden zu können, muss man nur wissen, wie Vektoren als Mengen interpretiert werden:

  1. Man muss keine eigene Operation anwenden, um in eine der beschriebenen Funktionen eine Menge einzugeben.
  2. Es reicht, Mengen als Vektoren einzugeben, denn Vektoren werden bei der Weiterverarbeitung als Mengen interpretiert.
  3. Da in R nicht zwischen Zahlen und Vektoren unterschieden wird, gibt es auch keine Unterscheidung zwischen Element und Menge.

Der zweite Punkt bedeutet, dass mehrfach vorkommende Elemente ignoriert werden und man nicht auf die Reihenfolge der Elemente achten muss. Allerdings kann sich bei der Ausgabe dann eine ungewöhnliche Reihenfolge ergeben (und man selbst sortieren sollte).

Das folgende Beispiel zeigt dies:

setequal(x = c(1, 2, 1), y = (1:2))
# [1] TRUE

setequal(x = c(1, 2, 1), y = (2:1))
# [1] TRUE

m <- union(x = c(1, 3, 5), y = c(2, 4, 6))
m
# [1] 1 3 5 2 4 6
# Die Ausgabe erfolgt unsortiert

m <- union(x = c(1, 3, 5, 1), y = c(2, 4, 6))
m
# [1] 1 3 5 2 4 6
# Aber doppelte Komponenten werden unterdrückt

Man erkennt:

Den dritten Punkt oben kann man an der Funktion is.element() erläutern: Die Eingabewerte der Funktion is.element(el, set) suggerieren, dass getestet wird, ob ein Element el in der Menge set enthalten ist. Man kann hier für el aber auch wieder einen Vektor eingeben und es wird für jede Komponente getestet, ob sie in set enthalten ist. Der Rückgabewert ist dann ein logischer Vektor mit der Länge von el:

m <- (1:6)
u <- c(1, 3, 5)

is.element(el = 1, set = u)
# [1] TRUE

is.element(el = 2, set = u)
# [1] FALSE

is.element(el = u, set = m)
# [1] TRUE TRUE TRUE

Funktionen zum Sortieren von Vektoren und verwandte Funktionen

Ein zentrales Thema beim Umgang mit Vektoren ist immer das Sortieren von Vektoren. Wer vor etwa 40 Jahren gelernt hat zu programmieren, hat vermutlich die meiste Zeit damit verbracht, Sortier-Algorithmen zu implementieren. Heute ist dies nicht mehr nötig, da zu jeder Programmiersprache umfangreiche Bibliotheken mit Sortier-Algorithmen existieren. In diesem Unterabschnitt werden einige Funktionen vorgestellt, mit denen Vektoren sortiert oder verwandte Aufgaben erledigt werden können. Vorgestellt werden die Funktionen, die in der folgenden Tabelle aufgelistet sind:

Funktion Beschreibung
rev(x) Erzeugt einen Vektor mit umgekehrter Anordnung der Komponenten wie im Vektor x (rev = reverse)
sort(x, decreasing = FALSE, na.last) Sortieren eines Vektors (besitzt zahlreiche Zusatz-Funktionalitäten)
is.unsorted(x, na.rm = FALSE, strictly = FALSE) Abfragen, ob ein Vektors x noch nicht sortiert ist.
unique(x) Entfernt mehrfach vorkommende Komponenten aus einem Vektor x
duplicated(x) Untersucht alle Komponenten von x und gibt an, ob ihr Wert schon einmal im Vektor vorgekommen ist.
order( ... , na.last = TRUE, decreasing = FALSE) Gibt die Indizes der sortierten Komponenten an (wo steht die kleinste Komponente, wo steht die zweit-kleinste Komponente und so weiter)
rank(x) Angabe der Rangfolge: zu jeder Komponente wird angegeben, welche Rangfolge sie im Vektor x hat, wenn man ihn sortieren würde.

Die Funktion rev()

Die Funktion rev(x) sorgt dafür, dass ein eingegebener Vektor x umgedreht wird; die Abkürzung rev steht natürlich für reverse.

v <- (1:9)
v
# [1] 1 2 3 4 5 6 7 8 9

v.rev <- rev(x = v)
v.rev
# [1] 9 8 7 6 5 4 3 2 1

Die Funktion sort()

Beim Sortieren eines Vektors greift man auf Komponenten des Vektors zu: sie werden entweder aufsteigend oder absteigend angeordnet — allerdings gibt es noch weitere Spitzfindigkeiten. Das folgende Skript zeigt die einfachsten Anwendungen der Funktion sort():

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)

v.default <- sort(x = v)
v.default
# [1] 1 1 2 2 3 3 4 5 6 6

v.decr <- sort(x = v, decreasing = TRUE)
v.decr
# # [1] 6 6 5 4 3 3 2 2 1 1

v.asc <- sort(x = v, decreasing = FALSE)
v.asc
# [1] 1 1 2 2 3 3 4 5 6 6

Zur Erklärung:

Zeile 1: Ein unregelmäßiger Vektor wird als v abgespeichert.

Zeile 3: Um das default-Verhalten von sort() zu untersuchen, wird nur der Vektor v übergeben.

Zeile 4 und 5: Die Ausgabe zeigt, dass per default aufsteigend sortiert wird.

Zeile 7: Das Argument decreasing erwartet einen logischen Wert und bestimmt ausdrücklich die Sortier-Reihenfolge. In v.decr wird v absteigend sortiert.

Zeile 11: In v.asc wird v aufsteigend sortiert. Da dies das default-Wert ist, kann man decreasing = FALSE auch weglassen.

Die Funktion sort() besitzt ein weiteres Argument, das einen logischen Wert erwartet: na.last. Damit lässt sich bestimmen, ob NA-Werte am Anfang oder am Ende des sortierten Vektors gestellt werden — ihre Anzahl bleibt unverändert.

v <- c(1, 5, NA, 2, 4, NA, 1, NA, 6, 3, 3, 6, 2)

v.default <- sort(x = v)
v.default
# [1] 1 1 2 2 3 3 4 5 6 6

v.last <- sort(x = v, na.last = TRUE)
v.last
# [1]  1  1  2  2  3  3  4  5  6  6 NA NA NA

v.first <- sort(x = v, na.last = FALSE)
v.first
# [1] NA NA NA  1  1  2  2  3  3  4  5  6  6

Man erkennt: wenn das Argument na.last nicht gesetzt wird (also selbst gleich NA ist), werden NA-Werte aus dem zu sortierenden Vektor entfernt.

Bisher wurden nur Zahlen sortiert. Da logische Werte intern als Zahlen gespeichert werden und auch für Zeichen eine lexikographische Anordnung existiert, erwartet man, dass auch Vektoren mit logischen Werten beziehungsweise Zeichen sortiert werden können:

v <- c(TRUE, FALSE, TRUE, FALSE)
v.log <- sort(v)
v.log
# [1] FALSE FALSE  TRUE  TRUE

v <- c('a', 'c', 'b')
v.char <- sort(v)
v.char
# [1] "a" "b" "c"

Man erkennt, dass per default wieder aufsteigend sortiert wird.

Die Sortierung von Zeichenketten kann zu zahlreichen Problemen führen (Behandlung von Sonderzeichen, Verwendung unterschiedlicher Kodierungen). Mehr zu diesem Thema findet man im Paket base unter Comparison.

Bekanntlich gibt es mehrere Sortier-Algorithmen, die je nach Anwendung ihre Vor- und Nachteil haben (kurzer oder langer Vektor, weitgehend vorsortierter Vektor) und sich deutlich in der Rechenzeit unterscheiden können. In R sind mehrere Sortier-Algorithmen implementiert und die Funktion sort() erlaubt mit Hilfe des Argumentes method einen oder mehrere davon auszuwählen. Dieses Thema soll hier aber nicht vertieft werden; eine ausführliche Beschreibung findet man in der Dokumentation zu sort().

---

Bei vielen Anwendungen muss man nur feststellen, ob ein Vektor v sortiert ist oder nicht. Da das Sortieren bei langen Vektoren viel Rechenzeit beansprucht, wäre es unsinnig, dazu v zuerst zu sortieren und dann zu vergleichen, ob der sortierte Vektor mit v übereinstimmt.

Sehr viel weniger Rechenzeit beansprucht die Funktion is.unsorted(); die Funktion besitzt drei Argumente:

Der Rückgabewert von is.unsorted() ist ein logischer Wert.

Das folgende Skript zeigt einige Anwendungen von is.unsorted():

v <- c(1, 1, 2, NA, 3)

is.unsorted(x = v)
# [1] NA

is.unsorted(x = v, na.rm = TRUE)
# [1] FALSE

is.unsorted(x = v, strictly = TRUE)
# [1] NA

is.unsorted(x = v, na.rm = TRUE, strictly =  TRUE)
# [1] TRUE

Die Funktion is.unsorted() besitzt kein Argument, das die Sortier-Reihenfolge festlegt, das heißt man kann mit ihr nur die aufsteigende Sortierung untersuchen (für absteigende Sortierung muss man die Funktion rev() einsetzen).

Die Funktion unique()

Um besser zu sehen, welche Elemente in einem Vektor enthalten sind, kann man sich aus einem gegebenen Vektor v alle mehrfach vorkommenden Elemente entfernen lassen; dies geschieht mit Hilfe der Funktion unique():

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)

v.uni <- unique(v)
v.uni
# [1] 1 5 2 4 6 3

sort(v.uni)
# [1] 1 2 3 4 5 6

Man kann die Wirkung von unique() auch anders interpretieren. In einem Vektor darf jeder Wert beliebig oft vorkommen; dagegen darf es bei der Angabe der Elemente einer Menge keine Wiederholungen geben. In diesem Sinne verwandelt unique() einen Vektor in eine Menge; aber erst ihre sortierte Ausgabe zeigt die typische Anordnung der Elemente.

Die Funktion duplicated()

Verwandt mit der Funktion unique() ist die Funktion duplicated(): sie erhält als Eingabewert einen Vektor x und erzeugt einen logischen Vektor, der für jede Komponente von x angibt, ob sie schon einmal im Vektor x vorgekommen ist.

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)

v.dupl <- duplicated(x = v)
v.dupl
# [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE

Die Funktion order()

Bei manchen Anwendungen möchte man nicht den sortierten Vektor bekommen, sondern man möchte wissen, an welcher Stelle (Index) der kleinste, der zweit-kleinste Wert und so weiter steht. Diese Information liefert die Funktion order():

v <- c(1, 5, 3)

v.ord <- order(v)
v.ord
# [1] 1 3 2

Die kleinste Zahl 1 in v steht an erster Stelle, die nächst größere Zahl 3 steht an dritter Stelle, die größte Zahl 5 steht an zweiter Stelle; der Aufruf von order(v) liefert daher den Vektor (1, 3, 2).

Um zu sehen, wie order() bei einer Folge mit Wiederholungen arbeitet, wird das Beispiel von oben verwendet:

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)

v.ord <- order(v)
v.ord
# [1]  1  5  3 10  7  8  4  2  6  9

Man kann den Vektor v.ord , also (1, 5, 3, 10, 7, 8, 4, 2, 6, 9), auch so lesen: Nimmt man aus dem Vektor v nacheinander

so erhält man den ausfsteigend sortierten Vektor zu v, also (1, 1, 2, 2, 3, 3, 4, 5, 6, 6).

Mit dem weiteren Argument decreasing = TRUE kann man die Sortier-Reihenfolge umdrehen und dafür die Indizes erzeugen:

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)

v.ord.dec <- order(v, decreasing = TRUE)
v.ord.dec
# [1]  6  9  2  4  7  8  3 10  1  5

Man kann die Funktion order() daher auch folgendermaßen beschreiben: Der Vektor der Indizes einer Zahlenfolge, also ein Vektor der Form (1:n) wird derart neu geordnet, wie es die Anordnung in der Zahlenfolge vorschreibt (ansteigende Anordnung beim default-Wert decreasing = FALSE und absteigende Anordnung bei decreasing = TRUE ). Somit erzeugt order() eine spezielle Permutation des Index-Vektors.

Die Funktion rank()

Verwandt mit der Funktion order() ist rank(): mit dieser Funktion lässt sich feststellen, welche Rangfolge eine Komponente in einem Vektor einnimmt, wenn der Vektor sortiert wird. Im Fall, dass jeder Wert in den Komponenten des Vektors nur einmal vorkommt, ist die Rangfolge eindeutig bestimmt; kommen Wiederholungen vor (in der Dokumentation werden sie als ties bezeichnet), kann man eine aus mehreren Strategien auswählen, wie die Rangfolge bestimmt wird. Die Strategie wird mit Hilfe des Argumentes ties.method ausgewählt.

Die Funktion rank() wird für das Beispiel oben mit dem Vektor (1, 5, 2, 4, 1, 6, 3, 3, 6, 2) erklärt, für den eine Rangfolge gebildet werden soll. Bei aufsteigender Sortierung ist 1 der kleinste Wert; und da 1 zweimal vorkommt, belegt die 1 den 1. und 2. Rang. Die 2 kommt ebenfalls zweimal vor, belegt also den 3. und 4. Rang. Die folgende Tabelle zeigt das Ergebnis der Rangfolgen-Bildung:

Plazierung Wert der Komponente mittlerer Rang
Rang 1 1 1.5
Rang 2 1 1.5
Rang 3 2 3.5
Rang 4 2 3.5
Rang 5 3 5.5
Rang 6 3 5.5
Rang 7 4 4.0
Rang 8 5 8.0
Rang 9 6 9.5
Rang 10 6 9.5

Die Tabelle zeigt die Strategie "average", die bei Wiederholungen den Mittelwert der Rangfolge berechnet. Das folgende Skript zeigt, wie der Vektor der Rangfolgen berechnet wird; die Strategie "average" muss dabei nicht angegeben werden, da sie der default-Wert für ties.method ist:

v <- c(1, 5, 2, 4, 1, 6, 3, 3, 6, 2)
  
v.r <- rank(x = v)
v.r
# [1] 1.5 8.0 3.5 7.0 1.5 9.5 5.5 5.5 9.5 3.5

v.r.min <- rank(x = v, ties.method = "min")
v.r.min
# [1] 1 8 3 7 1 9 5 5 9 3

In Zeile 7 wurde als Strategie "min" ausgewählt — der Unterschied zur Strategie "average" ist leicht festzustellen.

Es gibt kein Argument, das zwischen auf- und absteigender Sortier-Reihenfolge unterscheiden kann. Weitere Informationen, insbesondere zu weiteren Strategien und zur Behandlung von NA finden sich in der Dokumentation.

Vektoren und Bedingungsprüfungen

Im Abschnitt Ausblick auf Anwendungen mit Vektoren im Kapitel Einführung in R: Logische Werte wurde schon ein Beispiel gezeigt, das erahnen lässt, dass sich durch die Kombination von Vektoren und logischen Werten wichtige Funktionen zur Analyse von Vektoren bilden lassen. Hier wird gezeigt, wie die dort angedeuteten Funktionen realisiert werden.

Folgende Funktionen werden in diesem Unterabschnitt besprochen:

  1. subset(x, subset) : Auswahl eines Teilvektors aus einem Vektor x; ausgewählt werden diejenigen Komponenten, die eine Bedingung subset erfüllen. Der Rückgabewert ist ein Vektor (Modus wie x).
  2. which(x) : Untersucht den logischen Vektor x und gibt die Indizes der Komponenten mit Wert TRUE an. Rückgabewert ist ein Vektor von ganzen Zahlen.
  3. all(x) und any(x) : Gibt an, ob alle Komponenten im logischen Vektor x gleich TRUE sind beziehungsweise ob es mindestens eine Komponente gibt, die gleich TRUE ist. Rückgabewert ist jeweils ein logischer Wert.

Die Funktion subset()

Oft müssen aus einem Vektor nur bestimmte Komponenten ausgewählt werden, wobei durch einen logischen Ausdruck festgelegt wird, welche Komponenten dies sind; die Funktion subset() erledigt diese Aufgabe.

Sie erwartet zwei Argumente:

Der Rückgabewert ist ein (meist verkürzter) Vektor, in dem alle Komponenten die Bedingung subset erfüllen.

Damit verhält sich subset() wie der direkte Zugriff auf Komponenten eines Vektors mit einem logischen Ausdruck (was oben besprochen wurde, etwa wie v[v < 5] ). Der Aufruf von subset() ist aber deutlich leichter zu lesen.

Ein Beispiel, bei dem aus dem Vektor v diejenigen Komponenten ausgewählt werden, die größer sind als drei und zugleich ungerade sind (Division durch 2 ergibt Rest 1):

v <- (1:9)              # 1 2 3 4 5 6 7 8 9

w <- subset(x = v, subset = (v > 3) & (v %% 2 == 1))            #  5 7 9

Die Klammern im zweiten Argument von subset() dienen lediglich dazu, den logischen Ausdruck leichter lesbar zu machen und könnten weggelassen werden.

Nachdem oben Beispiele wie

v <- (1:9)
v1 <- v[v < 5]          # 1 2 3 4

diskutiert wurden, sollte klar sein, wie bei einem Aufruf von subset(x, subset) die Auswahl der Komponenten eines Vektors x mit Hilfe einer Bedingungsprüfung erfolgt:

Die Funktion which()

Mit der Funktion which() kann mit Hilfe einer Bedingungsprüfung auf die Indizes eines Vektors zugegriffen werden — also nicht auf die Komponenten selbst. Der Rückgabewert ist dann ein Vektor mit denjenigen Indizes, für die die Bedingung erfüllt ist.

Das folgende Beispiel demonstriert den Unterschied zwischen dem Zugriff auf die Komponenten und den Zugriff auf die Indizes:

vec <- c(1, 2, 1, 0, 1)
vec_1 <- vec[vec == 1]          # Zugriff auf die Komponenten von vec
vec_1                           # 1 1 1

ind_1 <- which(vec == 1)        # Zugriff auf die Indizes von vec
ind_1                           # 1 3 5

Zeile 2 und 3: Der Vektor vec_1 entsteht aus dem Vektor vec, indem alle Komponenten weggelassen werden, die ungleich 1 sind; vec_1 ist also kürzer als vec und besitzt nur die Einträge 1.

Zeile 5 und 6: Der Vektor ind_1 besitzt dieselbe Länge wie vec_1; seine Einträge sind aber die Indizes des Vektors vec, an denen dieser den Eintrag 1 besitzt.

Vektoren wie ind_1 aus dem Skript oben werden als Indexvektoren bezeichnet; im Kapitel Matrizen in R: der Datentyp matrix werden sie detaillierter vorgestellt.

Die Funktionen all() und any()

In der einfachsten Version untersuchen die Funktionen all() und any() einen logischen Vektor. Im Allgemeinen kann man mit ihnen:

Zunächst der einfachste Fall:

Ein logischer Vektor besteht aus den logischen Werten TRUE und FALSE.

Für die Funktion all() gilt:

Sind alle Komponenten eines logischen Vektors v.log gleich TRUE, so gibt all(v.log) den Wert TRUE zurück, andernfalls FALSE.

Für die Funktion any() gilt:

Ist eine oder sind mehrere Komponenten eines logischen Vektors v.log gleich TRUE, so gibt any(v.log) den Wert TRUE zurück, andernfalls FALSE.

Das folgende Beispiel zeigt, wie dies aussehen kann:

v1 <- vector(mode = "logical", length = 3)
v1[] <- FALSE

v2 <- !v1

v3 <- v1
v3[1] <- TRUE

v4 <- !v3

v1
# [1] FALSE FALSE FALSE

v2
# [1] TRUE TRUE TRUE

v3
# [1]  TRUE FALSE FALSE

v4
# [1] FALSE  TRUE  TRUE

all(v1)         # FALSE
all(v2)         # TRUE
all(v3)         # FALSE
all(v4)         # FALSE

any(v1)         # FALSE
any(v2)         # TRUE
any(v3)         # TRUE
any(v4)         # TRUE

---

Hat man einen Vektor mit beliebigem Modus gegeben, so kann man mit Hilfe einer Bedingungsprüfung, die auf jede Komponente angewendet wird, einen logischen Vektor erzeugen; dieser logische Vektor gibt dann an, welche Komponente die Bedingung erfüllt und welche nicht.

Mit all() kann man untersuchen, ob die Bedingung für alle Komponenten eines Vektors erfüllt ist.

Mit any() kann man untersuchen, ob es eine Komponente (oder mehrere Komponenten) eines Vektors gibt, die die Bedingung erfüllt.

v <- (1:5)
w <- (5:1)

v > w
# [1] FALSE FALSE FALSE  TRUE  TRUE

all(v > w)  
# [1] FALSE

any(v > w)
# [1] TRUE

Zur Erklärung:

Zeile 4: Sorgt dafür, dass ein logischer Vektor erzeugt wird, der komponentenweise angibt, ob die Bedingung v > w erfüllt ist oder nicht. Dieser Vektor wird nicht abgespeichert, um ausdrücklich zu zeigen:

Es ensteht somit ein logischer Vektor mit 5 Komponenten, der mit den Funktionen all() und any() untersucht wird (Zeile 7 und 10).

Zeile 7: Die untersuchte Bedingung ist nicht für alle Komponenten gleichzeitig erfüllt, daher liefert all() den Wert FALSE.

Zeile 10: Es gibt mindestens eine Komponente, die die angegebene Bedingung erfüllt, also liefert any() den Wert TRUE.

---

Ein Blick in die Dokumentation von all() und any() zeigt, dass die bisherige Darstellung dieser beiden Funktionen nicht ganz richtig ist: Es wurde suggeriert, dass beide Funktionen einen Vektor als Eingabewert erhalten. In der Dokumentation steht aber:

all(..., na.rm = FALSE)
any(..., na.rm = FALSE)
  1. Das Argument na.rm mit dem default-Wert FALSE wird hier nicht noch einmal erklärt (siehe die Diskussion der Funktion mean() weiter oben).
  2. Das Argument ... bedeutet: Den Funktionen all() und any() können beliebig viele logische Vektoren übergeben werden; sie werden aneinandergehängt und darauf wird all() beziehungsweise any() angewendet.

Das folgende Beispiel zeigt dies für zwei Eingabe-Vektoren:

v <- (1:5)
w <- -(5:1)

all(v > 0, w > 0)
# [1] FALSE

any(v > 0, w > 0)
# [1] TRUE

Der Index-Vektor

Das Problem, auf eine oder mehrere Komponenten eines Vektors zugreifen zu müssen, ist bei der Verarbeitung von Daten, die als Vektoren gespeichert sind, allgegenwärtig. Sowohl bei der Beschreibung von Vektoren im letzten Kapitel und bei den hier beschriebenen Anwendungen von Vektoren wurden dafür mehrere Möglichkeiten vorgestellt. Das Problem, von einem Vektor eine gewisse Teilmenge seiner Komponenten auszuwählen, soll hier nochmal etwas abstrakter unter Zuhilfenahme des Begriffs des Index-Vektors geschehen. Die Beschreibung weiterer zusammengesetzter Datentypen wird zeigen, dass das Konzept des Index-Vektors nicht nur auf Vektoren beschränkt ist.

Allgemein versteht man unter einem Index-Vektor einen Vektor, mit dem man auf eine Teilmenge der Komponenten eines Vektors -v zugreifen kann. Da der Vektor v indiziert ist mit den Indizes 1, 2,..., length(v), entspricht einer Teilmenge der Komponenten von v einer Teilmenge der Indizes.

In R gibt es vier Möglichkeiten, wie man einen Index-Vektor zu einem Vektor v bilden kann:

  1. Vektor der betreffenden Indizes; die Indizes sind positive, ganze Zahlen mit den möglichen Werten 1, 2,..., length(v).
  2. Vektor mit negativen Indizes: die Komponenten mit den entsprechenden Indizes werden aus dem Vektor v entfernt.
  3. Ein logischer Vektor mit der Länge von v: TRUE steht dafür, dass die Komponente übernommen wird und FALSE dafür, dass sie verworfen wird; ist der logische Vektor kürzer als v, wird der recycling-Mechanismus angewendet.
  4. Ist im Vektor das Attribut names gesetzt, kann auch über Vektoren von Zeichenketten auf die Komponenten von v zugegriffen werden (anstelle des Index wird hier der Name zum Zugriff verwendet).

Diese Kurzbeschreibung der vier Möglichkeiten, wie man einen Index-Vektors bildet, sollte eigentlich genügen — alle vier Möglichkeiten wurden bereits vorgestellt, sie wurden nur nicht unter ihrem gemeinsamen Aspekt betrachtet, nämlich eine Teilmenge aus den Komponenten eines Vektors auszuwählen. Unter diesem Aspekt werden die vier Möglichkeiten nochmals kurz beschrieben.

1. Vektor der betreffenden Indizes

Im folgenden Skript ist der Vektor v = (1, 0, 1, 0) gegeben und es wird ein Index-Vektor gebildet, der diejenigen Komponenten von v auswählt, die gleich 0 sind. Dazu gibt es mehrere Möglichkeiten.

Kennt man die Indizes, bei denen v == 0 , so kann man sie mit Hilfe der Funktion c() zum Index-Vektor iv.0 zusammenfassen:

v <- c(1, 0, 1, 0)

iv.0 <- c(2, 4)

v[iv.0]
# [1] 0 0

Zeile 1 und 3 sollten klar sein.

Zeile 5: Entscheidend zum Verständnis des Konzeptes Index-Vektor ist der Zugriff auf die Komponenten von v mit Hilfe des Index-Vektors iv.0. In den eckigen Klammern erwartet man vermutlich einen Index des Vektors v; ebenso kann ein Index-Vektor in die eckigen Klammern geschrieben werden. Der Vektor v wird an allen Komponenten ausgewertet, die der Index-Vektor iv.0 enthält — und so wie der Vektor iv.0 definiert war (Zeile 3), ist v dort immer gleich null. Da iv.0 zwei Komponenten hat, wird die 0 zweimal ausgegeben (Zeile 6). Oder abstrakter formuliert: Ein Index-Vektor besteht aus einer Teilmenge der Indizes eines Vektors v und man kann damit auf die zugehörige Teilmenge der Komponenten von v zugreifen.

Kennt man die betreffenden Indizes nicht, kann man andere Funktionen zuhilfe nehmen, um die Indizes zu bestimmen — zum Beispiel die Funktion which(), wie das folgende Skript zeigt:

v <- c(1, 0, 1, 0)

iv.0 <- which(v == 0)
iv.0
# [1] 2 4

str(iv.0)
# int [1:2] 2 4

v[iv.0]
# [1] 0 0

Zur Erklärung:

Zeile 3 bis 5: Da v ein numerischer Vektor mit 4 Komponenten ist, verbirgt sich hinter v == 0 ein logischer Vektor mit (ebenfalls) 4 Komponenten. Der Vektor iv.0 gibt diejenigen Komponenten in v an, die den Wert 0 besitzen, also die zweite und vierte Komponente (siehe Ausgabe von iv.0 in Zeile 5). Die Werte von iv.0 sind also eine Teilmenge der Indizes des Vektors v.

Zeile 7 und 8: Die Ausgabe der Struktur von iv.0 zeigt, dass es sich um einen Vektor mit 2 Komponenten und den Werten 2 und 4 handelt.

Zeile 10 und 11: Der Zugriff auf v mit dem Index-Vektor iv.0 liefert zweimal die Ausgabe 0 — die Erklärung ist identisch wie zum Skript oben.

2. Vektor mit negativen Indizes

Den Index-Vektor iv.0 kann man auch bilden, indem man die erste und dritte Komponente streicht; dies geschieht mit negativen Indizes, wie im folgenden Skript zu sehen ist:

v <- c(1, 0, 1, 0)

iv.0 <- c(-1, -3)
iv.0
# [1] -1 -3

v[iv.0]
# [1] 0 0

Möchte man dies wie oben mit der Funktion which() erledigen, könnte dies folgendermaßen geschehen:

v <- c(1, 0, 1, 0)

iv.0 <- - which(v != 0)
iv.0
# [1] -1 -3

v[iv.0]
# [1] 0 0

Zeile 3: Es werden zunächst mit which() alle Indizes ausgewählt, wo die Komponenten von v ungleich 0 sind; diese Indizes werden mit -1 multipliziert und dann im Vektor iv.0 abgespeichert.

Zeile 7: Greift man auf die durch den Index-Vektor iv.0 definierten Komponenten von v zu, erhält man wieder zweimal die 0.

Logischer Vektor als Index-Vektor

Diese Möglichkeit, einen Index-Vektor zu definieren, ist schon in iv.0 <- which(v == 0) enthalten. Denn mit v == 0 wird ein logischer Vektor definiert, nämlich (FALSE, TRUE, FALSE, TRUE). Es ist jetzt gar nicht nötig, mit Hilfe der Funktion which() die Indizes von v in einem numerischen Vektor abzuspeichern; man kann sofort den logischen Vektor verwenden (Zeile 3):

v <- c(1, 0, 1, 0)

iv.0 <- v == 0
iv.0
# [1] FALSE  TRUE FALSE  TRUE

str(iv.0)
# logi [1:4] FALSE TRUE FALSE TRUE

v[iv.0]
# [1] 0 0

Da auf der rechten Seite von Zeile 3 ein logischer Vergleich steht (für einen Vektor mit 4 Komponenten), ist iv.0 hier ein logischer Vektor. Die Ausgabe seiner Struktur bestätigt dies (Zeile 7). Auch der logische Vektor kann zum Zugriff auf die Komponenten von v verwendet werden (Zeile 10).

4. Indexvektor, der aus Zeichenketten gebildet wird

Die bisher genannten Möglichkeiten einen Index-Vektor zu bilden, können immer angewendet werden. Die letzte Möglichkeit setzt voraus, dass das Attribut names gesetzt ist.

v <- c(x1 = 1, x2 = 0, x3 = 1, x4 = 0)
v
# x1 x2 x3 x4 
# 1  0  1  0 

str(v)
# Named num [1:4] 1 0 1 0
# - attr(*, "names")= chr [1:4] "x1" "x2" "x3" "x4"

iv.0 <- c("x2", "x4")
iv.0
# [1] "x2" "x4"

str(iv.0)
# chr [1:2] "x2" "x4"

v[iv.0]
# x2 x4 
# 0  0

Zur Erklärung:

Zeile 1: Die Funktion c() wird in der Form angewendet, die es erlaubt sofort names zu setzen.

Zeile 2 bis 8: An der Ausgabe von v und dessen Struktur erkennt man das Attribut names.

Zeile 10: Es wird ein character-Vektor iv.0 gebildet (aus den Namen der zweiten und vierten Komponente von v).

Zeile 17: Auch dieser character-Vektor kann zum Zugriff auf die Komponenten von v verwendet werden. Anders ist jetzt nur die Ausgabe, da die Namen der Komponenten zusätzlich zu ihren Werten ausgegeben werden (Zeile 18).

Anwendung: Auswertung einer Zufallsfolge

Einführung

Die folgenden Aufgaben sollen zeigen, wie man die für Vektoren erläuterten Konzepte in der Praxis einsetzen kann. Als Beispiel wird eine Folge von Zufallszahlen ausgewertet. Wenn zusätzlich gezeigt wird, wie man eigene Funktionen schreiben kann und wie man Programmier-Konzepte (wie Bedingungsprüfungen und Schleifen) einsetzen kann, lassen sich die Quelltexte deutlich vereinfachen und so schreiben, dass sie wiederverwendbar sind.

Die Zufallsfolge v, die im Folgenden ausgewertet wird, lautet:

v <- c(32, 37, 25, 30, 34, 31, 36, 30, 29, 29, 28, 35, 30, 33, 29, 31, 36, 34, 30, 32, 27, 40, 24, 35, 35, 27, 29, 36, 29, 30, 24, 32, 31, 27,
24, 33, 30, 32, 28, 30, 35, 33, 37, 24, 33, 32, 35, 27, 31, 25, 35, 32, 24, 35, 36, 34, 38, 36, 32, 31, 33, 28, 30, 28, 31, 24, 27, 33,
35, 34, 30, 28, 25, 32, 26, 38, 32, 36, 30, 27, 40, 30, 33, 33, 32, 30, 33, 32, 32, 28, 30, 31, 31, 29, 28, 29, 39, 30, 31, 41, 36, 30,
33, 33, 34, 33, 30, 26, 29, 20, 34, 35, 31, 34, 37, 34, 31, 33, 33, 38, 32, 33, 39, 37, 30, 38, 38, 33)

Um sie leichter lesbar zu machen, wird eine sortierte Version erzeugt:

# sortierte Ausgabe von v

v_sort <- sort(v)
v_sort

Die Ausgabe von v_sort ergibt:

[1] 20 24 24 24 24 24 24 25 25 25 26 26 27 27 27 27 27 27 28 28 28 28 28 28 28 29 29 29 29 29 29 29 29 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
[49] 30 30 31 31 31 31 31 31 31 31 31 31 31 32 32 32 32 32 32 32 32 32 32 32 32 32 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 34 34 34 34 34 34
[97] 34 34 35 35 35 35 35 35 35 35 35 36 36 36 36 36 36 36 37 37 37 37 38 38 38 38 38 39 39 40 40 41

Enthaltene Zahlenwerte

An der sortierten Ausgabe erkennt man zwar sofort, dass die Zahlenfolge von 20 bis 41 läuft, es ist aber mühsam zu erkennen:

Das folgende Skript zeigt wie man diese Fragen leicht beantworten kann:

min_v <- min(v)         # 20
max_v <- max(v)         # 41

v_uni <- sort(unique(v))
v_uni
# [1] 20 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

length(v_uni)
# [1] 19

v_missing <- setdiff(x = (min_v:max_v), y = v_uni)
v_missing
# [1] 21 22 23

Statistische Eigenschaften der Zufallsfolge

Die wichtigsten Eigenschaften der Zufallsfolge v können durch folgendes Skript abgefragt werden, zudem wird ein Histogramm gezeichnet:

min_v <- min(v)         # 20
max_v <- max(v)         # 41

hist(x = v, breaks = (min_v:max_v))

options(digits = 4)

length(v)           # 128
mean(v)             # 31.66
var(v)              # 15.53
sd(v)               # 3.941

summary(v)
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 20.0    29.0    32.0    31.7    34.0    41.0

Abbildung 4: Histogramm für die Zufallsfolge v.Abbildung 4: Histogramm für die Zufallsfolge v.

Zur Erklärung:

Zeile 4: Das Histogramm wird mit Hilfe der Funktion hist() erzeugt, die hier nur kurz vorgestellt wird:

Nähere Erklärungen zu hist() finden sich in der Dokumentation im Paket graphics unter hist.

Zeile 9 bis 11: Für die statistische Auswertung werden die oben erläuterten statistischen Funktionen eingesetzt.

Zeile 13 bis 15: Einen schnellen Überblick über die statistischen Eigenschaften liefert auch die Funktion summary().

Spezielle statistische Eigenschaften der Zufallsfolge

Oben wurde festgestellt, dass der Mittelwert der Zufallsfolge knapp unterhalb 32 liegt. Man kann sich jetzt fragen, wie oft die Zahl 32 in der Zufallsfolge enthalten ist und bei welchen Indizes die 32 angenommen wird. Das folgende Skript erzeugt die entsprechenden Ausgaben:

h_32 <- length(subset(v, v == 32))          # 13
ind_32 <- which(v == 32)            #  1  20  32  38  46  52  59  74  77  85  88  89 121

An diesem Beispiel erkennt man:

Durch die Vielzahl von Funktionen, die R bereitstellt, gibt es meist mehrere Lösungen zu einem Problem. Die Häufigkeit, wie oft die Zahl 32 in der Zufallsfolge v vorkommt, hätte man auch aus der Länge von ind_32 bestimmen können.

Es gibt sogar eine noch einfachere Möglichkeit, die Häufigkeit der Zahl 32 in v festzustellen:

h_32 <- sum(v == 32)            # 13

Denn bei jeder Komponente von v die gleich 32 ist, ist die Bedingung v == 32 gleich TRUE und somit gleich 1. Werden nun diese Einsen aufaddiert, ergibt sich die Anzahl der 32 in v.

Die zwei Zeilen oben verlangen natürlich nach einer Verallgemeinerung: warum soll man die die Fragen nur für die Zahl 32 stellen? Später wird man dafür eine Funktion schreiben, die einen Wert der Zahlenfolge als Eingabewert erhält und die die Häufigkeit dieser Zahl beziehungsweise die Indizes in v zurückgibt.

Die Frage nach der Häufigkeit der Zahl 32 in der Zufallsfolge lässt sich auch folgendermaßen verallgemeinern: Wie groß ist die relative Häufigkeit der Zahlen im Intervall [ μ - σ, μ + σ ] = [ 27.71; 35.60]? Dabei ist μ der Mittelwert der Zahlenfolge mean(v) und σ die Standard-Abweichung sd(v).

m <- mean(v)
sigma <- sd(v)

length(which(v < m + sigma & v > m - sigma)) / length(v)            # 0.6953

Zusammenfassung

Der recycling-Mechanismus

Abbildung 5 zeigt das Beispiel, mit dem oben der recycling-Mechanismus erklärt wurde (Der recycling-Mechanismus in R), in der üblichen mathematischen Schreibweise mit Spaltenvektoren.

Abbildung 5: recycling-Mechanismus in mathematischer Schreibweise. Gleichung 1 zeigt die Addition zweier Vektoren gleicher Länge (sie wird komponentenweise ausgeführt und sollte keine Mehrdeutigkeiten zulassen). Gleichung 2 zeigt zwei Vektoren unterschiedlicher Länge. Wie werden sie addiert? In der Mathematik würde man sagen: Entweder die Addition ist nicht definiert. Oder der kürzere Vektor wird in den Raum des längeren Vektors eingebettet, indem er so lange mit Nullen aufgefüllt wird, bis die Längen übereinstimmen. Man erhält dann die Addition in Gleichung 3. In R wird auf den kürzeren Vektor der recycling-Mechanismus angewendet: er wird solange wiederholt, bis die beiden Vektoren gleich lang sind. Dann wird die Addition durchgeführt (Gleichung 4); sie wird im Allgemeinen ein anderes Ergebnis liefern als nach Gleichung 3.Abbildung 5: recycling-Mechanismus in mathematischer Schreibweise. Gleichung 1 zeigt die Addition zweier Vektoren gleicher Länge (sie wird komponentenweise ausgeführt und sollte keine Mehrdeutigkeiten zulassen). Gleichung 2 zeigt zwei Vektoren unterschiedlicher Länge. Wie werden sie addiert? In der Mathematik würde man sagen: Entweder die Addition ist nicht definiert. Oder der kürzere Vektor wird in den Raum des längeren Vektors eingebettet, indem er so lange mit Nullen aufgefüllt wird, bis die Längen übereinstimmen. Man erhält dann die Addition in Gleichung 3. In R wird auf den kürzeren Vektor der recycling-Mechanismus angewendet: er wird solange wiederholt, bis die beiden Vektoren gleich lang sind. Dann wird die Addition durchgeführt (Gleichung 4); sie wird im Allgemeinen ein anderes Ergebnis liefern als nach Gleichung 3.

Test auf Gleichheit von Vektoren

Die folgende Tabelle zeigt Funktionen, mit denen Vektoren auf Gleichheit getestet werden; selbstverständlich können diese Vektoren auch die Länge 1 besitzen. Nur bei der Funktion all.equal() müssen die zu vergleichenden Vektoren numerischen Typ besitzen.

Funktion Beschreibung
x == y Der Operator == ist der Vergleichsoperator: alle Komponenten der Vektoren x und y werden verglichen und für jeden Vergleich wird ein logischer Wert erzeugt; bei unterschiedlichen Vektor-Längen wird der recycling-Mechanismus angewendet.
identical(x, y) Gleichheit zweier Vektoren x und y kann nur bestehen, wenn sie identische Vektor-Längen haben. Das Ergebnis des Vergleichs wird in einem logischen Wert abgelegt: Falls die Vektor-Längen übereinstimmt und alle Komponenten identisch sind, wird TRUE zurückgegeben, andernfalls FALSE.
all.equal(target, current, tolerance) Vergleicht, ob die beiden numerischen Vektoren target und current näherungsweise — also bis auf die gegebene tolerance — übereinstimmen.

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und ihre Datentypen:

Funktion Rückgabewert Datentyp
x == y Logischer Vektor mit der Länge des längeren der beiden Vektoren x und y: jede Komponente gibt an, ob die entsprechenden Komponenten in x und y identisch sind (eventuell recycling-Mechanismus). logischer Vektor
identical(x, y) Ein logischer Wert, der angibt, ob gleichzeitig alle Komponenten von x und y Übereinstimmen (also TRUE, falls alle Komponenten übereinstimmen; FALSE, falls eine oder mehr Komponenten verschieden sind). Kein recycling-Mechanismus: FALSE bei unterschiedlichen Vektor-Längen. logischer Wert
all.equal(target, current, tolerance) TRUE, falls sich alle Komponenten von target und current um weniger als tolerance unterscheiden; eine Zeichenkette mit einer Beschreibung der Abweichung, falls eine oder mehr Komponenten sich um mehr als tolerance unterscheiden. der logische Wert TRUE oder eine Zeichenkette

Wissenschaftliche Funktionen

Im Kapitel Einführung in R: Zahlen und Variablen wurden die wissenschaftlichen Funktionen vorgestellt als hätten sie einen Eingabewert; in der Mathematik werden sie üblicherweise so definiert. In R kann man stattdessen einen Vektor von Zahlen eingeben und erhält als Rückgabewert wiederum einen Vektor: die Funktion wird auf jede Komponente des Eingabe-Vektors angewendet.

Da dies die einzige Neuerung gegenüber der Darstellung in Einführung in R: Zahlen und Variablen ist, werden die wissenschaftlichen Funktionen hier nicht nochmals aufgeführt.

Funktionen eines Vektors

Die folgende Tabelle zeigt eine Kurzbeschreibung der im Abschnitt Funktionen eines Vektors vorgestellten Funktionen. Man beachte dabei aber, dass die gezeigten Argumente meist nicht die komplette Liste der Eingabewerte darstellt. Es werden immer nur diejenigen Eingabewerte gezeigt, die hier auch besprochen wurden. Für die allgemeine Verwendung der Funktionen ist es erforderlich die Dokumentation zu Rate zu ziehen.

Man beachte, dass die Überschrift Funktionen eines Vektors nicht ganz richtig ist: manche der vorgestellten Funktionen können beliebig viele Vektoren als Eingabewerte aufnehmen, was durch das Argument ... ausgedrückt wird (dot-dot-dot-Notation); die Vektoren werden dann aneinandergehängt und als ein Vektor weiterverarbeitet. In der Praxis wendet man diese Funktionen meist mit einem Eingabe-Vektor an.

Funktion Beschreibung
length(x) Gibt die Länge eines Vektors an; ist nicht nur für Vektoren sondern für jedes Objekt definiert.
min( ..., na.rm = FALSE) Berechnet das Minimum der eingegebenen Werte (meist ein Vektor)
max( ..., na.rm = FALSE) Berechnet das Maximum der eingegebenen Werte (meist ein Vektor)
sum(..., na.rm = FALSE) Berechnet die Summe der eingegebenen Werte (meist ein Vektor)
prod(..., na.rm = FALSE) Berechnet das Produkt der eingegebenen Werte (meist ein Vektor)
diff(x, lag = 1, differences = 1) Berechnet die Differenzen zwischen aufeinanderfolgenden Komponenten von x für lag = 1 (mit lag wird festgelegt, wie groß der Abstand zwischen den Komponenten ist, deren Differenzen gebildet werden); differences gibt an, wie oft die Funktion diff() rekursiv aufgerufen wird.
cumsum(x) Kumulierte Summen des Vektors x
cumprod(x) Kumulierte Produkte des Vektors x
cummin(x) Kumuliertes Minimum des Vektors x
cummax(x) Kumuliertes Maximum des Vektors x
mean(x, trim = 0, na.rm = FALSE) Mittelwert des Vektors x; trim beschreibt den Bruchteil der Komponenten, die bei der Berechnung weggelassen werden (weggelassen werden die Komponenten ausgehend vom Minimum und Maximum)
var(x, na.rm = FALSE) Berechnet die empirische Varianz von x
sd(x, na.rm = FALSE) Berechnet die empirische Standard-Abweichung von x (Wurzel aus der empirischen Varianz)
?Extremes Weitere Informationen zu min(), max(),

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und ihre Datentypen; ist als Typ des Rückgabewertes Zahl angegeben, so ist damit eigentlich ein numerischer Vektor der Länge 1 gemeint:

Funktion Rückgabewert Datentyp
length(x) Länge von x (ist für jedes Objekt x definiert) Zahl
min( ..., na.rm = FALSE) Minimum der eingegebenen Werte Zahl
max( ..., na.rm = FALSE) Maximum der eingegebenen Werte Zahl
sum(..., na.rm = FALSE) Summe der eingegebenen Werte Zahl
prod(..., na.rm = FALSE) Produkt der eingegebenen Werte Zahl
diff(x, lag = 1, differences = 1) Differenzen zwischen aufeinanderfolgenden Komponenten von x für lag = 1 (mit lag wird festgelegt, wie groß der Abstand zwischen den Komponenten ist, deren Differenzen gebildet werden); differences gibt an, wie oft die Funktion diff() rekursiv aufgerufen wird. Vektor mit verringerter Anzahl von Komponenten (jeder rekursive Aufruf verringert die Anzahl um 1)
cumsum(x) Kumulierte Summen des Vektors x Vektor mit der Länge von x
cumprod(x) Kumulierte Produkte des Vektors x Vektor mit der Länge von x
cummin(x) Kumuliertes Minimum des Vektors x Vektor mit der Länge von x
cummax(x) Kumuliertes Maximum des Vektors x Vektor mit der Länge von x
mean(x, trim = 0, na.rm = FALSE) Mittelwert des Vektors x; trim beschreibt den Bruchteil der Komponenten, die bei der Berechnung weggelassen werden (weggelassen werden die Komponenten ausgehend vom Minimum und Maximum) Zahl
var(x, na.rm = FALSE) Berechnet die empirische Varianz von x Zahl
sd(x, na.rm = FALSE) Berechnet die empirische Standard-Abweichung von x (Wurzel aus der empirischen Varianz) Zahl

Funktionen von zwei oder mehreren Vektoren

Die folgende Tabelle zeigt eine Kurzbeschreibung von Funktionen, die zwei oder mehrere Vektoren als Eingabewert haben. Inhaltlich sind sie sehr verschieden und können folgenden Gruppen zugeordnet werden:

  1. Paralleles Minimum/Maximum
  2. Skalarprodukt
  3. Statistische Funktionen
  4. Mengen-Operationen
Funktion Beschreibung
pmin(..., na.rm = FALSE) Berechnet das parallele Minimum der eingegebenen Vektoren
pmax(..., na.rm = FALSE) Berechnet das parallele Maximum der eingegebenen Vektoren
?Extremes Weitere Informationen zu pmin(), pmax()
sum(u * v) Berechnet das Skalarprodukt der Vektoren u und v (kann bei unterschiedlichen Vektor-Längen zu unerwarteten Ergebnissen führen)
u %*% v Berechnet das Skalarprodukt der Vektoren u und v (liefert bei unterschiedlichen Vektor-Längen eine Fehlermeldung); der Operator %*% führt im Allgemeinen die Matrizenmultiplikation aus.
cov(x, y) Berechnet die Kovarianz der Vektoren x und y
cor(x, y) Berechnet den Korrelationskoeffizienten der Vektoren x und y
?cor Weitere Informationen zu Kovarianz und Korrelationskoeffizient im Paket stats unter cor
is.element(el, set) Testet für jede Komponente von el, ob sie in der Menge set enthalten ist
setequal(x, y) Testet, ob die beiden Mengen x und y identisch sind
union(x, y) Berechnet die Vereinigungsmenge der Mengen x und y
intersect(x, y) Berechnet die Durchschnittsmenge der Mengen x und y
setdiff(x, y) Berechnet die Differenz der Mengen x und y (Beachte: die Operation ist nicht symmetrisch bei Vertauschung der Argumente)
?sets Weitere Informationen zu Mengen-Operationen

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und ihre Datentypen:

Funktion Rückgabewert Datentyp
pmin(..., na.rm = FALSE) Paralleles Minimum der eingegebenen Vektoren Vektor mit der Länge des längsten Vektors
pmax(..., na.rm = FALSE) Paralleles Maximum der eingegebenen Vektoren Vektor mit der Länge des längsten Vektors
sum(u * v) Berechnet das Skalarprodukt der Vektoren u und v (kann bei unterschiedlichen Vektor-Längen zu unerwarteten Ergebnissen führen) Zahl
u %*% v Berechnet das Skalarprodukt der Vektoren u und v (liefert bei unterschiedlichen Vektor-Längen eine Fehlermeldung); der Operator %*% führt im Allgemeinen die Matrizenmultiplikation aus. 1 × 1-Matrix
cov(x, y) Kovarianz der Vektoren x und y Zahl
cor(x, y) Korrelationskoeffizient der Vektoren x und y Zahl zwischen -1 und 1
is.element(el, set) logischer Vektor, der für jede Komponente angibt, ob die entsprechende Komponente von el in der Menge set enthalten ist logischer Vektor mit der Länge von el
setequal(x, y) Testet, ob die beiden Mengen x und y identisch sind logischer Wert
union(x, y) Vereinigungsmenge der Mengen x und y Vektor (enthält keine doppelten Komponenten, im Allgemeinen nicht sortiert)
intersect(x, y) Durchschnittsmenge der Mengen x und y Vektor (enthält keine doppelten Komponenten, im Allgemeinen nicht sortiert)
setdiff(x, y) Differenz der Mengen x und y (Beachte: die Operation ist nicht symmetrisch bei Vertauschung der Argumente) Vektor (enthält keine doppelten Komponenten, im Allgemeinen nicht sortiert)

Funktionen zum Sortieren von Vektoren

Die folgende Tabelle zeigt Funktionen, mit denen Vektoren sortiert oder damit verwandte Aufgaben erledigt werden können:

Funktion Beschreibung
rev(x) Erzeugt einen Vektor mit umgekehrter Anordnung der Komponenten wie im Vektor x (rev = reverse)
sort(x, decreasing = FALSE, na.last) Sortieren eines Vektors (besitzt zahlreiche Zusatz-Funktionalitäten)
is.unsorted(x, na.rm = FALSE, strictly = FALSE) Abfragen, ob ein Vektors x noch nicht sortiert ist.
unique(x) Entfernt mehrfach vorkommende Komponenten aus einem Vektor x
duplicated(x) Untersucht alle Komponenten von x und gibt an, ob ihr Wert schon einmal im Vektor vorgekommen ist.
order( ... , na.last = TRUE, decreasing = FALSE) Gibt die Indizes der sortierten Komponenten an (wo steht die kleinste Komponente, wo steht die zweit-kleinste Komponente und so weiter)
rank(x) Angabe der Rangfolge: zu jeder Komponente wird angegeben, welche Rangfolge sie im Vektor x hat, wenn man ihn sortieren würde.

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und ihre Datentypen:

Funktion Rückgabewert Datentyp
rev(x) Vektor mit umgekehrter Anordnung der Komponenten wie im Vektor x Vektor
sort(x, decreasing = FALSE, na.last) Sortierte Version des Vektors x (mit decreasing wird die Sortier-Reihenfolge festgelegt, mit na.last wird festgelegt, ob NA-Werte am Anfang oder am Ende stehen) Vektor
is.unsorted(x, na.rm = FALSE, strictly = FALSE) Abfragen, ob ein Vektors x noch nicht sortiert ist. logischer Wert
unique(x) Vektor, der mit x übereinstimmt, aber keine Komponenten mehrfach enthält Vektor
duplicated(x) Logischer Vektor mit der Länge von x: FALSE für jeden Wert, der zum ersten Mal in x vorkommt, TRUE bei Wiederholungen logischer Vektor
order( ... , na.last = TRUE, decreasing = FALSE) Gibt die Indizes der sortierten Komponenten an (wo steht die kleinste Komponente, wo steht die zweit-kleinste Komponente und so weiter) Vektor
rank(x) Vektor mit den Rangfolgen der Komponenten von x Vektor

Vektoren und Bedingungsprüfungen

Die folgende Tabelle zeigt Funktionen, die auf Vektoren operieren und mit einer (oder mehreren) Bedingungsprüfung verbunden sind:

Funktion Beschreibung
subset(x, subset) Auswahl eines Teilvektors aus einem Vektor x; ausgewählt werden diejenigen Komponenten, die eine Bedingung subset erfüllen.
which(x) Untersucht den logischen Vektor x und gibt diejenigen Indizes an, für die x den Wert TRUE hat.
all(..., na.rm = FALSE) Mehrere logischen Vektoren Vektoren werden aneinandergehängt und es wird untersucht, ob alle Komponenten gleich TRUE sind.
any(..., na.rm = FALSE) Mehrere logischen Vektoren Vektoren werden aneinandergehängt und es wird untersucht, ob eine oder mehr Komponenten gleich TRUE sind.

Die folgende Tabelle beschreibt die Rückgabewerte der Funktionen und ihre Datentypen:

Funktion Rückgabewert Datentyp
subset(x, subset) Teilvektor von x; nur diejenigen Komponenten werden übernommen, die eine Bedingung subset erfüllen. Vektor (Länge ist kleiner oder gleich der Länge von x)
which(x) Untersucht den logischen Vektor x und gibt diejenigen Indizes an, für die x den Wert TRUE hat. Vektor von Indizes (Speichermodus "integer"; Länge ist kleiner oder gleich der Länge von x)
all(..., na.rm = FALSE) Mehrere logischen Vektoren Vektoren werden aneinandergehängt und es wird untersucht, ob alle Komponenten gleich TRUE sind. logischer Wert
any(..., na.rm = FALSE) Mehrere logischen Vektoren Vektoren werden aneinandergehängt und es wird untersucht, ob eine oder mehr Komponenten gleich TRUE sind. logischer Wert