Textverarbeitung mit R: Die Funktion paste0() zum Zusammenfügen von Vektoren

Die Funktion paste0() verknüpft entsprechende Komponenten von mehreren Vektoren; die Komponenten werden dazu in Zeichenketten verwandelt. Wird das Argument collapse nicht gesetzt, wird dieser Vektor von Zeichenketten zurückgegeben. Wird das Argument collapse gesetzt (es muss eine Zeichenkette sein), werden die Komponenten zu einer einzigen Zeichenkette zusammengefügt, wobei das Argument collapse als Trennungszeichen eingefügt wird. Typische Anwendungen und Spezialfälle werden erläutert.

Einordnung des Artikels

Fortsetzung dieses Artikels in Textverarbeitung mit R: Die Funktion paste() zum Zusammenfügen von Vektoren als Erweiterung von paste0().

Einführung

Die Funktion paste0() wird verwendet, um mehrere Objekte zuerst in eine Zeichenkette zu verwandeln und sie dann zu einer einzigen Zeichenkette oder einen Vektor von Zeichenketten zu verknüpfen. Sie arbeitet dabei nach dem "Reißverschlussprinzip", das unten näher erklärt wird.

In Dokumentation findet sich die Funktion paste0() im Paket base unter paste(). Diese ist eine Erweiterung der Funktion paste(), wird hier aber nicht besprochen.

Die Funktion paste0() besitzt zwei Eingabewerte:

paste0(..., collapse = NULL)

Die folgende Abbildung soll diese Vorgehensweise, das "Reißverschlussprinzip", erläutern:

Abbildung 1: Versuch einer graphischen Darstellung der Arbeitsweise von paste0(), wenn mehrere Vektoren zusammengefügt und ein Trennungszeichen eingefügt werden soll.Abbildung 1: Versuch einer graphischen Darstellung der Arbeitsweise von paste0(), wenn mehrere Vektoren zusammengefügt und ein Trennungszeichen eingefügt werden soll.

Eine der häufigsten Anwendungen von paste0() wird im ersten Abschnitt gezeigt: dort wird nur das Argument ... verwendet und paste0() wie die Funktion cat() eingesetzt mit einem entscheidenden Unterschied. Mit cat() (concatenation) wird die erzeugte Zeichenkette sofort auf der Konsole ausgegeben, mit paste0() wird eine Zeichenkette erzeugt, die in einer Variable abgespeichert und später verwendet werden kann – hier wird sie für die Überschriften von Diagrammen eingesetzt.

Die folgenden Abschnitte erläutern dann die Verwendung des Argumentes collapse = NULL .

Ein typisches Beispiel: Überschriften von Diagrammen

Um zu verstehen wie das Argument ... eingesetzt wird, hilft der Vergleich mit der Funktion cat(), wofür das folgende Skript ein typisches Beispiel einer debug-Ausgabe zeigt:

x <- 17
cat("x = ", x)
# x =  17

Zeile 1: Die Variable x wird initialisiert.

Zeile 2: Im Argument dot-dot-dot ... von cat() werden 2 Objekte eingegeben: eine Zeichenkette und die Variable x; sie werden durch ein Komma getrennt. Die Funktion cat() verarbeitet diese Eingaben wie folgt:

  1. Die Objekte werden (wenn nötig) in Zeichenketten umgewandelt. Die Variable x wird dabei in ihren Zahlenwert verwandelt.
  2. Die Zeichenketten werden aneinandergehängt (concatenation).
  3. Die Funktion cat() hat keinen Rückgabewert; die erzeugte Zeichenkette wird auf der Konsole ausgegeben.

Zeile 3: Zeigt die Ausgabe der Zeichenkette.

Die Arbeitsweise von paste0() unterscheidet sich nur im dritten Punkt: Die erzeugte Zeichenkette bildet den Rückgabewert und kann in einer Variable abgespeichert werden.

Man verwendet dies oft, wenn Diagramme beschriftet werden sollen, wobei feststehende Zeichenketten und die Werte von Variablen zusammengefügt werden.

In Abbildung 2 ist ein typisches Beispiel zu sehen: Es werden 5 Binomialverteilungen B(N, p, k) als Histogramme dargestellt, wobei die Anzahl der Versuche N und die Trefferwahrscheinlichkeit p in der Überschrift des Diagramms eingetragen werden sollen.

Das komplette Skript zum Erzeugen des Diagramms lautet:

N <- 20
Ns <- (0:N)
ps <- (1:5) / 6

par(mfrow = c(2, 3))
for(i in (1:5)){
  main <- paste0( "Binomialvert. mit N = ", N, ", p = ", format(ps[i], digits = 4) )
  probs <- dbinom(x = Ns, size = N, prob = ps[i])
  
  par(bg = "#FFFFFA")
  plot(x = Ns, y = probs,
       xlim = c(-1, max(Ns) + 1),
       ylim = c( -0.05, 0.25 ),
       col = "blue", type = "h",
       lwd = 5,
       xlab = "k", ylab = "B(N, p, k)",
       main = main, 
       frame.plot = TRUE)
  grid()
  abline(h = 0, col = "darkgray")
  abline(v = 0, col = "darkgray")
}

Durch die for-Schleife (Zeile 6 bis 22) werden 5 Plots zu den Trefferwahrscheinlichkeiten p = 1/6, 2/6, ..., 5/6 erzeugt (Zeile 3), die mit Hilfe von par(mfrow = c(2, 3)) (Zeile 5) in einem Gitter angeordnet werden.

Abbildung 2: Binomialverteilungen zu N=20 und 5 verschiedenen Trefferwahrscheinlichkeiten. Die Überschrift zu jedem der 5 Plots wird mit Hilfe von paste0() erzeugt.Abbildung 2: Binomialverteilungen zu N = 20 und 5 verschiedenen Trefferwahrscheinlichkeiten. Die Überschrift zu jedem der 5 Plots wird mit Hilfe von paste0() erzeugt.

Die entscheidende Anweisung, um das Diagramm zu beschriften, steht in Zeile 7:

main <- paste0( "Binomialvert. mit N = ", N, ", p = ", format(ps[i], digits = 4) )

Hier werden zwei Zeichenketten und die Werte zweier Variablen aneinandergehängt, wobei ps[i] auf 4 Nachkommastellen gekürzt wird. (Dafür sorgt der Einsatz von format(); ohne format() würde man 15 Nachkommastellen erhalten, die hier inhaltlich nicht gerechtfertigt sind.)

Die auf der rechten Seite von Zeile 7 erzeugte Zeichenkette ist der Rückgabewert von paste0(), der in der Variable main abgespeichert wird und dann innerhalb der Funktion plot() an das Argument main übergeben wird (Zeile 17).

Aufgabe: Schreiben Sie die Anweisung mit paste0() aus Zeile 7 so um, dass die Trefferwahrscheinlichkeiten als Brüche p = 1/6, 2/6, ..., 5/6 angegeben werden.

Das Argument collapse

Im letzten Abschnitt wurde der Fall beschrieben, in dem das Argument collapse = NULL nicht gesetzt wurde und an das Argument ... lediglich Vektoren der Länge 1 übergeben wurden. Wie paste0() arbeiten soll, wenn ein Trennungszeichen mit Hilfe des Argumentes collapse gesetzt wird und an ... echte Vektoren übergeben werden, wurde bereits in Abbildung 1 dargestellt. Dennoch sind im allgemeinen Fall einige Besonderheiten zu beachten, die in den folgenden Unterabschnitten besprochen werden.

Der default-Wert von collapse

Im Beispiel zum Beschriften eines Plots wurde das Argument collapse nicht gesetzt. Was geschieht, wenn an das Argument ... aber echte Vektoren (die Länge ist größer als 1) übergeben werden?

Das folgende Skript zeigt das Beispiel aus Abbildung 1, in dem aber das Argument collapse nicht gesetzt wird, also der default-Wert collapse = NULL verwendet wird:

paste0( (1:4), (4:1))
# "14" "23" "32" "41"

str(paste0( (1:4), (4:1)))
# chr [1:4] "14" "23" "32" "41"

Man erkennt, dass lediglich der erste Verarbeitungsschritt aus Abbildung 1 ausgeführt wird: Der Rückgabewert ist jetzt ein Vektor mit 4 Komponenten; der Speichermodus der Komponenten ist character.

Mit anderen Worten:

Man kann für collapse natürlich auch eine leere Zeichenkette einsetzen, also collapse = "" verwenden; das folgende Skript zeigt die Wirkung:

paste0( (1:4), (4:1), collapse = "")
# "14233241"

Jetzt wird auch der zweite Verarbeitungsschritt ausgeführt, der die Komponenten aus dem ersten Verarbeitungsschritt zu einer Komponente vereinigt; als Trennungszeichen wird aber die leere Zeichenkette collapse = "" verwendet.

Der allgemeine Fall

Im allgemeinen Fall der Anwendung von paste0() werden

Als Beispiel soll Abbildung 1 aufgegriffen und dann leicht abgewandelt werden:

paste0( (1:4), (4:1), collapse = " - ")
# "14 - 23 - 32 - 41"

paste0( (1:4), (4:1), rep(0, times = 4), collapse = " - ")
# "140 - 230 - 320 - 410"

paste0( (1:4), (4:1), 0, collapse = " - ")
# "140 - 230 - 320 - 410"

paste0( (1:4), (4:1), collapse = "\n")
#"14\n23\n32\n41"

cat(paste0( (1:4), (4:1), collapse = "\n"))
# 14
# 23
# 32
# 41

paste0( (1:4), (5:1), collapse = " - ")
# "15 - 24 - 33 - 42 - 11"

Zeile 1 und 2: Das Beispiel aus Abbildung 1.

Zeile 4 und 5: Das Beispiel zeigt wie das Reißverschlussprinzip mit mehr als zwei Vektoren arbeitet.

Zeile 7 und 8: Aufgrund des recycling-Mechanismus für Vektoren ist es nicht nötig den Vektor rep(0, times = 4) in Zeile 4 zu verwenden; schreibt man stattdessen nur 0 , wird in jeder Komponente die 0 verwendet.

Zeile 10 bis 17: Wie immer können in den Zeichenketten auch Zeichen wie \n verwendet werden. Die Ausgabe des erzeugten Vektors mit cat() zeigt die Wirkung.

Zeile 19 und 20: Wichtig ist auch zu wissen, wie der recycling-Mechanismus arbeitet, wenn die Vektoren unterschiedliche Länge besitzen. Der längere Vektor bestimmt, wie viele Komponenten im ersten Arbeitsschritt (Abbildung 1) erzeugt werden; der kürzere Vektor wird entsprechend wiederholt.

In den bisherigen Beispielen wurde gezeigt, wie das Reißverschlussprinzip arbeitet, wenn an das Argument ... zwei oder mehrere Vektoren übergeben werden; naheliegend ist die Frage, wie es bei nur einem Vektor arbeitet.

Das folgende Skript zeigt, dass der erste Arbeitsschritt aus Abbildung 1 mit eben nur einem Vektor ausgeführt wird – wobei aber nichts zusammengefügt werden muss, die Komponenten werden lediglich in Zeichenketten verwandelt. Der zweite Arbeitsschritt wird anschließend mit dem erzeugten Vektor von Zeichenketten ausgeführt:

paste0((1:4), collapse = " - ")
# "1 - 2 - 3 - 4"

Ein Missverständnis

Liest man eine Anweisung wie

paste0( (1:4), (4:1), collapse = " - ")

zum ersten Mal, könnte man auch folgende Interpretation für richtig halten:

Diese Interpretation ist natürlich falsch, aber mit dem Wissen über die Arbeitsweise von paste0() ist es leicht die Zeichenkette "1234 - 4321" zu erzeugen:

paste0( paste0((1:4), collapse = ""), " - ", paste0((4:1), collapse = "") )
# "1234 - 4321"

Aufgabe: Welche Zeichenkette erzeugt

paste0( paste0((1:4), collapse = ""), paste0((4:1), collapse = ""), collapse = " - " )

Welche Eingabewerte sind im Argument dot-dot-dot von paste0() erlaubt?

Bisher wurden an das Argument stets Vektoren (oder Vektoren der Länge 1) übergeben. Naheliegend ist die Frage, ob man auch andere zusammengesetzte Datentypen übergeben kann und wie paste0() dann arbeitet.

Um diese Frage zu beantworten, muss man den ersten Arbeitsschritt in Abbildung 1 nochmals in zwei Schritte unterteilen:

  1. Zuerst werden die eingegebenen Vektoren mit Hilfe der Funktion as.character() in Vektoren des Speichermodus character verwandelt.
  2. Erst dann greift das eigentliche Reißverschlussprinzip und die Vektoren werden zu einem character-Vektor zusammengesetzt.

Mit der Kenntnis des ersten Teilschrittes sind aber obige Fragen leicht zu beantworten: Man kann an das Argument ... jedes Objekt übergeben, das durch as.character() in einen character-Vektor verwandelt werden kann. Da die Wirkung von as.character() aber bei vielen zusammengesetzten Datentypen schwer zu kontrollieren ist oder zu ungewöhnlichen Ergebnissen führt, sollte man den Einsatz von paste0() tatsächlich auf die Verarbeitung von Vektoren beschränken.

Das folgende Skript zeigt zwei Beispiele für die Arbeitsweise von as.character() – es ist schwer sich Anwendungen vorzustellen, wo man diese Objekte mit paste0() weiter verarbeiten möchte:

# Matrizen:

m1 <- diag(1:4)
as.character(m1)
# "1" "0" "0" "0" "0" "2" "0" "0" "0" "0" "3" "0" "0" "0" "0" "4"

m2 <- diag(4:1)
as.character(m2)
# "4" "0" "0" "0" "0" "3" "0" "0" "0" "0" "2" "0" "0" "0" "0" "1"

paste0(m1, m2, collapse = " - ")
# "14 - 00 - 00 - 00 - 00 - 23 - 00 - 00 - 00 - 00 - 32 - 00 - 00 - 00 - 00 - 41"

# Listen:

l <- list(l1 <- (1:4), l2 <- (4:1))
str(l)
# List of 2
# $ : int [1:4] 1 2 3 4
# $ : int [1:4] 4 3 2 1
as.character(l)
# "1:4" "4:1"

paste0(l, l, collapse = " - ")
# "1:41:4 - 4:14:1"