Percursos
/
R
R
/
Exercícios
/
Níveis de log
Níveis de log

Níveis de log

Exercício de aprendizagem

Introdução

Contexto

Uma string é um pedaço de texto entre aspas. Qualquer versão de R atualmente suportada codifica as strings em UTF-8, pelo que é possível suportar a maioria das línguas do mundo.

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

Infelizmente, esta terminologia pode ser confusa para programadores familiarizados com outras linguagens.

Apesar de o type ser character, o R não faz distinção entre uma única letra (chamada char em várias outras linguagens, mas não em R) e uma string longa. Ambas são um único item e podem estar entre aspas simples ' ' ou duplas " ", indistintamente.

O guia de estilo recomenda usar aspas duplas, exceto quando queres usar aspas duplas dentro da string sem as escapar.

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

Um character vector é, portanto, um vetor de strings, em que cada string é um único 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

No exemplo acima, length(s[1]) é equivalente a length("some"): o número de elementos do vetor, não o comprimento da string.

Manipular uma string individual é obviamente possível, como acontece com nchar() no exemplo acima, mas exige funções específicas, que serão abordadas a seguir.

Funções de string

No R base, quase tudo é possível, mas muitos programadores consideram o suporte para manipulação de strings bastante limitado e confuso.

Felizmente, existem pacotes do tidyverse que são muito mais intuitivos, flexíveis e coerentes.

R base

Ainda existe muito código legado que usa funções de string ao estilo antigo. Isto inclui a maioria das soluções da comunidade no Exercism.

Além disso, em tarefas pequenas pode ser útil usar funções incorporadas em vez de importar uma biblioteca grande. Quem mantém o track de R no Exercism tende a seguir esta abordagem quando escreve soluções exemplares para validar os exercícios.

Por isso, ainda vale a pena conhecer algumas das abordagens antigas.

nchar

Devolve o número de carateres numa string.

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

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

O tema (complicado!) do que significa "caráter" está fora do âmbito desta introdução. Repara apenas em alguns pontos nos exemplos acima:

  • Embora sejam implementadas em C internamente, as strings em R não terminam com \0 (o que acrescentaria 1 ao comprimento).
  • Os carateres Unicode multibyte são tratados como seria de esperar. "Hrōðgār" tem 7 carateres (também chamados "runas" ou "pontos de código"), mas precisa de 10 bytes para ser armazenado.
paste e paste0

Combina um número arbitrário de strings numa única string.

Opcionalmente, pode indicar-se um separador para paste: o valor predefinido é um espaço.

paste0(v) é equivalente a paste(v, sep = "").

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

Para um controlo mais fino sobre a construção de strings, o R copia a função sprintf do C (e de várias linguagens posteriores: a Wikipedia lista cerca de 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

Os espaços em branco no início e/ou no fim podem ser removidos com trimws():

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

Existe um parâmetro which = "left" (ou "right") para não remover os espaços das duas extremidades.

substr

Atua sobre uma única string e devolve uma substring entre dois limites (inclusive).

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

Divide uma string numa lista de R, com base numa string separadora. O separador é obrigatório (não tem valor predefinido), mas pode ser uma string vazia.

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

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

As listas serão abordadas num Conceito separado. Se ainda não chegaste a essa parte do currículo, fica a saber, por agora, que a função unlist() vai (neste caso) converter uma lista num vetor.

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

Tidyverse

Sendo generosos, podemos dizer que as funções de string ao estilo antigo não são o melhor que o R tem para oferecer (há opiniões menos generosas).

Para melhorar esta situação, a biblioteca stringr está em desenvolvimento desde 2009 e é agora uma parte fundamental da coleção Tidyverse.

Importa-a para o teu namespace com library(stringr) ou library(tidyverse).

Algumas características principais:

  • Todos os nomes de funções têm o prefixo str_ e procuram deixar claro o seu propósito.
  • A ordem dos argumentos procura ser coerente, com os dados de entrada como primeiro argumento.
  • Os dados que recebem podem ir de uma única string a estruturas de dados complexas. As funções tentam adaptar-se e fazer o mais sensato.

Tal como todos os pacotes do Tidyverse, o stringr tem boa documentação, normalmente escrita por quem criou o pacote.

Um desvio: introdução rápida aos pipes

O uso de pipes |> para encadear funções será abordado com mais detalhe noutras partes do currículo.

Por agora, é útil saber que:

  • Um resultado à esquerda do pipe passa a ser o primeiro argumento da função à direita (em que "direita" ignora quebras de linha opcionais).
  • Quaisquer outros argumentos podem ser incluídos na chamada da função.
  • Em R, temos de encadear para uma chamada de função, não apenas para um nome, pelo que os parênteses são sempre obrigatórios (ao contrário do que acontece nalgumas outras linguagens).

O exemplo abaixo ilustra a sintaxe. As funções individuais serão abordadas mais adiante.

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"
Gerir comprimentos

str_length conta pontos de código (semelhante a nchars).

Várias outras funções manipulam espaços em branco, incluindo str_trim() e str_squish(). Isto é, muitas vezes, uma tarefa de limpeza essencial no início de qualquer projeto de ciência de dados.

# 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"
Subconjuntos de strings

A função str_sub(string, start, end) devolve a substring de start a end (inclusive), enquanto str_sub_all() pode atuar sobre várias strings.

Há muita flexibilidade. start e end assumem por predefinição o primeiro e o último caráter, respetivamente. Podem ser vetores de índices. Em contraste bem-vindo com a indexação de vetores, os valores negativos contam a partir do fim da string, ao estilo do 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"
Juntar strings

str_flatten converte um vetor de carateres numa única string. O argumento collapse é inserido entre cada elemento e assume "" por predefinição.

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

str_c converte vários vetores de carateres num único vetor de carateres, com reciclagem quando necessário.

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 traz a interpolação simples de strings ao R. As expressões dentro de chavetas { } são interpretadas e substitui-se a representação textual.

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
Dividir strings

Um uso muito comum do R é arrumar dados desorganizados num formato consistente para análise. Para ajudar nisto, há várias formas de dividir texto em substrings. A maioria está fora do âmbito atual.

str_split_1 é a mais simples: converte uma única string num vetor de partes, divididas por um padrão especificado (não há padrão predefinido).

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"
Transformar strings

Algumas linguagens (sobretudo europeias) distinguem entre letras MAIÚSCULAS e minúsculas. Existe um conjunto de funções do stringr para converter umas nas outras, de formas adequadas a cada localização:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : primeira letra de cada palavra em maiúscula
  • str_to_sentence() : primeira letra da frase em maiúscula
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"

Para substituir substrings com base na correspondência de padrões, str_replace() altera a primeira ocorrência do padrão e str_replace_all() altera todas as ocorrências.

Para substituir substrings com base na indexação de carateres, existe uma forma de atribuição de str_sub().

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

Instruções

Neste exercício vais processar linhas de log.

Cada linha de log é uma string com o seguinte formato: "[<LEVEL>]: <MESSAGE>".

Existem três níveis de log diferentes:

  • INFO
  • WARNING
  • ERROR

Tens três tarefas, cada uma recebe uma linha de log e pede-te para fazer algo com ela.

Podes usar à vontade tanto funções legacy do R base como a biblioteca stringr, e ambas permitem soluções curtas e viáveis.

1. Obter a mensagem de uma linha de log

Implementa a função message para devolver a mensagem de uma linha de log:

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

Qualquer espaço em branco no início ou no fim deve ser removido:

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

2. Obter o nível de log de uma linha de log

Implementa a função log_level para devolver o nível de log de uma linha de log, que deve ser devolvido em minúsculas:

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

3. Reformatar uma linha de log

Implementa a função reformat que reformata a linha de log, colocando a mensagem primeiro e o nível de log a seguir, entre parênteses:

reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Editar via GitHub A ligação abre numa nova janela ou separador
R Exercism

Estás pronto para começar Níveis de log?

Inscreve-te no Exercism para aprenderes e dominares R com 21 conceitos111 exercícios, e mentoria humana real, tudo grátis.