Tracks
/
R
R
/
Ejercicios
/
Niveles de registro
Niveles de registro

Niveles de registro

Ejercicio de aprendizaje

Introducción

Antecedentes

Un string (una cadena) es un fragmento de texto entre comillas. Cualquier versión de R con soporte en la actualidad codifica los strings en UTF-8, así que se puede dar soporte a la mayoría de los idiomas del mundo.

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

Lamentablemente, la terminología puede resultar confusa para quienes ya conocen otros lenguajes.

Aunque el type es character, R no distingue entre una sola letra (a la que en varios otros lenguajes se le llama char, pero no en R) y un string largo. Ambas son un solo elemento y pueden ir entre comillas simples ' ' o dobles " " de manera intercambiable.

La guía de estilo recomienda usar comillas dobles, salvo cuando quieras usar comillas dobles dentro del string sin escaparlas.

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

Un character vector es, entonces, un vector de strings, donde cada string es un solo 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

En el ejemplo anterior, length(s[1]) equivale a length("some"): la cantidad de elementos del vector, no la longitud del string.

Manipular un string individual es, por supuesto, posible, como con nchar() en el ejemplo anterior, pero requiere funciones concretas que veremos más abajo.

Funciones para strings

Casi todo es posible en R base, pero a muchas personas que programan les parece que el soporte para manipular strings es más bien limitado y confuso.

Por suerte, existen paquetes de tidyverse mucho más intuitivos, flexibles y coherentes entre sí.

R base

Todavía hay mucho código heredado que usa funciones de strings del estilo antiguo. Esto incluye la mayoría de las soluciones de la comunidad en Exercism.

Además, para tareas pequeñas puede resultar útil usar funciones integradas en lugar de importar una biblioteca grande. Quienes mantienen el track de R suelen adoptar este enfoque cuando escriben soluciones de ejemplo para validar los ejercicios.

Por lo tanto, todavía vale la pena aprender algo sobre los enfoques antiguos.

nchar

Devuelve la cantidad de caracteres de un string.

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

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

El tema (¡complicado!) de qué significa «carácter» queda fuera del alcance de esta introducción. Solo fíjate en un par de puntos de los ejemplos anteriores:

  • Aunque por dentro están implementados en C, los strings en R no terminan en \0 (lo que sumaría 1 a la longitud).
  • Los caracteres Unicode de varios bytes se manejan como esperarías o desearías. «Hrōðgār» tiene 7 caracteres (también llamados «runas» o «puntos de código»), pero necesita 10 bytes para guardarse.
paste y paste0

Combina una cantidad arbitraria de strings en un solo string.

De forma opcional, se puede indicar un separador para paste: el valor por defecto es un espacio.

paste0(v) equivale a paste(v, sep = "").

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

Para un control más fino del ensamblado de strings, R copia la función sprintf de C (y de varios lenguajes posteriores: Wikipedia enumera alrededor 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

Los espacios en blanco al principio o al final se pueden eliminar con trimws():

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

Existe un parámetro which = "left" (o "right") para evitar recortar ambos extremos.

substr

Opera sobre un solo string y devuelve un substring entre dos límites (ambos incluidos).

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

Divide un string en una lista de R, a partir de un string separador. El separador es obligatorio (no tiene valor por defecto), pero puede ser un string vacío.

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

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

Las listas se tratan en otro concepto. Si todavía no has llegado a esa parte del temario, por ahora basta con saber que la función unlist() (en este caso) convierte una lista en un vector.

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

Tidyverse

Siendo generosos, podríamos decir que las funciones de strings del estilo antiguo no son lo mejor que R tiene para ofrecer (hay opiniones menos generosas).

Para mejorar la situación, la biblioteca stringr está en desarrollo desde 2009 y hoy es una parte clave de la colección Tidyverse.

Impórtala en tu espacio de nombres con library(stringr) o library(tidyverse).

Algunas de sus características principales son:

  • Todos los nombres de funciones llevan el prefijo str_ e intentan dejar claro para qué sirven.
  • El orden de los argumentos intenta ser coherente, con los datos de entrada como primer argumento.
  • Las entradas pueden ir desde un solo string hasta estructuras de datos complejas. Las funciones intentan adaptarse y hacer lo más razonable.

Como todos los paquetes de Tidyverse, stringr tiene buena documentación, normalmente escrita por quienes crearon el paquete.

Digresión: una introducción rápida a los pipes

El uso de los pipes |> para conectar funciones se trata con más detalle en otras partes del temario.

Por ahora, es útil saber que:

  • Un resultado a la izquierda del pipe se convierte en el primer argumento de la función a la derecha (donde «derecha» ignora los saltos de línea opcionales).
  • Los argumentos restantes se pueden incluir en la llamada a la función.
  • En R, hay que hacer pipe hacia una llamada a función, no solo hacia un nombre, así que los paréntesis siempre son obligatorios (esto es distinto de lo que pasa en otros lenguajes).

El ejemplo de abajo ilustra la sintaxis. Las funciones individuales se tratan más adelante.

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"
Manejar longitudes

str_length cuenta puntos de código (similar a nchars).

Otras funciones manipulan los espacios en blanco, como str_trim() y str_squish(). A menudo esta es una tarea de limpieza esencial al inicio de cualquier proyecto de ciencia de datos.

# 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

La función str_sub(string, start, end) devuelve el substring desde start hasta end (ambos incluidos), mientras que str_sub_all() puede operar sobre varios strings.

Hay mucha flexibilidad. start y end toman por defecto el primer y el último carácter, respectivamente. Pueden ser vectores de índices. A diferencia de la indexación de vectores (algo que se agradece), los valores negativos cuentan desde el final del string, al estilo de 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"
Unir strings

str_flatten convierte un character vector en un solo string. El argumento collapse se inserta entre cada elemento y su valor por defecto es "".

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

str_c convierte varios character vectors en un solo character vector, con reciclaje cuando sea necesario.

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 trae a R una interpolación de strings sencilla. Las expresiones dentro de llaves { } se interpretan y se sustituye su representación como texto.

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

Un uso muy común de R es acomodar datos desordenados en un formato consistente para analizarlos. Para ayudar con esto, hay varias maneras de dividir texto en substrings. La mayoría quedan fuera del alcance de esta introducción.

str_split_1 es la más simple: convierte un solo string en un vector de partes, divididas según un patrón especificado (no hay patrón por defecto).

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

Algunos lenguajes (sobre todo europeos) distinguen entre letras MAYÚSCULAS y minúsculas. Hay un conjunto de funciones de stringr para convertir entre ambas, de manera adecuada según la configuración regional:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : la primera letra de cada palabra a mayúscula
  • str_to_sentence() : la primera letra de la oración a mayú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 reemplazar substrings según coincidencias de patrones, str_replace() cambia la primera aparición del patrón y str_replace_all() cambia todas las apariciones.

Para reemplazar substrings según el índice de caracteres, existe una forma de asignación de str_sub().

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

Instrucciones

En este ejercicio vas a procesar líneas de log.

Cada línea de log es un string con el siguiente formato: "[<LEVEL>]: <MESSAGE>".

Hay tres niveles de log diferentes:

  • INFO
  • WARNING
  • ERROR

Tienes tres tareas, y cada una recibe una línea de log y te pide hacer algo con ella.

Puedes usar las funciones heredadas de R base o la librería stringr, y ambas pueden darte soluciones cortas y viables.

1. Obtén el mensaje de una línea de log

Implementa la función message para que devuelva el mensaje de una línea de log:

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

Cualquier espacio en blanco al principio o al final debe eliminarse:

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

2. Obtén el nivel de log de una línea de log

Implementa la función log_level para que devuelva el nivel de log de una línea de log, que debe devolverse en minúsculas:

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

3. Reformatea una línea de log

Implementa la función reformat, que reformatea la línea de log: primero pone el mensaje y después el nivel de log entre paréntesis:

reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
R Exercism

¿Todo listo para empezar Niveles de registro?

Regístrate en Exercism para aprender y dominar R con 21 conceptos111 ejercicios y mentoría humana real, todo gratis.