library(stringr)
library(htmlwidgets)

Creando cadenas de texto

string1 <- "Esta es una cadena de caracteres"
string2 <- 'Si quiero incluir "comillas" dentro de la cadena, uso comillas simples'

Cuando se nos olvida cerrar comillas quedamos atrapados en la consola, el símbolo cambia de > a +

"Esta es una cadena de caracteres sin comillas de cierre
+
+
+PRESIONA ESCAPE PARA SALIR"

Para incluir comillas sencillas o dobles en una cadena de texto debes escaparlas

comilla_doble <- "\"" # o '"'
comilla_simple <- '\'' # o "'"

Ten cuidado con el hecho de que la representación impresa de una cadena no es equivalente a la cadena misma, ya que la representación muestra las barras utilizadas para “escapar” caracteres,

x <- c('\'', '\\')
x
## [1] "'"  "\\"

Para imprimir la cadena en sí misma utiliza la siguiente función:

writeLines(x)
## '
## \



Manipulando cadenas de texto

Ver la longitud de un string

str_length(c("hola","como estas", "buenas tardes"))
## [1]  4 10 13

Concatenar strings

str_c("a","b","c", sep = ", ")
## [1] "a, b, c"

Puedes utilizar un vector para agregar el mismo prefijo o sufijo a varias cadenas

str_c("prefix-", c("a","b","c"), "-suffix")
## [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"

La concatenación de cadenas se va a saltar los valores perdidos (NA)

x <- c("abc", NA)
str_c("hola", x, "adios", sep = " ")
## [1] "hola abc adios" NA

Observa el comportamiento cuando concatenas vectores de la misma longitud

x <- c("abc", "de")
str_c(c(1,2), x, c(3,4), sep = " ")
## [1] "1 abc 3" "2 de 4"

La concatenación puede depender de condicionales

name <- "Alejandro Magno"
momento_del_dia <- "tarde"
birthday <- FALSE

str_c(
  "Buena ", momento_del_dia," ", name,
  if(birthday) " y FELIZ CUMPLEA?OS",
  "."
)
## [1] "Buena tarde Alejandro Magno."

Colapsando vectores

Si queremos colapsar un vector de strings en un solo string, usaremos la función collapse():

str_c(c("a", "b", "c"), collapse = ",")
## [1] "a,b,c"

Extraer subconjuntos de un string

x <- c("Manzanas", "Peras", "Limones", "Platanos")

Tomando las posiciones a partir del inicio

str_sub(x, 1,3)
## [1] "Man" "Per" "Lim" "Pla"

Tomando las posiciones a partir del final

str_sub(x, -3, -1)
## [1] "nas" "ras" "nes" "nos"

Transformando un string

Cambiando a minúscula la primer letra

str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))

Cambiando a mayúsculas el string completo

str_to_upper(x)
## [1] "MANZANAS" "PERAS"    "LIMONES"  "PLATANOS"

Cambiando el string completo a formato de título

str_to_title(x)
## [1] "Manzanas" "Peras"    "Limones"  "Platanos"

Ordenando un string

Para la función de ordenamiento importa el idioma

str_sort(x, locale = "es")
## [1] "limones"  "manzanas" "peras"    "platanos"

En hawaiano las vocales van primero

str_sort(x, locale = "haw") # Hawaiano
## [1] "limones"  "manzanas" "peras"    "platanos"



Expresiones regulares

Buscando patrones

Una expresión regular nos permite buscar un patrón en una cadena de texto.

La función str_view nos ayuda a visualizar las ocurrencias del patrón

x <- c("manzana", "banana", "pera", "pomelo", "mango")
str_view(x,"an") # buscando el patrón an
## [1] │ m<an>z<an>a
## [2] │ b<an><an>a
## [5] │ m<an>go

Existen caracteres con significado especial. Por ejemplo, el punto significa cualquier caracter. La siguiente expresión regular busca una o entre cualesquiera dos caracteres excluyendo el inicio de la palabra y el final de la palabra

str_view(x, ".o.")
## [4] │ <pom>elo

Entonces,¿cómo busco un punto? Para esto debo “escapar” el caracter especial para que pueda ser utilizado su significado literal. Para escapar un caracter uso dos diagonales invertidas (\)

dot <- "\\."
str_view(c("abc", "a.c", "bc"), dot)
## [2] │ a<.>c

Otros caracteres con significado especial:

El siguiente patrón busca todo lo que empiece con p

str_view(x, "^p")
## [3] │ <p>era
## [4] │ <p>omelo

Ahora busquemos todo lo que termine en a

(str_view(x, "a$"))
## [1] │ manzan<a>
## [2] │ banan<a>
## [3] │ per<a>

Existen otros símbolos que nos ayudan a hacer búsquedas:

x <- "MDCCCLXXXVIII"
str_view(x, "CC+") # Una C seguida de una o más C
## [1] │ MD<CCC>LXXXVIII
str_view(x, "C[LX]+") # Una C seguida de una o más L o X  
## [1] │ MDCC<CLXXX>VIII
str_view(x, "C{2}") # Exactamente dos C
## [1] │ MD<CC>CLXXXVIII
str_view(x, "C{2,3}") # Entre 2 y 3 C
## [1] │ MD<CCC>LXXXVIII

*¿Cómo sabemos si el patrón que buscamos existe en las cadenas de texto de nuestro vector? Utilicemos str_detect

fruits <- c("banana", "coco", "papaya", "manzana", "pera", "pepino")
str_detect(fruits, "a")
## [1]  TRUE FALSE  TRUE  TRUE  TRUE FALSE

Ahora utilicemos el conjunto de datos nativo de R: words

head(words)
## [1] "a"        "able"     "about"    "absolute" "accept"   "account"
length(words)
## [1] 980

¿Cuántas palabras empiezan por j?

sum(str_detect(words, "^j")) 
## [1] 6

¿% de palabras que empiezan por vocal?

mean(str_detect(words, "^[aeiou]")) 
## [1] 0.1785714

¿% de palabras que acaban por vocal?

mean(str_detect(words, "[aeiou]$")) 
## [1] 0.2765306

EJERCICIO

¿Como encontraríamos todas las palabras que no contienen ni una vocal?

## [1] 6



Extrayendo patrones

Extrayendo las palabras que cumplen con el patrón

Todas las palabras que acaban en vocal:

head(str_subset(words, "[aeiou]$"))
## [1] "a"         "able"      "absolute"  "achieve"   "active"    "advertise"
length(str_subset(words, "[aeiou]$"))
## [1] 271

Ahora aplicaremos algunas de funciones que hemos aprendido sobre un tibble

library(tidyverse)

df <- tibble(
    words = words, 
    i = seq_along(words)
)
summary(df)
##     words                 i        
##  Length:980         Min.   :  1.0  
##  Class :character   1st Qu.:245.8  
##  Mode  :character   Median :490.5  
##                     Mean   :490.5  
##                     3rd Qu.:735.2  
##                     Max.   :980.0
df %>% 
    head
## # A tibble: 6 × 2
##   words        i
##   <chr>    <int>
## 1 a            1
## 2 able         2
## 3 about        3
## 4 absolute     4
## 5 accept       5
## 6 account      6

Mantener solo las palabras que acaban en x

df %>% 
    filter(str_detect(words, "x$"))
## # A tibble: 4 × 2
##   words     i
##   <chr> <int>
## 1 box     108
## 2 sex     747
## 3 six     772
## 4 tax     841

Contando número de aparaciones

Podemos hacer uso de la función str_count() que en vez de dar una condición booleana de TRUE or FALSE como str_detect(), cuenta el número de veces que aparece un carácter dentro de un string

str_count(fruits, "a")
## [1] 3 0 3 3 1 0

Si combinamos la cuenta con otras funciones nativas o estadísticas podemos obtener calculos interesantes, por ejemplo, podemos obtener el promedio de las vocales por palabra

mean(str_count(words, "[aeiou]"))
## [1] 1.991837

EJERCICIO

Genera un tibble que contenga el número de consonantes y el número de vocales por palabra. Utiliza la función mutate que viste en las sesiones anteriores de este curso

## # A tibble: 980 × 4
##    words        i vowels consonants
##    <chr>    <int>  <int>      <int>
##  1 a            1      1          0
##  2 able         2      2          2
##  3 about        3      3          2
##  4 absolute     4      4          4
##  5 accept       5      2          4
##  6 account      6      3          4
##  7 achieve      7      4          3
##  8 across       8      2          4
##  9 act          9      1          2
## 10 active      10      3          3
## # ℹ 970 more rows



Extrayendo las secciones que son reconocidas por un patrón

library(datos)
sustantivo <- "(el|la|los|las|lo|un|una|unos|unas) ([^ ]+)"
tiene_sustantivo <- oraciones %>%
    str_subset(sustantivo)
tiene_sustantivo %>% 
    str_extract(sustantivo) %>% 
    head
## [1] "los de"      "el camión"   "la mejor"    "la cuenta"   "las ruinas."
## [6] "la hoja"

str_extract() nos devuelve la coincidencia completa; str_match() nos entrega cada componente. Los componentes están delimitados por los paréntesis dentro de la expresión regular

tiene_sustantivo %>% 
    str_match(sustantivo) %>% 
    head
##      [,1]          [,2]  [,3]     
## [1,] "los de"      "los" "de"     
## [2,] "el camión"   "el"  "camión" 
## [3,] "la mejor"    "la"  "mejor"  
## [4,] "la cuenta"   "la"  "cuenta" 
## [5,] "las ruinas." "las" "ruinas."
## [6,] "la hoja"     "la"  "hoja"

Si tus datos están en un tibble, suele ser más fácil utilizar tidyr::extract() el cual funciona como str_match(). Requieres asignarle un nombre a cada componente, debido a que cada uno de estos se asignará a una nueva columna

tibble(oracion = oraciones) %>% 
    tidyr::extract(
      oracion, c("articulo", "sustantivo"), "(el|la|los|las|un|una|unos|unas) ([^ ]+)", 
      remove = FALSE
    )
## # A tibble: 50 × 3
##    oracion                                                   articulo sustantivo
##    <chr>                                                     <chr>    <chr>     
##  1 Las casas están construidas de ladrillos de arcilla roja. los      de        
##  2 La caja fue arrojada al lado del camión estacionado.      el       camión    
##  3 El domingo es la mejor parte de la semana.                la       mejor     
##  4 Agrega a la cuenta de la tienda hasta el último centavo.  la       cuenta    
##  5 Nueve hombres fueron contratados para excavar las ruinas. las      ruinas.   
##  6 Pega la hoja en el fondo azul oscuro.                     la       hoja      
##  7 Instalaron azulejos verdes en la cocina.                  la       cocina.   
##  8 Si arrojo la taza azul al suelo se romperá.               la       taza      
##  9 Dos peces azules nadaban en el tanque.                    el       tanque.   
## 10 El ancho camino brillaba bajo el calor del sol.           el       calor     
## # ℹ 40 more rows

Si te interesa extraer todas las coincidencias para cada cadena, tienes que utilizar str_match_all(), el cual regresará una lista.

tiene_sustantivo %>% 
    str_match_all(sustantivo) %>% 
    head
## [[1]]
##      [,1]       [,2]  [,3]   
## [1,] "los de"   "los" "de"   
## [2,] "la roja." "la"  "roja."
## 
## [[2]]
##      [,1]        [,2] [,3]    
## [1,] "el camión" "el" "camión"
## 
## [[3]]
##      [,1]         [,2] [,3]     
## [1,] "la mejor"   "la" "mejor"  
## [2,] "la semana." "la" "semana."
## 
## [[4]]
##      [,1]        [,2] [,3]    
## [1,] "la cuenta" "la" "cuenta"
## [2,] "la tienda" "la" "tienda"
## [3,] "el último" "el" "último"
## 
## [[5]]
##      [,1]          [,2]  [,3]     
## [1,] "las ruinas." "las" "ruinas."
## 
## [[6]]
##      [,1]       [,2] [,3]   
## [1,] "la hoja"  "la" "hoja" 
## [2,] "el fondo" "el" "fondo"


EJERCICIOS

  1. Busca en datos::oraciones todas las palabras que vengan después de un “número”, como “un(o|a)”, “dos”, “tres”, etc. Extrae tanto el número como la palabra.
##      [,1]             [,2]   [,3]        
## [1,] "dos para"       "dos"  "para"      
## [2,] "dos pastillas." "dos"  "pastillas."
## [3,] "una ave"        "una"  "ave"       
## [4,] "una llama"      "una"  "llama"     
## [5,] "un cartel"      "un"   "cartel"    
## [6,] "tres gráficos." "tres" "gráficos."
  1. En español a veces se utiliza el guión para unir adjetivos, establecer relaciones entre conceptos o para unir gentilicios (p. ej., teórico-práctico, precio-calidad, franco-porteña). ¿Cómo podrías encontrar esas palabras y separar lo que viene antes y después del guión?
##      [,1] [,2] [,3]
##      [,1]               [,2]      [,3]      
## [1,] "teorico-practico" "teorico" "practico"



Ahora, ¿Cómo encontrar todas las frases que contengan un determinado color?

  1. Primero definimos los colores: junta los colores en una nueva variable separados por una barra vertical, recuerda que este símbolo funciona como un OR en expresiones regulares
colors <- c("red", "orange", "yellow", "green", "blue", "purple")
color_match <- str_c(colors, collapse = "|") 
color_match
## [1] "red|orange|yellow|green|blue|purple"
  1. Filtramos las frases que tengan un determinado color:
has_color <- str_subset(sentences, color_match)
head(has_color)
## [1] "Glue the sheet to the dark blue background."
## [2] "Two blue fish swam in the tank."            
## [3] "The colt reared and threw the tall rider."  
## [4] "The wide road shimmered in the hot sun."    
## [5] "See the cat glaring at the scared mouse."   
## [6] "A wisp of cloud hung in the blue air."
  1. Buscar las coincidencias exactas de colores
matches <- str_extract(has_color, color_match)
head(matches)
## [1] "blue" "blue" "red"  "red"  "red"  "blue"

¿En cuáles oraciones aparece más de un color?

str_count(sentences, color_match) %>% 
  head
## [1] 0 1 0 0 0 0
more_than_one <- sentences[str_count(sentences, color_match) > 1]
str_view_all(more_than_one,color_match) 
## Warning: `str_view_all()` was deprecated in stringr 1.5.0.
## ℹ Please use `str_view()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## [1] │ It is hard to erase <blue> or <red> ink.
## [2] │ The <green> light in the brown box flicke<red>.
## [3] │ The sky in the west is tinged with <orange> <red>.
str_extract_all(more_than_one, color_match) %>% 
  head
## [[1]]
## [1] "blue" "red" 
## 
## [[2]]
## [1] "green" "red"  
## 
## [[3]]
## [1] "orange" "red"



Reemplazar patrones

Reemplazando la primer vocal por guión bajo

str_replace(fruits, "[aeiou]", "_")
## [1] "b_nana"  "c_co"    "p_paya"  "m_nzana" "p_ra"    "p_pino"

Reemplazando todas las vocales por guión bajo

str_replace_all(fruits, "[aeiou]", "_")
## [1] "b_n_n_"  "c_c_"    "p_p_y_"  "m_nz_n_" "p_r_"    "p_p_n_"

Otras opciones de reemplazo haciendo uso de un vector entero:

str_replace_all(c("1 coche", "2 telefonos", "3 amigos"),
                  c("1" = "un", "2" = "dos", "3" = "tres")
  )
## [1] "un coche"      "dos telefonos" "tres amigos"

Incercambiar el orden de la segunda y la tercera palabra en una frase

sentences %>%
    head(5)
## [1] "The birch canoe slid on the smooth planks." 
## [2] "Glue the sheet to the dark blue background."
## [3] "It's easy to tell the depth of a well."     
## [4] "These days a chicken leg is a rare dish."   
## [5] "Rice is often served in round bowls."
sentences %>%
    str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
    head(5)
## [1] "The canoe birch slid on the smooth planks." 
## [2] "Glue sheet the to the dark blue background."
## [3] "It's to easy tell the depth of a well."     
## [4] "These a days chicken leg is a rare dish."   
## [5] "Rice often is served in round bowls."

Separando las palabras por espacio

sentences %>%
    head(3) %>%
    str_split(" ") %>% 
    .[[1]]
## [1] "The"     "birch"   "canoe"   "slid"    "on"      "the"     "smooth" 
## [8] "planks."

Conociendo las posiciones de los patrones

Una forma de conocer las posiciones de inicio y de fin de cada palabra dentro de una frase:

sent <- "Frase de ejemplo para provar. Y esto es todo."
str_locate_all(sent, "[aeiou] ")
## [[1]]
##      start end
## [1,]     5   6
## [2,]     8   9
## [3,]    16  17
## [4,]    21  22
## [5,]    36  37
str_view_all(sent, "[aeiou] ")
## [1] │ Fras<e >d<e >ejempl<o >par<a >provar. Y est<o >es todo.
str_sub(sent, 8, 9)
## [1] "e "

Último ejemplo

Ejemplo expresión regular para saber si es un teléfono:

phone <- regex("
                \\(?         # paréntesis de oportura opcionales
               (\\d{3})     # código de área (ej: barcelona 34)
               [)-]?        # cierre de paréntesis, guión o espacio opcionales
               (\\d{3})     # tres dígitos de teléfono
               [ -]?        # espacio o guión opcional
               (\\d{3})     # tres digitos finales
               ", comments = TRUE)
str_detect("971-123-456", "\\(?(\\d{3})[)-]?(\\d{3})[ -]?(\\d{3})")
## [1] TRUE
str_match("971-123-456", phone)
##      [,1]          [,2]  [,3]  [,4] 
## [1,] "971-123-456" "971" "123" "456"


La librería predecesor de stringr: stringi

stringr está construido sobre la base del paquete stringi. stringr es útil cuando estás aprendiendo, ya que presenta un set mínimo de funciones, que han sido elegidas cuidadosamente para inlcuir las funciones de manipulación de cadenas más comunes. stringi, por su parte, está diseñado para ser completo Contiene casi todas las funciones que podrías necesitar: stringi tiene 256 funciones, frente a las 49 de stringr.

Si en algún momento te encuentras en dificultades para hacer algo en stringr , vale la pena darle una mirada a stringi. Ambos paquetes funcionan de manera muy similar, por lo que deberías poder traducir tu conocimiento sobre stringr de manera natural. La principal diferencia es el prefijo: str_ vs. stri_.


Ahora a practicar lo aprendido y espero te sea de utilidad