RegExp-Kurs: R (stringr)

Aus DebianforumWiki
Zur Navigation springen Zur Suche springen
Baustelle.png Baustelle: Dieser Artikel ist eine Baustelle. Das heißt, jemand hat sich dieses Artikels angenommen und überarbeitet ihn gerade.


Wiki ‹ Scripting ‹ RegExp-Kurs: R (stringr)


Wiki ‹ Softwareentwicklung und paketierung ‹ RegExp-Kurs: R (stringr)


Diskussion 184549

Übersicht: Reguläre Ausdrücke

Einleitung

In diesem Thread geht es um die Anwendung von regulären Ausdrücken in R mittels des Packages (der Bibliothek) stringr.

https://de.wikipedia.org/wiki/R_(Programmiersprache): “R ist eine freie Programmiersprache für statistische Berechnungen und Grafiken. Sie wurde 1992 von Statistikern für Anwender mit statistischen Aufgaben neu entwickelt. Die Syntax orientiert sich an der Programmiersprache S, mit der R weitgehend kompatibel ist, und die Semantik an Scheme. Als Standarddistribution wird R mit einem Interpreter als Kommandozeilenumgebung mit reduzierten grafischen Schaltflächen angeboten. So ist R aktuell auf den wichtigsten Plattformen verfügbar; die Umgebung wird von den Entwicklern ausdrücklich ebenfalls als R bezeichnet. R ist Teil des GNU-Projekts.”

Bei Stringr handelt es sich um einen benutzerfreundliche Oberfläche/Wrapper für die stringi-Bibliothek. Der verwendete Regex-Dialekt wird als “ICU” oder “Java-ähnlich” bezeichnet (https://cloud.r-project.org/web/packages/stringi/index.html). Die ERE-Ausdrücke, die wir während des Grundlagen-Kurses von Meillo gelernt haben, können (nach Anpassung der Escapezeichen und der vordefinierten Zeichenklasse; siehe weiter unten) in der Regel übernommen werden.

Installation

R und die für den Kursteil benötigten R-Packages sind in den offiziellen Debian-Quellen verfügbar. Benötigt werden r-recommended und r-cran-tidyverse.

Optional: Für diesen Kurs-Part reicht prinzipiell ein beliebiger Texteditor. Eine integrierte Entwicklungsumgebung macht die Benutzung von R aber bequemer und einfacher. Eine Übersicht findet sich im Archwiki (https://wiki.archlinux.org/title/R#Editors_IDEs_and_notebooks_with_R_support) oder auf Wikipedia https://en.wikipedia.org/wiki/R_(programming_language)#Interfaces.

Hilfreicheiches Vorwissen

Zuweisungsoperator

Im Unterschied zu Python, gibt es in R zwei Möglichkeiten eine Zuweisung auszudrücken. “<-” und “=.” Für die Zuweisung eines Wertes an ein Objekt sind beide Schreibweisen möglich. Für die Übergabe eines Schlüsselwortarguments an eine Funktionen oder eine Methode ist hingegen (wie in Python) ausschließlich die Schreibweise “=” erlaubt.

data(trees)
# Zuweisung an Objekte: Sowohl "<-" als auch "=" sind erlaubt.
volumen_kubikzoll = trees$Volume
volumen_kubikzoll <- trees$Volume 
# Übergabe eines Schlüsselwortarguments an eine Funktion: Ausschließlich "=" ist erlaubt.
summary(object = volumen_kubikzoll, digit = 4)

Pipe-Operator

Im weiteren Verlauf dieser Einführung wird häufig der Pipe-Operator %>% verwendet. Der Pipe-Operator ermöglicht es zwei Funktionen miteinander zu verknüpfen. Das heißt die zweite Funktion verwendet den Rückgabewert der ersten Funktion als (ootb: erstes positionales) Argument. Der Pipe-Operator stellt damit eine IMHO flexiblere und übersichtlichere Alternative zu Klammern da. Um den Pipe-Operator verwenden zu können, ist es allerdings erforderlich, zuvor entweder das Package magrittr oder Meta-Package tidyverse zu laden.

library(magrittr)
data(mtcars) # Beispieldatensatz (ist in R enthalten) laden

# Folgendes wird gemacht: Die Antriebsleistung der Autos wird von Pferdestärken (Pferdestärken) in Kilowatt (kW) umgerechnet. Anschließen werden das (als Zahl vorliegende) Ergebnis in eine Zeichenkette umwandelt. Zum Schluss wird die Angabe der Einheit ("kW") zu dieser  Zeichenkette hinzufügt.

## Variante 1: Mit Klammern (ohne Pipe-Operator) --> IMHO eher unübersichtlich
paste((as.character(multiply_by(mtcars$hp, 0.746))), "kW", sep = " ")
## Variante 2: Mit Pipe-Operator (in einer Zeile) --> IMHO schon übersichtlicher
mtcars$hp %>% multiply_by(0.746) %>% as.character %>% paste("kW", sep = " ")
## Variante 3: Mit Pipe-Operator (in mehren Zeilen --> IMHO noch mal  übersichtlicher
mtcars$hp %>%
  multiply_by(0.746) %>%
  as.character() %>%
  paste("kW", sep = " ")

Weitergehende Infos zum Pipe-Operator (für die das Verstädnis/Bearbeitung dieses Kurs-Parts nicht notwendig): https://magrittr.tidyverse.org

Escaping

Als Escapezeichen dient der Backslash. Eine Besonderheit innerhalb R ist jedoch, dass grundsätzlich doppelt escapet werden muss, um aus einen Standardzeichen ein Metazeichen zu machen. De facto dient also ein doppelter Backslash als Escape-Zeichen.

library(tidyverse)
# TODO: Es soll nur die Schreibweise mit zwei getrennten Wörter ("hallo debianforum") gefunden werden.
## richtig:
"hallo_debianforum hallo debianforum" %>%
  str_extract_all(pattern = "hallo\\b.debianforum")
## falsch:
"hallo_debianforum hallo debianforum" %>% 
  str_extract_all(pattern = "hallo\b.debianforum")

Weitergehende Infos: https://raw.githubusercontent.com/rstudio/cheatsheets/main/strings.pdf (Seite 2, linke Spalte “Need to Know”).

Vorbereitung

Arbeitsverzeichnis festlegen, Script-Datei erstellen und Schwäbische Kunde downloaden

Erstellt ein Verzeichnis (z. B. ~/regex_R) für diesen Kurs-Part und speichert dort eine leere Textdatei mit der Dateinamensendung “.R” (z. B. skript_regex.R) sowie die Schwäbische_Kunde (https://nopaste.debianforum.de/41651). Öffnet nun das R-Skript und fügt folgenden Befehl ein, um das Arbeitsverzeichnis festzulegen. {r eval=FALSE} #!/usr/bin/env Rscript #optional setwd(~/regex_R) # Arbeitsverzeichnis festlegen (Pfad ggf. anpassen) getwd() # Arbeitsverzeichnis anzeigen print("Das ist ein Test") # Zeichenkette "Das ist ein Test" ausgeben

Skript ausführen (Ohne Entwicklungsumgebung)

(Wenn ihr eine Entwicklungsumgebung zum Ausführen des Befehle bzw. eures Skripts nutzt, könnt ihr diesen Abschnitt überspringen.)
Öffnet ein Terminal, wechselt in eurer Arbeitsverzeichnis und übergebt das Script an das Programm Rscript. Daraufhin wird das Skript ausgeführt. ```{bash eval=FALSE} cd ~/regex_R # Pfad zum Arbeitsverzeichnis ggf. anpassen Rscript skript_regex.R # Pfad ggf. anpassen

## Schwäbische Kunde importieren und für spätere Verwendung abspeichern.
Zum importieren der Schwäbischen Kunde benutzen wir die Funktion read_lines aus dem Package readr. Das Ergebnis des Imports speichern wir unter dem Dateinamen "schwaebische_kunde.RData" ab, um es später wiederverwenden zu können.
```{r}
# Importieren
schwaebische_kunde <- readr::read_lines("schwaebische-kunde.txt") %>%
  dplyr::as_tibble() %>%
  dplyr::rename(text = value) %>%
  tibble::rowid_to_column("zeile")
# abspeichern
save(schwaebische_kunde, file = "schwaebische_kunde.RData")


Übersicht über die in stringr möglichen regulären Ausdrucke

Ein tabellarische Übersicht der innerhalb von stringr zur Verfügung stehenden Operationen und Zeichenklassen bietet die zweite Seite des Cheat-Sheets “strings” von Rstudio: https://github.com/rstudio/cheatsheets/blob/main/strings.pdf. Eine Übersicht als Fließtext liefert die die Vignette “regular expressions”: https://cloud.r-project.org/web/packages/stringr/vignettes/regular-expressions.html. Ein auffälliger Unterschied zu egrep (ERE) besteht darin, dass auch vordefinierte Zeichenklassen nur in einfache (statt doppelte) eckige Klammern gesetzt werden.

library(tidyverse)
# Nur Elemente der vordefinierten Zeichenklasse "[:digit:]" (Zahlen) extrahieren
"Die folgende Zahl lautet 9 (in Worten: Neun)." %>%
  str_extract_all(pattern = "[:digit:]")

Funktionen im stringr-Package

Ein Überblick über die in stringr enthaltenen regex-Funktionen ist zum Beispiel in der Vignette “Introduction to stringr” zu finden: https://stringr.tidyverse.org/articles/stringr.html#pattern-matching zu finden. Eine ausführliche Beschreibung findet sich in der Befehlreferenz des stringr-Packages: https://cloud.r-project.org/web/packages/stringr/stringr.pdf.

Feststellen, ob ein Match vorliegt: str_detect

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen:
## Zeile soll ein Komma enthalten.
schwaebische_kunde$is_match <- schwaebische_kunde$text %>%
  str_detect(pattern = ",")
# Ergebnis ausgeben
schwaebische_kunde %>%
  print(n = Inf)

Feststellen eines Matches am Beginn bzw. Ende einer Zeichenfolge: str_starts und str_ends

Soll nur der Beginn bzw. Ende einer Zeichenfolge betrachtet werden, so bietet sich die Nutzung der Funktionen str_starts bzw. str_ends an. Die Angabe der Zeilenanker ^ und $ kann bei der Verwendung dieser Funktionen entfallen.

input_string <- "debianforum.de"

# Match am Anfang einer Zeichenfolge feststellen
input_string %>% str_starts(pattern = "[dD]") # entspricht ...
input_string %>% str_detect(pattern = "^[dD]")

# Match am Ende einer Zeichenfolge feststellen
input_string %>% str_ends(pattern = "de") # entspricht ...
input_string %>% str_detect(pattern = "de$")

Anzahl der Matches bestimmen: str_count

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## Vorkommen des Buchstabens e bzw. E in der jeweiligen Zeichenfolge (hier: Strophe der Schwäbischen Kunde)
schwaebische_kunde$n_matches <- schwaebische_kunde$text %>%
  str_count(pattern = "[eE]")
# Ergebnis anzeigen
schwaebische_kunde %>%
  print(n = Inf)

Position des ersten Matches bestimmen: str_locate

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## erstes auftreten des Buchstaben A bzw. a
schwaebische_kunde$position <-  schwaebische_kunde$text %>%
  str_locate(pattern = "[aA]")
# Ergebnis anzeigen
schwaebische_kunde %>%
  print (n = Inf)

Position aller matches bestimmen: str_locate_all

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## jedes auftreten von A bzw. a
str_locate_all(string = schwaebische_kunde$text,
               pattern = "[Aa]")

erstes Match extrahieren: str_extract

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## D
schwaebische_kunde$first_match <- schwaebische_kunde$text %>%
  str_extract(pattern = "[aA]")
# Ergebnis anzeigen
schwaebische_kunde %>%
  print (n = Inf)

alle Matches extrahieren: str_extract_all

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## jedes auftreten von A bzw. a
schwaebische_kunde$text %>%
  str_extract_all(pattern = "[aA]")

Gruppen extrahieren (nur erstes match): str_match

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## Extrahiere das Wort vor dem ersten Komma oder Punkt
schwaebische_kunde$text %>%
  str_match(pattern = "([:alnum:]*)[:blank:]*[.,].*")

Gruppen extrahieren (alle matches): str_match_all

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen
## Extrahiere alle Kleinbuchstaben, die auf einen Großbuchstaben folgen
schwaebische_kunde$text %>%
  str_match_all("[:upper:]([:lower:])")

(Komplette) Zeichenkette zurückgeben, wenn mindestens ein Match enthalten ist: str_subset

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen:
## Zeichenfolgen (Zeilen), die ein Komma enthalten
schwaebische_kunde$text %>% str_subset(pattern = ",")

Ersetzen (nur erstes Match ersetzen): str_replace

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen:
## Ersetze das erste Auftreten von "ae" durch "ä" 
schwaebische_kunde$text <- schwaebische_kunde$text %>%
  str_replace(pattern = "ae",
              replacement = "ä")
# Ergebnis ausgeben
schwaebische_kunde %>% 
  print(n = Inf)

Ersetzten (alle Matches ersetzen): str_replace_all

# Tidyverse laden
library(tidyverse)
# Schwäbische Kunde laden
load("schwaebische_kunde.RData")
# Regex-Funktion ausführen:
## Ersetze alle Leerzeichen durch Unterstriche
schwaebische_kunde$text <- schwaebische_kunde$text %>%
  str_replace_all(pattern = "[:space:]",
                  replacement = "_")
# Ergebnis anzeigen
schwaebische_kunde %>%
  print(n = Inf)

Aufgaben

  1. Bestimme, wie viele Buchstaben die URL des dfde-Unterforums “Softwareentwicklung und -paketierung, Scripting” (https://debianforum.de/forum/viewforum.php?f=34) enthält. Zähle einmal mit und einmal ohne die Angabe des Protokolls (“https”).
  2. Bestimme, wie viele Verse (Zeilen) der Schwäbischen Kunde mit einem Vokal (Selbstlaut) beginnen.
  3. Mehrere Einwohner von Neu New York wurden nach ihrer Lieblingsfarbe gefragt. Das Ergebnis findest du im NoPaste: https://nopaste.debianforum.de/41775. Ordne ihren Antworten mit Hilfe regulärer Ausdrücke den folgenden Kategorien zu “Rot,” “Blau,” “Grün,” “Andere Farbe” und NA (fehlender Wert).
  4. Extrahiere aus der Schwäbischen Kunde alle Großbuchstaben. Formatiere die Ausgabe so, dass getrennt nach Versen (Zeilen) folgende Informationen möglichst übersichtlich angezeigt werden: zeilennummer des Verses, gesamter Text des Verses sowie alle Großbuchstaben, die in diesem Vers enthalten sind. Du bist frei in der Wahl des Ausgabemediums (z. B. R-Konsole, Standardausgabe, Spreadsheet-Datei oder ähnliches).
  5. Denke Dir selbst eine Aufgabe für die anderen Teilnehmer dieses Kursteils aus.
  6. Löse die Aufgaben der anderen Teilnehmer.

Bitte postet Eure Lösungen zu den Aufgaben 1-5 frühestens ab Mittwoch (13. Juli). Bitte denkt als Aufgabenersteller bei Aufgabe 6 daran, anzugeben, ab wann Lösungen zu Eurer Aufgabe frühestens gepostet werden sollen :).