Tracks
/
R
R
/
Übungen
/
Log-Level
Log-Level

Log-Level

Lernübung

Einführung

Hintergrund

Ein String (Zeichenkette) ist ein Stück Text zwischen Anführungszeichen. Jede derzeit unterstützte Version von R kodiert Strings in UTF-8, sodass die meisten Sprachen der Welt unterstützt werden können.

mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"

Leider kann die Terminologie für Programmierer verwirrend sein, die andere Sprachen kennen.

Obwohl der type character ist, macht R keinen Unterschied zwischen einem einzelnen Buchstaben (in mehreren anderen Sprachen char genannt, in R aber nicht) und einem langen String. Beides ist ein einzelnes Element und kann austauschbar in einfache ' ' oder doppelte " " Anführungszeichen gesetzt werden.

Der Styleguide empfiehlt doppelte Anführungszeichen, außer wenn du doppelte Anführungszeichen innerhalb des Strings verwenden möchtest, ohne sie zu escapen.

str2 <- 'String with "quotes"'  # convenience format
str2
#> [1] "String with \"quotes\""    # standard format

Ein character vector ist also ein Vektor von Strings, wobei jeder String ein einzelnes Element ist.

s <- c("some", "strings")
s
#> [1] "some"    "strings"
length(s)
#> [1] 2
length(s[1])  # the number of vector elements, not the string length
#> [1] 1

nchar(s[1])  # the number of characters in the string "some"
#> [1] 4

Im obigen Beispiel ist length(s[1]) gleichbedeutend mit length("some"): die Anzahl der Vektorelemente, nicht die Stringlänge.

Einen einzelnen String zu manipulieren ist natürlich möglich, wie nchar() im obigen Beispiel zeigt, erfordert aber bestimmte Funktionen, die weiter unten besprochen werden.

String-Funktionen

In Base R ist fast alles möglich, aber viele Programmierer finden die Unterstützung für String-Manipulation ziemlich begrenzt und verwirrend.

Zum Glück gibt es die tidyverse-Pakete, die viel intuitiver, flexibler und in sich stimmiger sind.

Base R

Es gibt noch viel Legacy-Code, der String-Funktionen im alten Stil verwendet. Dazu gehören die meisten Community-Lösungen auf Exercism.

Außerdem kann es bei kleinen Aufgaben nützlich sein, eingebaute Funktionen zu verwenden, statt eine große Bibliothek zu importieren. Die Maintainer des R-Tracks wählen gerne diesen Ansatz, wenn sie Musterlösungen schreiben, um die Übungen zu validieren.

Daher lohnt es sich, ein wenig über die alten Ansätze zu wissen.

nchar

Gibt die Anzahl der Zeichen in einem String zurück.

nchar("string")
#> [1] 6

nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7

Das (komplizierte!) Thema, was „Zeichen“ eigentlich bedeutet, sprengt unseren Rahmen. Merke dir nur ein paar Punkte aus den obigen Beispielen:

  • Obwohl Strings in R intern in C implementiert sind, sind sie nicht \0-terminiert (was die Länge um 1 erhöhen würde).
  • Mehrbyte-Unicode-Zeichen werden so behandelt, wie du es erwartest oder erhoffst. „Hrōðgār“ hat 7 Zeichen (auch „Runen“ oder „Code Points“ genannt), braucht aber 10 Bytes zum Speichern.
paste und paste0

Fügt eine beliebige Anzahl von Strings zu einem einzigen String zusammen.

Optional kann für paste ein Trennzeichen angegeben werden: Standard ist ein Leerzeichen.

paste0(v) ist gleichbedeutend mit paste(v, sep = "").

paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintf

Für eine feinere Kontrolle beim Zusammenbauen von Strings übernimmt R die Funktion sprintf aus C (und aus mehreren späteren Sprachen: Wikipedia listet etwa 30 auf).

r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimws

Führende und/oder nachfolgende Leerzeichen lassen sich mit trimws() entfernen:

s <- "    messy string   "
trimws(s)
#> [1] "messy string"

Es gibt einen Parameter which = "left" (oder „right“), damit nicht beide Enden gekürzt werden.

substr

Wirkt auf einen einzelnen String und gibt einen Teilstring zwischen zwei (einschließlich) Grenzen zurück.

substr("Exercism", 5, 7)
#> [1] "cis"
strsplit

Teilt einen String anhand eines Trenn-Strings in eine R-Liste auf. Der Trenner ist erforderlich (es gibt keinen Standardwert), kann aber ein leerer String sein.

strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R"      "Python" "Julia" 

strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

Listen werden in einem eigenen Konzept behandelt. Wenn du diesen Teil des Lehrplans noch nicht erreicht hast, merke dir vorerst nur, dass die Funktion unlist() (in diesem Fall) eine Liste in einen Vektor umwandelt.

unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

Tidyverse

Wohlwollend formuliert könnte man sagen, dass die String-Funktionen im alten Stil nicht das Beste sind, was R zu bieten hat (weniger wohlwollende Ansichten gibt es auch).

Um die Lage zu verbessern, wird die Bibliothek stringr seit 2009 entwickelt und ist heute ein zentraler Teil der Tidyverse-Sammlung.

Importiere sie mit library(stringr) oder library(tidyverse) in deinen Namensraum.

Einige wichtige Merkmale:

  • Alle Funktionsnamen haben das Präfix str_ und versuchen, den Zweck klar zu machen.
  • Die Reihenfolge der Argumente ist möglichst einheitlich, wobei die Eingabedaten das erste Argument sind.
  • Die Eingaben reichen von einem einzelnen String bis zu komplexen Datenstrukturen. Die Funktionen versuchen, sich anzupassen und das Sinnvollste zu tun.

Wie alle Tidyverse-Pakete hat stringr eine gute Dokumentation, die meist von den Autor(inn)en des Pakets geschrieben wird.

Abschweifung: Eine kurze Einführung in Pipes

Die Verwendung von Pipes |> zum Verbinden von Funktionen wird an anderen Stellen des Lehrplans ausführlicher besprochen.

Für den Moment ist es nützlich zu wissen, dass:

  • Ein Ergebnis links von der Pipe zum ersten Argument der Funktion rechts davon wird (wobei „rechts“ optionale Zeilenumbrüche ignoriert).
  • Alle übrigen Argumente können im Funktionsaufruf angegeben werden.
  • Wir in R an einen Funktions_aufruf_ pipen müssen, nicht nur an einen Namen, daher sind runde Klammern immer erforderlich (das unterscheidet sich von einigen anderen Sprachen).

Das folgende Beispiel veranschaulicht die Syntax. Die einzelnen Funktionen werden später besprochen.

library(stringr)

"Monday-25-February" |> 
  str_split("-") |>     
  unlist() |>               # [1] "Monday"   "25"       "February"
  str_sub(1, 3) |>          # [1] "Mon" "25"  "Feb"
  str_to_upper() |>         # [1] "MON" "25"  "FEB"
  str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
Längen verwalten

str_length zählt Code Points (ähnlich wie nchars).

Verschiedene andere Funktionen bearbeiten Leerzeichen, darunter str_trim() und str_squish(). Das ist oft ein wichtiger Aufräumschritt am Anfang jedes Data-Science-Projekts.

# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R"))           #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right")  # add spaces if necessary
#> [1] "Julia   " "Python  " "R       "

str_trim("Julia    Python    R    ")   # remove leading & trailing whitespace
#> [1] "Julia    Python    R"
str_squish("Julia   Python  R    ")  # trim, collapse multiple spaces 
#> [1] "Julia Python R"
Teilstrings auswählen

Die Funktion str_sub(string, start, end) gibt den Teilstring von start bis end (einschließlich) zurück, während str_sub_all() auf mehrere Strings gleichzeitig wirken kann.

Es gibt viel Spielraum. start und end stehen standardmäßig für das erste bzw. letzte Zeichen. Sie können Vektoren von Indizes sein. Im wohltuenden Gegensatz zur Vektorindizierung zählen negative Werte wie in Python vom Ende des Strings rückwärts.

s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab"  "de"  "fgh"

s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"
Strings zusammenfügen

str_flatten wandelt einen Character-Vektor in einen einzelnen String um. Das Argument collapse wird zwischen jedes Element eingefügt und ist standardmäßig "".

c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"

str_c wandelt mehrere Character-Vektoren in einen einzelnen Character-Vektor um, wobei bei Bedarf recycelt wird.

str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"

str_glue bringt einfache String-Interpolation nach R. Ausdrücke in geschweiften Klammern { } werden ausgewertet und durch ihre Textdarstellung ersetzt.

r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631
Strings aufteilen

Eine sehr häufige Verwendung von R ist es, unordentliche Daten für die Analyse in ein einheitliches Format zu bringen. Um das zu erleichtern, gibt es mehrere Möglichkeiten, Text in Teilstrings aufzuteilen. Die meisten sprengen den aktuellen Rahmen.

str_split_1 ist die einfachste: Sie wandelt einen einzelnen String in einen Vektor von Teilen um, aufgeteilt an einem angegebenen Muster (es gibt kein Standardmuster).

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"
Strings verändern

Einige Sprachen (vor allem europäische) unterscheiden zwischen GROSS- und Kleinbuchstaben. Es gibt eine Reihe von stringr-Funktionen, um diese umzuwandeln, und zwar passend zur jeweiligen Locale:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : erster Buchstabe jedes Wortes groß
  • str_to_sentence() : erster Buchstabe des Satzes groß
str_to_upper("elixir")    # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά")  # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"

str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"

Für das Ersetzen von Teilstrings auf Basis von Musterabgleich ändert str_replace() das erste Vorkommen des Musters, str_replace_all() alle Vorkommen.

Für das Ersetzen von Teilstrings auf Basis von Zeichenindizes gibt es eine Zuweisungsform von str_sub().

 s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"

Anleitung

In dieser Übung verarbeitest du Logzeilen.

Jede Logzeile ist ein String mit folgendem Format: "[<LEVEL>]: <MESSAGE>".

Es gibt drei verschiedene Log-Level:

  • INFO
  • WARNING
  • ERROR

Du hast drei Aufgaben. Jede bekommt eine Logzeile, und du sollst etwas damit machen.

Du kannst entweder die alten Funktionen aus base R oder die Bibliothek stringr verwenden. Beide führen zu kurzen, brauchbaren Lösungen.

1. Nachricht aus einer Logzeile auslesen

Implementiere die Funktion message, die die Nachricht einer Logzeile zurückgibt:

message("[ERROR]: Invalid operation")
#> "Invalid operation"

Führender und nachfolgender Leerraum soll entfernt werden:

message("[WARNING]:  Disk almost full\r\n")
#> "Disk almost full"

2. Log-Level aus einer Logzeile auslesen

Implementiere die Funktion log_level, die das Log-Level einer Logzeile zurückgibt. Es soll in Kleinbuchstaben zurückgegeben werden:

log_level("[ERROR]: Invalid operation")
#> "error"

3. Eine Logzeile umformatieren

Implementiere die Funktion reformat, die die Logzeile umformatiert. Die Nachricht steht zuerst, danach folgt das Log-Level in Klammern:

reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
R Exercism

Bereit, mit Log-Level zu starten?

Melde dich bei Exercism an, um R mit 21 Konzepte111 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.