Rutas
/
R
R
/
Ejercicios
/
Niveles de registro
Niveles de registro

Niveles de registro

Ejercicio de aprendizaje

Introducción

Antecedentes

Una cadena de caracteres 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 pueden admitir la mayoría de los idiomas del mundo.

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

Por desgracia, la terminología puede resultar confusa para quienes programan y conocen otros lenguajes.

Aunque el type sea character, R no distingue entre una sola letra (a la que en otros lenguajes se llama char, pero no en R) y un string largo. Ambos son un único elemento y pueden ir entre comillas simples ' ' o dobles " " indistintamente.

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, por tanto, un vector de strings, donde cada string es un ú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

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

Manipular un string concreto es, obviamente, posible, como con nchar() en el ejemplo anterior, pero requiere funciones específicas que veremos más adelante.

Funciones para strings

En el R base casi todo es posible, 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 para strings del estilo antiguo. Entre ellos se incluyen la mayoría de las soluciones de la comunidad en Exercism.

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

Por lo tanto, sigue mereciendo la pena conocer un poco los métodos antiguos.

nchar

Devuelve el número 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» se escapa del alcance de este documento. Basta con fijarse en un par de detalles de los ejemplos anteriores:

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

Combina un número arbitrario de strings en un único string.

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

paste0(v) es equivalente 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 preciso del ensamblado de strings, R copia la función sprintf de C (y de varios lenguajes posteriores: la Wikipedia enumera unos 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 iniciales o finales se pueden eliminar con trimws():

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

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

substr

Opera sobre un único string y devuelve una subcadena 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 predeterminado), 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 un concepto aparte. Si todavía no has llegado a esa parte del temario, por ahora basta con saber que la función unlist() convertirá (en este caso) una lista en un vector.

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

Tidyverse

Siendo benévolos, podríamos decir que las funciones para strings del estilo antiguo no son lo mejor que R tiene que ofrecer (opiniones menos benévolas también existen).

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

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

Algunas de sus funcionalidades clave son:

  • Todos los nombres de función llevan el prefijo str_ y tratan de dejar claro para qué sirven.
  • El orden de los argumentos tiende a ser coherente, con los datos de entrada como primer argumento.
  • Las entradas pueden ir desde un único string hasta estructuras de datos complejas. Las funciones intentarán adaptarse y hacer lo más sensato.

Como todos los paquetes de Tidyverse, stringr tiene buena documentación, normalmente escrita por sus autores.

Un inciso: una introducción rápida a los pipes

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

Por ahora, es útil saber lo siguiente:

  • Un resultado a la izquierda del pipe se convierte en el primer argumento de la función que está 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, el pipe debe apuntar a una llamada a función, no solo a un nombre, por lo que los paréntesis son siempre obligatorios (esto es distinto de lo que ocurre en otros lenguajes).

El ejemplo siguiente ilustra la sintaxis. Las funciones concretas se comentarán 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"
Gestionar longitudes

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

Otras funciones manipulan los espacios en blanco, como str_trim() y str_squish(). A menudo es una tarea de limpieza esencial al principio 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"
Extraer partes de strings

La función str_sub(string, start, end) devuelve la subcadena 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 lo que ocurre (y se agradece) con la indexación de vectores, 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 vector de caracteres en un único string. El argumento collapse se inserta entre cada elemento y su valor predeterminado es "".

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

str_c convierte varios vectores de caracteres en un único vector de caracteres, reciclando cuando es 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 aporta a R una interpolación de strings sencilla. Las expresiones que hay 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 habitual de R es dar forma a datos desordenados para dejarlos en un formato coherente que permita analizarlos. Para ayudarte con ello, hay varias formas de dividir texto en subcadenas. La mayoría quedan fuera de lo que tratamos aquí.

str_split_1 es la más sencilla: convierte un único string en un vector de fragmentos, dividiendo según un patrón que se especifica (no hay patrón predeterminado).

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

Algunos idiomas (principalmente europeos) distinguen entre letras MAYÚSCULAS y minúsculas. Hay un conjunto de funciones de stringr para convertir unas en otras, de forma adecuada a cada configuración regional:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : pone en mayúscula la primera letra de cada palabra
  • str_to_sentence() : pone en mayúscula la primera letra de la frase
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 sustituir subcadenas según la coincidencia de un patrón, str_replace() cambia la primera aparición del patrón y str_replace_all() cambia todas las apariciones.

Para sustituir subcadenas según el índice de los 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 este 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 que hagas algo con ella.

Puedes usar tanto las funciones heredadas de R base como la biblioteca stringr; ambas permiten llegar a soluciones breves 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"

Hay que eliminar los espacios en blanco tanto al principio como al final:

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 colocando primero 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 pestaña nueva
R Exercism

¿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.