Track
/
R
R
/
Esercizi
/
Livelli di log
Livelli di log

Livelli di log

Esercizio di apprendimento

Introduzione

Contesto

Una stringa è un pezzo di testo racchiuso tra virgolette. Qualsiasi versione di R attualmente supportata codifica le stringhe in UTF-8, quindi è possibile gestire la maggior parte delle lingue del mondo.

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

Purtroppo, la terminologia può risultare confusa per chi programma e ha familiarità con altri linguaggi.

Anche se il type è character, R non fa alcuna distinzione tra una singola lettera (chiamata char in diversi altri linguaggi, ma non in R) e una stringa lunga. Entrambe sono un singolo elemento e possono essere racchiuse tra virgolette singole ' ' o doppie " " in modo intercambiabile.

La guida di stile consiglia di usare le virgolette doppie, tranne quando vuoi usare le virgolette doppie all'interno della stringa senza fare escape.

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

Un character vector è quindi un vettore di stringhe, in cui ogni stringa è un singolo elemento.

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

Nell'esempio precedente, length(s[1]) equivale a length("some"): il numero di elementi del vettore, non la lunghezza della stringa.

Manipolare una singola stringa è ovviamente possibile, come con nchar() nell'esempio precedente, ma richiede funzioni specifiche che vedremo più avanti.

Funzioni per le stringhe

Quasi tutto è possibile con l'R di base, ma molti programmatori trovano il supporto per la manipolazione delle stringhe piuttosto limitato e confusionario.

Fortunatamente, esistono i pacchetti tidyverse, molto più intuitivi, flessibili e coerenti al loro interno.

R di base

Esiste ancora molto codice legacy che usa funzioni per le stringhe in vecchio stile. Questo include la maggior parte delle soluzioni della community su Exercism.

Inoltre, per compiti piccoli può essere utile usare le funzioni integrate invece di importare una grande libreria. I manutentori del track R tendono a seguire questo approccio quando scrivono soluzioni di esempio per validare gli esercizi.

Perciò vale comunque la pena conoscere un po' i vecchi approcci.

nchar

Restituisce il numero di caratteri in una stringa.

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

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

L'argomento (complicato!) di cosa significhi «carattere» esula dallo scopo di questa introduzione. Basti notare un paio di punti negli esempi precedenti:

  • Sebbene siano implementate internamente in C, le stringhe in R non hanno terminazione \0 (che aggiungerebbe 1 alla lunghezza).
  • I caratteri Unicode multi-byte sono gestiti come ti aspetteresti o spereresti. «Hrōðgār» ha 7 caratteri (chiamati anche «rune» o «code point»), ma occupa 10 byte per essere memorizzato.
paste e paste0

Combina un numero arbitrario di stringhe in un'unica stringa.

Facoltativamente, si può specificare un separatore per paste: il valore predefinito è uno spazio.

paste0(v) is equivalent to paste(v, sep = "").

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

Per un controllo più preciso sull'assemblaggio delle stringhe, R riprende la funzione sprintf dal C (e da diversi linguaggi successivi: Wikipedia ne elenca circa 30).

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

Gli spazi bianchi all'inizio e/o alla fine si possono rimuovere con trimws():

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

Esiste un parametro which = "left" (o "right") per evitare di rimuovere gli spazi da entrambe le estremità.

substr

Opera su una singola stringa e restituisce una sottostringa compresa tra due limiti (inclusi).

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

Divide una stringa in una lista di R, in base a una stringa separatore. Il separatore è obbligatorio (non ha un valore predefinito), ma può essere una stringa vuota.

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

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

Le liste saranno trattate in un Concept a parte. Se non hai ancora raggiunto quella parte del programma, per ora sappi solo che la funzione unlist() convertirà (in questo caso) una lista in un vettore.

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

Tidyverse

Per essere generosi, potremmo dire che le funzioni per le stringhe in vecchio stile non sono il meglio che R abbia da offrire (esistono opinioni meno generose).

Per migliorare la situazione, la libreria stringr è in sviluppo dal 2009 ed è ormai una parte fondamentale della raccolta Tidyverse.

Importala nel tuo namespace con library(stringr) o library(tidyverse).

Alcune funzionalità chiave includono:

  • Tutti i nomi delle funzioni hanno il prefisso str_ e cercano di rendere chiaro lo scopo.
  • L'ordine degli argomenti cerca di essere coerente con se stesso, con i dati di input come primo argomento.
  • Gli input possono variare da una singola stringa a strutture dati complesse. Le funzioni cercheranno di adattarsi e di fare la cosa più sensata.

Come tutti i pacchetti Tidyverse, stringr ha una buona documentazione, di solito scritta dall'autore o dagli autori del pacchetto.

Digressione: una breve introduzione ai pipe

L'uso dei pipe |> per collegare le funzioni sarà trattato più in dettaglio in altre parti del programma.

Per ora, è utile sapere che:

  • Un risultato a sinistra del pipe diventa il primo argomento della funzione a destra (dove «destra» ignora le eventuali interruzioni di riga).
  • Gli eventuali argomenti rimanenti possono essere inclusi nella chiamata alla funzione.
  • In R, si deve fare pipe verso una chiamata a funzione, non solo verso un nome, quindi le parentesi tonde sono sempre obbligatorie (a differenza di alcuni altri linguaggi).

L'esempio seguente illustra la sintassi. Le singole funzioni saranno trattate più avanti.

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"
Gestire le lunghezze

str_length conta i code point (simile a nchars).

Diverse altre funzioni manipolano gli spazi bianchi, tra cui str_trim() e str_squish(). Spesso è un'operazione di pulizia fondamentale all'inizio di qualsiasi progetto di data science.

# 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"
Sottoinsiemi di stringhe

La funzione str_sub(string, start, end) restituirà la sottostringa da start a end (inclusi), mentre str_sub_all() può operare su più stringhe.

C'è molta flessibilità. start e end hanno come valori predefiniti il primo e l'ultimo carattere, rispettivamente. Possono essere vettori di indici. In netto contrasto con l'indicizzazione dei vettori, i valori negativi contano a partire dalla fine della stringa, in stile Python.

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"
Unire le stringhe

str_flatten converte un vettore di caratteri in un'unica stringa. L'argomento collapse viene inserito tra un elemento e l'altro e ha come valore predefinito "".

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

str_c converte più vettori di caratteri in un unico vettore di caratteri, con il riciclo degli elementi quando necessario.

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 porta in R una semplice interpolazione di stringhe. Le espressioni all'interno delle parentesi graffe { } vengono interpretate e la loro rappresentazione testuale viene sostituita al loro posto.

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
Dividere le stringhe

Un uso molto comune di R è trasformare dati disordinati in un formato coerente per l'analisi. Per aiutare in questo, ci sono diversi modi per dividere il testo in sottostringhe. La maggior parte va oltre lo scopo di questa introduzione.

str_split_1 è la più semplice: converte una singola stringa in un vettore di parti, suddivise in base a un pattern specificato (non c'è un pattern predefinito).

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"
Modificare le stringhe

Alcuni linguaggi (per lo più europei) distinguono tra lettere MAIUSCOLE e minuscole. Esiste un insieme di funzioni di stringr per convertire le une nelle altre, in modi adatti alla locale:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : la prima lettera di ogni parola in maiuscolo
  • str_to_sentence() : la prima lettera della frase in maiuscolo
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"

Per la sostituzione di sottostringhe basata sulla corrispondenza di un pattern, str_replace() modifica la prima occorrenza del pattern, str_replace_all() le modifica tutte.

Per la sostituzione di sottostringhe basata sull'indicizzazione dei caratteri, esiste una forma di assegnazione di str_sub().

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

Istruzioni

In questo esercizio elaborerai delle righe di log.

Ogni riga di log è una stringa formattata così: "[<LEVEL>]: <MESSAGE>".

Esistono tre diversi livelli di log:

  • INFO
  • WARNING
  • ERROR

Hai tre attività, ognuna delle quali riceve una riga di log e ti chiede di fare qualcosa con essa.

Sei libero di usare sia le funzioni legacy di base R sia la libreria stringr: entrambe possono portare a soluzioni brevi e valide.

1. Ottenere il messaggio da una riga di log

Implementa la funzione message in modo che restituisca il messaggio di una riga di log:

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

Eventuali spazi bianchi all'inizio o alla fine vanno rimossi:

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

2. Ottenere il livello di log da una riga di log

Implementa la funzione log_level in modo che restituisca il livello di log di una riga di log, che va restituito in minuscolo:

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

3. Riformattare una riga di log

Implementa la funzione reformat che riformatta la riga di log, mettendo prima il messaggio e poi il livello di log tra parentesi tonde:

reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
R Exercism

Vuoi iniziare Livelli di log?

Iscriviti a Exercism per imparare e padroneggiare R con 21 concetti111 esercizi e il mentoring di persone reali, tutto gratis.