Wie extrahiere ich Informationen aus Zeichenketten in R? Heute zeige ich dir, wie du mit Zeichenketten arbeitest (auch „string“ genannt, oder „character“ in R). Dazu gehört, wie du Substrings ausliest, Strings auf bestimmte Muster prüfst, oder verschiedene Texte zusammenfügst.
Einführung
Zeichenketten bzw. Strings in R sind vom Typ character und werden mit Anführungszeichen gebildet: s <- "This is a string!"
. Die Strings in einem character vector können unterschiedlich lang sein und sind für den typischen Gebrauch nicht eingeschränkt, was die Länge angeht. In diesem Post bin ich bereits auf die Grundlagen eingegangen. Jetzt möchte ich auf fortgeschrittene Themen eingehen, die sehr nützlich sind, wenn man zum Beispiel Datensätze mit Texten hat.
String Parsing
Unter String Parsing versteht man den Vorgang, einen String vom Ausgangsformat in eine bestimmte Struktur zu bringen. Dazu wird der Input-String beispielsweise zerlegt, um dann die relevanten Informationen zu extrahieren. Oder der String wird in eine Liste umgewandelt.
Beispiel für String Parsing in R mit substring
Ein einfaches Beispiel ist eine Funktion, die aus einem Datums-String Tag, Monat und Jahr extrahieren soll und diese als Liste zurückgibt:
# Parses date string of format 'd.m.Y' and returns listparse_date <- function(s) { day <- as.integer(substring(s, 1, 2)) month <- as.integer(substring(s, 4, 5)) year <- as.integer(substring(s, 7, 10)) result <- list(Day=day, Month=month, Year=year) return(result)}
Für diese Funktion muss der Input-String s ein bestimmtes Format haben, z.B. „21.03.2022“. Mit Hilfe der substring-Funktion werden Tag, Monat und Jahr extrahiert, indem einzelne Substrings gebildet werden. Die Funktion erwartet einen String als erstes Argument, anschließend die Start- und Endposition. Es wird also der Substring extrahiert, der von der Startposition bis zur Endposition geht. In unserem Fall sollte das der Tag, Monat, bzw. das Jahr sein. Mit as.integer wandeln wir den String zu einer Ganzzahl um. Übrigens, die parse_date-Funktion dient nur zur Veranschaulichung. Es gibt bereits gute Packages für das Handling von Datums-Strings (siehe diesen Post).
Beispiel für String Parsing in R mit strsplit
Um die Vielfalt der Möglichkeiten zu demonstrieren, zeige ich hier nochmal eine weitere Variante, um ein Datum zu parsen:
# Parses date string of format 'd.m.Y' and returns listparse_date <- function(s) { dmy <- as.integer(strsplit(s, ".", fixed=TRUE)[[1]]) result <- list(Day=dmy[1], Month=dmy[2], Year=dmy[3]) return(result)}
Mit strsplit können wir einen String in Einzelteile zerlegen, basierend auf spezifischen Zeichen, in diesem Fall ein Punkt. Das angegebene Zeichen wird dabei nicht mit in die Substrings genommen, sodass wir einfach mit as.integer den Vektor zu einem int-Vektor machen können. Beachte, dass strsplit eine Liste zurückgibt, da s ja ein Vektor mit mehreren Strings sein kann. Jedes Listen-Element beinhaltet dann die „gesplitteten Substrings“. Das „fixed=TRUE“ bedeutet, dass der Punkt keine Regular Expression ist, sondern wirklich ein Punkt. Was das wiederum bedeutet, liest du im kommenden Abschnitt.
Regular Expressions in R
Reguläre Ausdrücke, im Englischen Regular Expressions oder häufig kurz regex genannt, sind Zeichenketten und jedes Zeichen steht für eine bestimmte syntaktische Regel. Dieses Prinzip kennen wir sehr wahrscheinlich auch, ohne von Regex gehört zu haben: so verstehen wir ein Ausrufezeichen häufig als Negierung, oder ein Sternchen als Platzhalter für beliebige Zeichen (etwa, wenn wir Textdateien mit *.txt
suchen). Regex hat viele Facetten und kann sehr komplex werden und deshalb werde ich hier nur ein kleines Beispiel zeigen:
# Checks if string contains only digits or lettersis_alphanumeric <- function(v) { return(grepl("^[a-zA-Z0-9]+$", v))}# Test the functiontestVec <- c("hallo", "arc42", "hallo?", "strings in r", "!?%)§$=!", "", "g00d")is_alphanumeric(testVec)
Die Funktion is_alphanumeric prüft, welche Elemente in v für den regulären Ausdruck ^[a-zA-Z0-9]+$ valide sind (= ob es ein „pattern match“ gibt). grepl, kurz für „global regular expression print logical“, ist eine von verschiedenen Regex-Funktionen und bekommt hier das Regex-Pattern sowie den character vector als Argumente übergeben. Das Pattern setzt sich aus verschiedenen Zeichen zusammen:
- ^: Dieses Zeichen markiert den Start der Zeichenkette, wenn es am Anfang steht. Die kommende Regel bezieht sich als auch auf das erste Zeichen
- $: Das hinterste Zeichen ist das Equivalent für das Ende der Zeichenkette, wenn es am Ende steht. Alles zwischen ^ und $ muss also den Regeln folgen, die durch die weiteren Zeichen definiert sind.
- []: Die eckigen Klammern definieren eine „matching list expression“, ein Block, der selbst wieder aus Zeichen bestehen kann (siehe nächsten Punkt).
- a-zA-Z0-9: Die zulässigen Zeichen sind alle Kleinbuchstaben, alle Großbuchstaben und alle Ziffern. Der Block [a-zA-Z0-9] besagt also „ein Buchstabe oder eine Ziffer“
- +: Das Plus hinter dem Block bedeutet, dass dieser mindestens 1 Mal vorkommen muss, oder häufiger. Somit gibt es für den gesamten Regex ein Match, wenn es ein Buchstabe oder eine Ziffer ist, oder eben eine Zeichenkette mit mehreren Buchstaben und Ziffern. Nicht aber, wenn es ein leerer String ist.
Vereinfacht gesagt bedeutet das benutzte Regex also „von Anfang bis Ende mindestens 1 oder mehr Zeichen, die entweder Buchstaben oder Ziffern sind“.
Regex: Finde welche Elemente ein Match ergeben
Sehr nah dran an der vorigen Funktion ist grep, also das selbe Prinzip, nur dass die Funktion die Positionen im Vektor zurückgibt, die ein Match ergeben. Benutze am besten den Code von oben und ersetze das grepl, um es einmal zu testen.
Regex: Ersetze Elemente, die ein Match ergeben
Vielleicht möchten wir in einem String Zeichen ersetzen, die einer bestimmten Regel folgen. Nicht immer kommt man in so einem Fall mit simplem „Suchen und Ersetzen“ weiter; hier zeigt sich, wie mächtig Regex sein kann. Ein Beispiel:
# Replaces special charactersreplace_special_chars <- function(s) { return(gsub("[^a-zA-Z0-9]", "", s))}# Test the functiontestVec <- c("hallo", "arc42", "hallo?", "strings in r", "!?%)§$=!", "", "g00d")replace_special_chars(testVec)
Mit dieser kurzen Funktion können wir jegliche Sonderzeichen entfernen, indem wir alles ersetzen, was mit dem Pattern übereinstimmt. In diesem Fall geben wir an, dass alles, was nicht alphanumerisch ist, mit „nichts“ (einem leeren String) ersetzt werden soll. Im Regex-Pattern ist hier das „^“ eine Negierung. Es hat eine andere Bedeutung als im vorigen Beispiel, da es nicht am Anfang des Patterns steht. Übrigens: möchtest du z.B. gsub verwenden ohne dass reguläre Ausdrücke verwendet werden, dann benutze das Argument fixed=TRUE.
Zusammenfassung
Du kennst nun die wichtigsten Funktionen, um mit Strings umzugehen. Hier nochmal eine Zusammenfassung:
- substring: Extrahiert einen Sub-String aus einem String mit von/bis-Angabe
- strsplit: Zerlegt einen String immer dort, wo eine bestimmte Zeichenfolge ist
- grepl: Gibt an, ob es einen Match für das angegebene Pattern gibt (logical vector)
- grep: Gibt an, für welche Elemente eines Vektors es einen Match für das angegebene Pattern gibt
- gsub: Ersetzt Zeichen in einem String, die ein Match für das angegebene Pattern haben
Es gibt noch weitere Regex-Funktionen, aber mit diesen solltest du schon sehr weit kommen können!
Soweit für heute! Hinterlasse gerne einen Kommentar, wenn es dir gefallen hat oder du dir etwas anderes wünschst. Würde dich vielleicht ein bestimmtes Projekt oder System interessieren, oder hast du ein spezifisches Problem, das du lösen musst? Dann sag gern Bescheid!
Beste Grüße