Manipulacion y limpieza de datos

Luis D. Verde Arregoitia

Temas del bloque:

  • ‘pipes’ y el tidyverse 
  • Estructuras y organización de datos
  • Métodos para reestructurar tablas y archivos
  • Principios de datos ordenados (tidy data) y herramientas del ‘tidyverse’ para ordenar datos
  • Expresiones regulares para trabajar con cadenas de texto
  • Herramientas para manejar datos faltantes
  • Formas de identificar información repetida

Pipes ()

Si queremos hacer varias operaciones en secuencia:

  • Código anidado (los resultados se evalúan de adentro hacia afuera)

  • Objetos intermedios (creamos objetos con resultados intermedios y para la siguiente función en la secuencia)

  • Pipes (estructuramos operaciones seriadas de izquierda a derecha)

Código anidado


notas <- c(7,8,8,10,6,9,5,9,8)

round(mean(notas), digits = 2)
[1] 7.78

Objetos intermedios


notas_promedio <- mean(notas)

notas_promedio_rd <- 
  round(notas_promedio,
           digits = 2)

notas_promedio_rd
[1] 7.78

Pipes

Mano izquiera
(LHS)

operador ‘pipe’

%>%
de 📦 magrittr

|> en R v >= 4.1.0

Mano derecha
(RHS)

Toma un objeto a la izquerda del operador y lo inserta como argumento en la función que esté a su derecha

* Por defecto lo que esté a la izquierda del ‘pipe’ se inserta como el primer argumento de la función

Pipes

  • Reducen la necesidad de guardar resultados intermedios o de anidar código

  • Código más fácil de leer

  • Más fácil quitar o agregar pasos a la secuencia

Insertamos con ctrl + shift + M


library(magrittr)
notas %>% 
  mean() %>% 
  round(digits=2)
[1] 7.78
notas %>% 
  mean %>% 
  round(digits=2)
[1] 7.78


LHS |> RHS(argument = 12)

se convierte en

RHS(LHS, argument = 12)

tidyverse

… una colección de paquetes de R diseñados para ciencia de datos. Todos los paquetes comparten una filosofía de diseño, gramática, y estructuras de datos. Su objetivo principal es de ‘facilitar una conversación sobre datos entre un humano y su computador’

Wickham et al. (2019)
Welcome to the tidyverse
https://www.tidyverse.org/

tidyverse

  • Para tareas comunes con datos: importar archivos, limpiar datos, transformar, visualizar, o programar nuevas funciones.

  • Diseñado para facilitar su aprendizaje y que los usuarios vayan aprendiendo más funciones conforme interactuen con más elementos de este ‘ecosistema’.

Çetinkaya-Rundel et al. (2021)
An educator’s perspective of the tidyverse

tibbles (/tibls/)

Modernizando el data.frame

Implementado para todo el tidyverse a través de 📦 tibble

tibble(
  x = c("a", "b", "c"),
  y = c(1, 2, 3)
)
# A tibble: 3 × 2
  x         y
  <chr> <dbl>
1 a         1
2 b         2
3 c         3

Objeto rectangular común para todos los paquetes del tidyverse

Funciones de tidyverse

  • reciben un data.frame ➡️ regresan un data.frame

  • reciben un tibble ➡️ regresan un tibble

  • generan un objeto nuevo ➡️ crean un tibble

Creando tibbles

  • Salida predeterminada de funciones

  • Convirtiendo otros objetos con as_tibble()

  • Construyendo a partir de vectores con tibble()

tibbles

Para menos ambigüedad:

  • No asignan nombres a las filas

  • No modifican los nombres de las columnas

  • No convierten caracteres en factores

  • Exigen nombres completos para seleccionar subconjuntos de variables con $

Impresión de tibbles

Para no saturar la consola, los métodos de impresión para tibbles muestran:

  • Las primeras 10 filas

  • Cuántas filas y columnas no se imprimieron

  • Solo las columnas que entran en el ancho de nuestra pantalla

  • El tipo de variable de todas las columnas

Organizando información

¿Qué problemas vemos con esta presentación de datos?

Más utilizable

Nombre Periodo TipoViaje PropositoViaje Costo Pais
Miguel J. 2022 Nacional Salida campo 1000 MX
Miguel J. 2022 Nacional Salida campo 1000 MX
Miguel J. 2022 Nacional Congreso 500 MX
Miguel J. 2022 Nacional Taller 500 MX
Miguel J. 2022 Internacional Congreso 2000 EUA
Alejandra M. 2022 NA NA NA NA
Raul S. 2022 Nacional Salida campo 1000 MX

Datos bien organizados

  • Más fácil de importar y manipular

  • Menos potencial de errores no replicables (alteraciones, omisiones, duplicaciones)

Siguiendo buenos principios de organización producimos mejores datos y podremos identificar y resolver problemas en datos existentes

Recomendaciones

  • Unidades, nombres, esquemas, fechas etc. consistentes

  • Nombres utilizables

  • Estructuras rectangulares

Openscapes (2020)
Tidy data illustrated

Broman and Woo (2018)
Organizing data in spreadsheets

Verde Arregoita et al. (2018)
Good practices for sharing analysis-ready data…

Consistencia

Tablas relacionadas con datos sobre seguimiento GPS de aves

id ave edad estado
23 gorrión juvenil VER
11 buho adulto Veracruz
7 buho j Puebla
40 codorniz ADULTO PU
34 gorrión 1 VER
30 paloma A V
ID_ave num_rasteador sexo
23 b452 M
11 b256 M
7 a122 F
40 b889 M
34 d136 F

¿Cuántas inconsistencias ven aquí?

Datos rectangulares

Objetos con filas y columnas, en los cuales:

  • Cada fila tiene el mismo número de columnas

  • Cada columna tiene el mismo número de filas

  • Vectores o factores de la misma longitud entonces todas las columnas tienen la misma ‘altura’

datos rectangulares ~ rangos de celdas en una hoja de cálculo

animal ind_observados habitat
caracol 12 mar
gaviota 14 costa
nutria 11 río
caracol gaviota nutria
12 14 11
mar costa río

Estructurando datos

¿Datos listos para uso?

  • Agrupar, transformar, ordenar, visualizar, modelar, compartir, etc.

¿No se puede?

  • Ordenar para un manejo más fácil y eficiente

Datos ordenados (tidy data)

Una reinterpretación accesible de distintos principios y definiciones de estadística y ciencias de la computación


Wickham (2014)
Tidy Data - Journal of Statistical Software

‘Tidy data’

  • Datos acomodados en una matriz 2D con filas y columnas

  • Las filas corresponden a observaciones

  • Las columnas corresponden a variables

  • Un solo valor por celda

¿Para qué?

Más fácil de reacomodar y manejar

¿Para qué?

Trabajando con filas

Los valores de cada observación quedan juntos


Las variables entran directemente como especificaciones para modelos y/o parámetros gráficos

Cuándo no

  • Los datos ordenados son más repetitivos y pueden ocupar más espacio que otras representaciones más compactas


  • No es un formato ideal para la captura de datos, o para tablas que forman parte de algún texto que será impreso

Consejos rápidos de organización de datos

Variables por separado para definir grupos

Usar ‘variables indicadoras’ en lugar de colapsar información entre dos o más variables.

Equipo Liga Victorias Clasificado
Osos Primera 8 SI
Venados Reservas 2 NO
Dragones Primera 12 SI
Leones Primera 4 NO
Fénix FC Reservas 7 SI

Valores faltantes

No se registraron, no aplican, se perdieron, etc.


Los datos faltantes siguen siendo datos

En R usamos NA, una constante especial que indica valores faltantes

  • Tratar los valores NA consistentemente

  • Evitar ambigüedad

¿Falta un valor, se dejó una celda en blanco intencionalmente, o la medición no aplica?

  • No usar celdas vacías como relleno para alinear datos o con fines estéticos-estructurales

  • Evitar valores NA en nombres de variables

Encabezados utilizables (nombres de las columnas)

Nombres poco informativos e inconsistentes

X x1 X2 calif_min CALIFICACION.PROMEDIO
UANL Norte Arquitectura 6 8.90
UT Norte Diseño 7 7.78
UABJO Sur Diseño 6 8.50
  • Más díficil de recordar para hacer referencia a diferentes variables

  • No se ordenan bien (alfanuméricamente)

universidad region area calificacion_minima calificacion_promedio
UANL Norte Arquitectura 6 8.90
UT Norte Diseño 7 7.78
UABJO Sur Diseño 6 8.50

Nombres de columna partidos

universidad region area calificacion_ calificacion
NA NA NA minima promedio
UANL Norte Arquitectura 6 8.9
UT Norte Diseño 7 7.78
UABJO Sur Diseño 6 8.5
  • Los nombres de las columnas aparecen en >1 fila

  • Mezcla de fragmentos de nombre con datos

  • Se pierden los separadores entre palabras

  • Se introducen NA ::: :::

Compartiendo datos

Datos compartidos adecuadamente

Fáciles de interpretar (por humanos y por máquinas) y listos para analizar

↘ Reduce la dependencia a software complejo o caro y disminuye la necesidad de saber programar para limpiar datos complejos

↘ Reducir la interpretación que habrá que hacer para poder usar los datos

Formatos

  • El PDF no es un formato para datos

  • Mientras más simple mejor (ej: texto delimitado - csv, tsv, etc.)

  • Compartir datos, guardar cálculos y resultados por separado

Herramientas para manipular datos rectangulares

Funciones y paquetes para preparar, transformar, y estructurar datos

📦 dplyr
📦 tidyr

Flexibles, pero desarrollados en torno a una filosofía común

  • Trabas para no usar las funciones incorrectamente
  • Menos código para lograr un resultado
  • Grámatica y estructuras de datos compartidas
  • Tareas divididas entre funciones y paquetes
  • Uso de ‘pipes’

Funciones útiles de 📦 dplyr

  • Funciones separadas para las operaciones más comunes

  • Cada función solo hace una cosa pero la hace bien


  • Funciones intuitivas para:

    • Crear subconjuntos de filas y columnas

    • Crear o modificar columnas

pais especies amenazadas region
Indonesia 670 191 Asia
Brasil 648 80 America
China 551 73 Asia
Mexico 523 96 America
Peru 467 53 America
Congo 430 15 Africa
India 412 93 Asia
Kenya 376 30 Africa
Argentina 374 38 America
Ecuador 372 47 America
Venezuela 363 35 America
Tanzania 359 41 Africa
paises_m <- 
tibble::tribble(
        ~pais, ~especies, ~amenazadas,   ~region,
  "Indonesia",      670L,        191L,    "Asia",
     "Brasil",      648L,         80L, "America",
      "China",      551L,         73L,    "Asia",
     "Mexico",      523L,         96L, "America",
       "Peru",      467L,         53L, "America",
      "Congo",      430L,         15L,  "Africa",
      "India",      412L,         93L,    "Asia",
      "Kenya",      376L,         30L,  "Africa",
  "Argentina",      374L,         38L, "America",
    "Ecuador",      372L,         47L, "America",
  "Venezuela",      363L,         35L, "America",
   "Tanzania",      359L,         41L,  "Africa"
  )

select()

Seleccionar columnas

  • por nombre
  • por posición
  • por tipo
paises_m %>% 
  select(pais,
         amenazadas) %>% 
  head(4)
# A tibble: 4 × 2
  pais      amenazadas
  <chr>          <int>
1 Indonesia        191
2 Brasil            80
3 China             73
4 Mexico            96

mutate()

Crea o modifica columnas

  • Crear nuevas
  • ‘sobre-escribir’ existentes
paises_m %>%
  select(pais, amenazadas) %>% 
  mutate(amenazadas_log = log(amenazadas)) %>% 
  head(5)
# A tibble: 5 × 3
  pais      amenazadas amenazadas_log
  <chr>          <int>          <dbl>
1 Indonesia        191           5.25
2 Brasil            80           4.38
3 China             73           4.29
4 Mexico            96           4.56
5 Peru              53           3.97

Podemos contrar en dónde aparece la nueva columna con los argumentos .before y .after

filter()

Seleccionar filas

Retiene solamente las filas que cumplen una condición

paises_m %>% 
  select(pais, especies, amenazadas) %>% 
  filter(amenazadas > 75)
# A tibble: 4 × 3
  pais      especies amenazadas
  <chr>        <int>      <int>
1 Indonesia      670        191
2 Brasil         648         80
3 Mexico         523         96
4 India          412         93

rename()

Cambiar nombres de columnas

Nombre nuevo primero luego variable objetivo

paises_m %>% 
  rename(country=pais,
         species=especies, threatened=amenazadas) %>% 
  head()
# A tibble: 6 × 4
  country   species threatened region 
  <chr>       <int>      <int> <chr>  
1 Indonesia     670        191 Asia   
2 Brasil        648         80 America
3 China         551         73 Asia   
4 Mexico        523         96 America
5 Peru          467         53 America
6 Congo         430         15 Africa 

Ejercicio - Datos desde paquetes (1)


data() carga o genera una lista de juegos de datos disponibles con los paquetes que tengamos instalados

data() sin ningún argumento muestra todos los datos disponibles

Ejercicio - Datos desde paquetes (2)

Podemos especificar de cuál paquete enlistar datos

data(package="ggplot2")

data(package="datasets")

data(package="tidyr")

Ejercicio - Datos desde paquetes (3)


data() carga datos desde un paquete, como argumento usamos el nombre del juego de datos que necesitamos (con o sin comillas)

Debe estar cargado el paquete que contiene los datos

library(ggplot2)
data(economics)

library(openintro)
data("nba_players_19")

Ejercicio - manipulación de datos

  • Cargar el objeto pinguinos, se instala con el paquete datos

  • Crear un subconjunto de datos que solo incluya especie, isla, y largo_aleta_mm

  • ¿Cuántos individuos tienen aletas > a 195 mm?

  • Crea una nueva columna con el largo de aleta en pulgadas

Trabajando con > 1 columna con across()

Operaciones con varias columnas en simultáneo

across()

Para aplicar filter o mutate a varias columnas

  • Columnas especificadas por posición (no es buena opción), nombre, o tipo

  • Selecciones compuestas
    (ej: variables de tipo factor que contienen ‘temp’ en su nombre)

argumentos de across():

.cols Columnas para transformar

.fns Función a aplicarse a cada una

across espera una función entonces el nombre y sus argumentos se pueden pasar por separado


paises_m %>% 
  mutate(across(c(especies, amenazadas), `*`, 2)) %>% 
  head(3)
# A tibble: 3 × 4
  pais      especies amenazadas region 
  <chr>        <dbl>      <dbl> <chr>  
1 Indonesia     1340        382 Asia   
2 Brasil        1296        160 America
3 China         1102        146 Asia   
  • .cols es un vector con los nombres de las dos columnas que nos interesan

  • * es la función para aplicar, 2 es su argumento

Funciones adicionales para referirnos a columnas

Existen funciones auxiliares para especificar conjuntos de varaibles, se usan junto con o en lugar de across()


everything()

Todas las variables

paises_m %>% 
  mutate(across(everything(), as.character)) %>% head(3)
# A tibble: 3 × 4
  pais      especies amenazadas region 
  <chr>     <chr>    <chr>      <chr>  
1 Indonesia 670      191        Asia   
2 Brasil    648      80         America
3 China     551      73         Asia   

!

Devuelve el complemento de un conjunto de variables

paises_m %>% 
  select(!c(region,amenazadas)) %>% head()
# A tibble: 6 × 2
  pais      especies
  <chr>        <int>
1 Indonesia      670
2 Brasil         648
3 China          551
4 Mexico         523
5 Peru           467
6 Congo          430

where()

Selecciona las variables para las cuales alguna comparación regrese TRUE

ej: transformar todas las variables de tipo numérico, identificadas con is.numeric()

paises_m %>% 
  mutate(across(where(is.numeric), log2)) %>% slice(1:3)
# A tibble: 3 × 4
  pais      especies amenazadas region 
  <chr>        <dbl>      <dbl> <chr>  
1 Indonesia     9.39       7.58 Asia   
2 Brasil        9.34       6.32 America
3 China         9.11       6.19 Asia   

matches()

Encuentra nombres de variables con expresiones regulares

ej: quitar variables que contengan “prec”

paises_m %>% 
  mutate(across(matches("p..s"), toupper)) %>% head
# A tibble: 6 × 4
  pais      especies amenazadas region 
  <chr>        <int>      <int> <chr>  
1 INDONESIA      670        191 Asia   
2 BRASIL         648         80 America
3 CHINA          551         73 Asia   
4 MEXICO         523         96 America
5 PERU           467         53 America
6 CONGO          430         15 Africa 

:

Selecciona variables contiguas

paises_m %>% 
  select(pais:amenazadas) %>% head()
# A tibble: 6 × 3
  pais      especies amenazadas
  <chr>        <int>      <int>
1 Indonesia      670        191
2 Brasil         648         80
3 China          551         73
4 Mexico         523         96
5 Peru           467         53
6 Congo          430         15

-

Excluir variables

(devuleve todos los elementos excepto el que sigue el operador de resta)

paises_m %>% 
  select(-region) %>% head()
# A tibble: 6 × 3
  pais      especies amenazadas
  <chr>        <int>      <int>
1 Indonesia      670        191
2 Brasil         648         80
3 China          551         73
4 Mexico         523         96
5 Peru           467         53
6 Congo          430         15

across sin funciones auxiliares

Raíz cuadarada (sqrt()) de especies y amenazadas

paises_m %>%  
  mutate(across(c(especies, amenazadas), sqrt)) %>% 
  head()
# A tibble: 6 × 4
  pais      especies amenazadas region 
  <chr>        <dbl>      <dbl> <chr>  
1 Indonesia     25.9      13.8  Asia   
2 Brasil        25.5       8.94 America
3 China         23.5       8.54 Asia   
4 Mexico        22.9       9.80 America
5 Peru          21.6       7.28 America
6 Congo         20.7       3.87 Africa 

Raíz cuadrada (sqrt()) de todas las variables numéricas

paises_m %>% 
  mutate(across(where(is.numeric), sqrt)) %>% 
  head()
# A tibble: 6 × 4
  pais      especies amenazadas region 
  <chr>        <dbl>      <dbl> <chr>  
1 Indonesia     25.9      13.8  Asia   
2 Brasil        25.5       8.94 America
3 China         23.5       8.54 Asia   
4 Mexico        22.9       9.80 America
5 Peru          21.6       7.28 America
6 Congo         20.7       3.87 Africa 

Funciones auxiliares para descartar variables

Dejar solo variables numéricas

paises_m %>% 
  select(where(is.numeric)) %>% 
  head()
# A tibble: 6 × 2
  especies amenazadas
     <int>      <int>
1      670        191
2      648         80
3      551         73
4      523         96
5      467         53
6      430         15

Pivotar/pivotear datos

Datos anchos

Lectura y captura más fácil
Útil para edición interactiva en hojas de cálculo

Datos largos

Tiende a seguir principios de Datos Ordenados
Listos para analizar
Se vinculan directamente con parámetros estéticos en un gráfico

Reestructurando datos con 📦 tidyr


ancho a largo: pivot_longer()

pivot_longer(data, cols, names_to, values_to)


largo a ancho: pivot_wider()

pivot_longer(data, cols, names_from, values_from)

Asentamiento Tipo CP Abarrotes Supermercado Minisuper
Arroyo Blanco Colonia 91025 7 2 1
Atenas Fraccionamiento 91184 1 0 2
Colibris Colonia 91067 8 3 2
Del Valle Unidad Habitacional 91097 2 1 1
El Cafetal Fraccionamiento 91150 2 0 0
colonias <- 
tibble::tribble(
    ~Asentamiento,                 ~Tipo,    ~CP, ~Abarrotes, ~Supermercado, ~Minisuper,
  "Arroyo Blanco",             "Colonia", 91025L,         7L,            2L,         1L,
         "Atenas",     "Fraccionamiento", 91184L,         1L,            0L,         2L,
       "Colibris",             "Colonia", 91067L,         8L,            3L,         2L,
      "Del Valle", "Unidad Habitacional", 91097L,         2L,            1L,         1L,
     "El Cafetal",     "Fraccionamiento", 91150L,         2L,            0L,         0L
  )

Variables:

colonia, tipo de asentamiento, Código Postal, Número de negocios

Ancho a largo

colonias_largo <- colonias %>%
  pivot_longer(
    cols = c(Abarrotes,Supermercado,Minisuper),
    names_to = "tipo_negocio",
    values_to = "n_negocios")

cols Las columnas cuyos nombres son datos

names_to Nombre para la nueva varible que se crea a partir de los nombres de cada cols

values_to Nombre para la variable que se crea a partir de los contenidos de cada columna especificada con cols

colonias %>%
  pivot_longer(
    cols = c(Abarrotes,Supermercado,Minisuper),
    names_to = "tipo_negocio",
    values_to = "n_negocios")

colonias %>%
  pivot_longer(
    cols = c(Abarrotes,Supermercado,Minisuper),
    names_to = "tipo_negocio",
    values_to = "n_negocios")
# A tibble: 15 × 5
   Asentamiento  Tipo                   CP tipo_negocio n_negocios
   <chr>         <chr>               <int> <chr>             <int>
 1 Arroyo Blanco Colonia             91025 Abarrotes             7
 2 Arroyo Blanco Colonia             91025 Supermercado          2
 3 Arroyo Blanco Colonia             91025 Minisuper             1
 4 Atenas        Fraccionamiento     91184 Abarrotes             1
 5 Atenas        Fraccionamiento     91184 Supermercado          0
 6 Atenas        Fraccionamiento     91184 Minisuper             2
 7 Colibris      Colonia             91067 Abarrotes             8
 8 Colibris      Colonia             91067 Supermercado          3
 9 Colibris      Colonia             91067 Minisuper             2
10 Del Valle     Unidad Habitacional 91097 Abarrotes             2
11 Del Valle     Unidad Habitacional 91097 Supermercado          1
12 Del Valle     Unidad Habitacional 91097 Minisuper             1
13 El Cafetal    Fraccionamiento     91150 Abarrotes             2
14 El Cafetal    Fraccionamiento     91150 Supermercado          0
15 El Cafetal    Fraccionamiento     91150 Minisuper             0

Largo a ancho

colonias_largo %>% 
  pivot_wider(names_from = tipo_negocio, 
              values_from = n_negocios)

names_from De cuáles columnas vamos a tomar valores para los nombres de las nuevas variables

values_from Cuál columna tiene los valores de celda para las nuevas variables creadas

colonias_largo %>% 
  pivot_wider(names_from = tipo_negocio, 
              values_from = n_negocios)
# A tibble: 5 × 6
  Asentamiento  Tipo                   CP Abarrotes Supermercado Minisuper
  <chr>         <chr>               <int>     <int>        <int>     <int>
1 Arroyo Blanco Colonia             91025         7            2         1
2 Atenas        Fraccionamiento     91184         1            0         2
3 Colibris      Colonia             91067         8            3         2
4 Del Valle     Unidad Habitacional 91097         2            1         1
5 El Cafetal    Fraccionamiento     91150         2            0         0

Ejercicio - pivot

  • Carga el archivo “expresion_genes.csv”. Esta tabla contiene valores de expresión génica para cinco especies de peces, a partir de muestras tomadas en seis tiempos y de dos tejidos.

  • Transforma los datos de ancho a largo y de regreso a ancho.

Otras funciones útiles

coalesce() - Combina valores. A partir de dos o más vectores, retiene valores en orden y rellena datos faltantes con valores de los siguientes vectores.


fill() Rellena valores adyacentes. Ayuda a evitar datos faltantes innecesarios. Similar a las funciones para rellenar valores contiguos en hojas de cálculo.

dplyr::coalesce()

  • Encuentra y regresa el primer valor que no sea NA en cada posición para un conjunto de vectores

* las columnas en datos rectangulares son vectores

  • Devuelve un solo vector con los valores que no sean NA que aparezcan primero.

x <- c(1, 2, NA, NA, 5)
y <- c(NA, NA, 3, 4, 5)
z <- c(1, 4, 3, 5, 3)

Vectores numéricos del mismo largo, hay valores faltantes.

coalesce(x, y, z)
[1] 1 2 3 4 5

El orden de los argumentos importa

x <- c(1, 2, NA, NA, 5)
y <- c(NA, NA, 3, 4, 5)
z <- c(1, 4, 3, 5, 3)
coalesce(z,x,y)
[1] 1 4 3 5 3

muestra sensor_1 sensor_respaldo literatura
ladera sur NA 4.9 2.6
ladera norte 2.2 NA 4.3
urbano 4.5 5.0 3.4
invernadero A NA 2.7 3.5
invernadero B 2.5 NA 2.3
humedad <- 
tibble::tribble(
         ~muestra, ~sensor_1, ~sensor_respaldo, ~literatura,
     "ladera sur",        NA,              4.9,         2.6,
   "ladera norte",       2.2,               NA,         4.3,
         "urbano",       4.5,                5,         3.4,
  "invernadero A",        NA,              2.7,         3.5,
  "invernadero B",       2.5,               NA,         2.3
  )


humedad %>% 
  mutate(val_completo = coalesce(sensor_1, sensor_respaldo, literatura))
# A tibble: 5 × 5
  muestra       sensor_1 sensor_respaldo literatura val_completo
  <chr>            <dbl>           <dbl>      <dbl>        <dbl>
1 ladera sur        NA               4.9        2.6          4.9
2 ladera norte       2.2            NA          4.3          2.2
3 urbano             4.5             5          3.4          4.5
4 invernadero A     NA               2.7        3.5          2.7
5 invernadero B      2.5            NA          2.3          2.5

tidyr::fill()

mamiferos biomasa
ganado 21
NA 5
NA 7
NA 23
NA 74
silvestre 23
NA 65
NA 12
NA 5
  • Rellena valores faltantes en una columna (por defecto de arriba a abajo)

  • Se asume que los valores contiguos son los mismos hasta que aparece otro distinto en la misma columna

  • Rellena hasta el siguiente valor que no sea NA

carbon_mamiferos
# A tibble: 9 × 2
  mamiferos biomasa
  <chr>       <dbl>
1 ganado         21
2 <NA>            5
3 <NA>            7
4 <NA>           23
5 <NA>           74
6 silvestre      23
7 <NA>           65
8 <NA>           12
9 <NA>            5
carbon_mamiferos %>% 
  fill(mamiferos)
# A tibble: 9 × 2
  mamiferos biomasa
  <chr>       <dbl>
1 ganado         21
2 ganado          5
3 ganado          7
4 ganado         23
5 ganado         74
6 silvestre      23
7 silvestre      65
8 silvestre      12
9 silvestre       5

Expresiones regulares para trabajar con cadenas de texto

¿Expresiones regulares?

  • Conocidas como regex, R.E., E.R., o regexp (singular)

  • Lenguaje conciso para describir patrones de texto


Cadenas (🧶strings) de caracteres codificadas especificamente para empatar patrones en otras cadenas de texto

Expresiones regulares

En la práctica, un lenguaje de programación con su propia sintaxis y terminología


entrada: una cadena de texto que se compila en un mini programa construido específicamente para identificar un patrón


Pueden usarse para empatar, buscar, reemplazar, o partir texto

Cadenas de texto (🧶Strings)

Un conjunto de caracteres que componen un elemento de un vector:

cadena_de_texto <- "Esta oración es una cadena"

Podemos almacenar varias cadenas en un vector de texto:

mascotas <-  c("perro","gato","loro","perro")

Cadenas de texto

Los nombres de variables, objetos, y los valores en un data frame también pueden ser cadenas de texto:

bebida precio
café 3.50
2.99
jugo 3.20

R distingue entre mayúsculas y minúsculas

La misma letra en mayúscula y mínuscula se trata como un caracter diferente

"rata"=="RATA"
[1] FALSE
"rata"=="raTa"
[1] FALSE

Por defecto las expresionas regulares también distinguen entre mayúsculas y mínusculas

Podemos:

  • Construir expresiones que no distingan
  • ‘Ignorar’ esta distinción al momento de empatar caracteres

¿Para qué aprender a escribir expresiones regulares?

  • Ahorrar tiempo al buscar patrones, transformar texto, describir patrones, extraer partes de palabras, etc.

  • Una sola expresión regular puede reemplazar decenas de líneas de código

  • Sirven en prácticamente cualquier lenguaje de programación o aplicación de línea de comandos

Primeros pasos con regex

Expresión regular


gato

Entrada



“Ese gato feo”

Resultado



Ese gato feo

La expresión regular para encontrar una secuencia fija de caracteres es esa misma secuencia

busca una g seguida de una a luego t y o (todos estos caracteres juntos y en ese orden de izq. a der.)

Probando regex

¿Está funcionando mi expresión?

Ejercicio

Abrir rubular, regex101, regexr, o regexpal

Probemos el primer ejemplo gato y “ese gato feo”

Caracteres literales y metacaracteres

Tanto las cadenas de texto como las expresiones regulares se componen por caracteres


Podemos agrupar a los caracteres dependiendo de su comportamiento

Literales

Si la entrada es “pato” y la E.R. es pato


Habrá coincidencia cuando los caracteres p, a, t y o aparezcan consecutivamente en el texto de entrada

Caracteres literales

p, a, t y o serían ejemplos de caracteres literales


Se encuentran a sí mismos

El poder y la flexibilidad de las expresiones regulares viene de su capacidad de describir patrones complejos


Si un patron de texto se puede describir verbalmente, seguramente se puede codificar en una expresión regular

Patrones posibles

  • “rata” pero no “bata”

  • “pollo” pero solo si el esta secuencia está al principio de la cadena de texto

  • números (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

  • Variantes ortográficas “Brasil” o “Brazil”

  • Palabras con terminaciones específicas (DAE, NAE, etc.)

  • Fechas

  • Correos electrónicos

Más posibilidades

  • Códigos postales

  • Números entre paréntesis

  • Usuarios de Twitter (comienzan con @, sin espacios ni símbolos, <16 caracteres)

  • Palabras en MAYÚSCULA

Metacaracteres

Un pequeño subconjunto de caracteres nos ayudan a describir patrones más complejos porque tienen comportamientos especiales y no se buscan solo a sí mismos.

Cuando aparecen en una E.R., se interpretan de una manera especial.

Incluyen: []^$.|?*+(), que están apartados para fines especiales

Comodines

Para empatar caracteres desconocidos

. empata cualquier caracter una vez


m..l empata “miel” pero no “miguel”

Conjuntos de caracteres

Empatan uno o más caracteres especificados con corchetes

[ ] para buscar conjuntos de caracteres


[mr]ata empata “mata”, y “rata”, pero no “bata”

Negando conjuntos de caracteres

[^]    excluye conjuntos de caracteres

^ va al principio del conjunto


[^aoeiou] solo empata consonantes

[^R] todo excepto R mayúscula

Intervalos de caracteres

Indican una serie de caracteres contiguos dentro de un conjunto de caracteres

El guión - dentro de un conjunto de caracteres sirve para abreviar secuencias alfabéticas o numéricas

[A-D] cualquier letra mayúscula entre A y D

[5-8] cualquier dígito entre 5 y 8

[A-Za-z] cualquier letra

Aserciones de tipo límite (anchors)

Especifican la posición relativa del patrón que estamos buscando

 ^     comienza con
 $     termina con


  ^mil empata “militar” pero no “familia”

ing$ empata “Boing” pero no “ingeniero”

Ejercicio

  • ¿Cómo podemos empatar mano, milo, malo, y mito?

  • ¿Cómo podemos empatar Cristian, Kristian, y Crystian usando conjuntos de caracteres?

Práctica

  • ¿Cuál de estas E.R. empata mila al principio de una cadena de texto?
  1. ^mila
  1. mila
  1. $mila
  1. mila^

Cuantificadores

Indican el número de caracteres o expresiones que deben coincidir

 ?    Cero o una
 *    Cero o más
 +    Una o más
 {}    n veces

los cuantificadores aplican al caracter que tienen a su izquierda

Cuantificadores

Isabell?a coincide con “Isabella” y con “Isabela

0 o 1

go*l! coincide con: “gl!”, “gol!”, “gooool!”, “goooooooool!”, etc.

0 o más

Cuantificadores

no+ empata “no”, “nooo”, “noooooo”, etc, pero no “n”

una o más

a{2} empata “portaaviones” pero no “portaviones”

n veces

Ejercicio

  • Empatar mira, miiiiiiiiiira, y miiira

  • Empatar Computador, computadora, computador, y Computadora (usar juegos de caracteres y cuantificadores)

Coincidencias alternativas

Separa alternativas de búsqueda

 |     una u otra opción


Bra(s|z)il ecuentra Brasil y Brazil.

las dos alternativas van entre paréntesis

Construcciones especiales y escapes

 \     indica una construcción especial abreviada o sirve para poder usar caracteres especiales como literales

Escapes

qué\\? para poder buscar el signo de interrogación literalmente

en R la diagonal también se tiene que escapar

Los metacaracteres dentro de conjuntos de caracteres se vuelven literales

Construcciones especiales

Para abreviar conjuntos de caracteres

\w  letras, guión bajo, y números

\d  dígitos

\ttabulación

\n   saltos de línea

\s  espacio

\b  límite de palabra

Límites de palabra

\b

Marca la posición en la que un caracter de palabra (letras, números, guión bajo) no va seguido o precedido por otro caracter de palabra (espacios o el inicio o fin de la cadena)

El límite de una palabra encontrada no se incluye en el resultado

Antes de

\bmila encuentra “la milanesa” pero no “Camila”

Después de

arte\b encuentra “saludarte hoy” pero no “artesanal”

Ejercicio

  • Con la entrada “Los tacos de canasta en Tabasco se abarrotaron”

Expliquemos qué pasa cuando usamos estas tres E.R.

  1. ta
  2. \bta
  3. ta\b

Combinaciones

^can.* empata “can”, “canino”, “canasta”, y “canadiense”

A.*n cualquier cadena de texto que empiece con “A” y termine con “n

^[a-z]+$ solo min núsculas

\w+\b$ la última palabra en una cadena

“12 de marzo
“Liomys pictus

^\w+\b primera palabra “12 de marzo”
Liomys pictus”

Ejercicio

Empatemos:

  1. Oraciones que terminen con un punto
  2. Cuatro dígitos
  3. Cadenas sin la letra s

Expresiones regulares en R

Podemos crear subconjuntos de filas y columnas mediante coincidencias entre expresiones regulares y nombres de variables o con sus valores

📦 stringr

Funciones para manipular cadenas de texto

  • Los nombres de las funciones tienen el prefijo str_

  • El primer argumento de sus funciones es la cadena de texto de interés

  • Función regex() para modificar el comportamiento de búsqueda

ignore_case=TRUE para dejar de distinguir entre mayúsculas y minúsculas

stringr - ejemplos

¿Coincidencias?

str_detect(string = c("catalogo", "aguacate",
"pera"),
 pattern = "cat")

Regresa un vector V/F de la misma longitud que la entrada

stringr - ejemplos

[1]  TRUE  TRUE FALSE

stringr - más ejemplos

¿Cuáles elementos coinciden?

str_which(string = c("catalogo", "aguacate", "pera"), pattern = "cat")

Regresa el índice de los elemenots con coincidencias

stringr - más ejemplos

[1] 1 2

stringr - reemplazar coincidencias

str_replace(string = c("anthonyi", "daviesi", "jonesi"),
            pattern = "i$",
            replacement = "ii")
[1] "anthonyii" "daviesii"  "jonesii"  

stringr - función regex()

Sin distinción entre MAYUSCULAS y minúsculas

str_replace(string = c("anthonyI", "daviesi", "jonesi"),
            pattern = regex("i$",ignore_case = TRUE),
            replacement = "ii")
[1] "anthonyii" "daviesii"  "jonesii"  

Ejercicio - stringr

Probemos estas expresiones contra este vector usando str_detect. Explicar las coincidencias.

E. regulares

  1. ^ave
  2. ^[a-z]+$
  3. \d
vec_texto <- c("Aves y reptiles","hamburguesa",
                 "34","(34)","rAta","avenida",
                 "2011_julio","inecol","INECOL")

Manipulando datos con expresiones regulares


Elegir/descartar filas y columnas

Reemplazar o modificar valores

Extraer o borrar partes de cadenas de texto

Expresiones regulares y 📦 dplyr


La función auxiliar matches() acepta expresiones regulares para seleccionar variables que contengan coincidencias

Seleccionando variables

Datos de pingüinos pinguinos de 📦 datos

library(datos)
data(pinguinos)
names(pinguinos)
[1] "especie"         "isla"            "largo_pico_mm"   "alto_pico_mm"   
[5] "largo_aleta_mm"  "masa_corporal_g" "sexo"            "anio"           

pinguinos %>% 
  select(especie, matches("larg")) %>% 
  sample_n(3)
# A tibble: 3 × 3
  especie largo_pico_mm largo_aleta_mm
  <fct>           <dbl>          <int>
1 Papúa            45.2            215
2 Barbijo          48.5            191
3 Barbijo          52.7            197

Filtrando filas

Datos de paises del paquete 📦 datos

library(datos)
data(paises)
paises %>% select(pais,continente) %>% 
  sample_n(4)
# A tibble: 4 × 2
  pais        continente
  <fct>       <fct>     
1 Jordania    Asia      
2 Corea, Rep. Asia      
3 Marruecos   África    
4 Comoras     África    

Coincidencias en filas

Paises con “z” en su nombre

paises %>% select(pais,continente) %>% 
  filter(str_detect(pais,"z")) %>% 
  distinct()
# A tibble: 7 × 2
  pais                 continente
  <fct>                <fct>     
1 Bosnia y Herzegovina Europa    
2 Mozambique           África    
3 Swazilandia          África    
4 Suiza                Europa    
5 Tanzania             África    
6 Túnez                África    
7 Venezuela            Américas  

Información repetida

Valores repetidos

  • Definición variable pero generalmente dos o más copias del mismo registro u observación

Problemas posibles

  • Inflán datos

  • Costos de repetición no intencional

  • Resultados imprecisos

Identificando y eliminando duplicados

  • Identificar con get_dupes() del paquete 📦 janitor

  • Eliminar con distinct() de 📦 dplyr

Repetición

  • En todas las variables (copias idénticas de una fila)

  • En las variables que definen nuestras unidades observacionales

  • En conjuntos arbitrarios de variables

IDusuario Direccion Colonia Alcaldia
Pablitoo Cafetal 395 Casa 2 San Angel A. Obregón
susana24 Fresnos 114 San Angel A. Obregón
susana24 Fresnos 114 San Angel A. Obregón
jd.adriana Cafetal 395 Casa 8 San Angel A. Obregón
JuanManuel Hidalgo 11 Depto 1B Campestre A. Obregón
JoaquinED Hidalgo 11 Depto 1B Campestre A. Obregón
e_ric2 Hidalgo 11 Depto 1B Campestre A. Obregón
ordenes_pizza <- 
tibble::tribble(
    ~IDusuario,            ~Direccion,    ~Colonia,    ~Alcaldia,
    "Pablitoo",  "Cafetal 395 Casa 2", "San Angel", "A. Obregón",
    "susana24",         "Fresnos 114", "San Angel", "A. Obregón",
    "susana24",         "Fresnos 114", "San Angel", "A. Obregón",
  "jd.adriana",  "Cafetal 395 Casa 8", "San Angel", "A. Obregón",
  "JuanManuel", "Hidalgo 11 Depto 1B", "Campestre", "A. Obregón",
   "JoaquinED", "Hidalgo 11 Depto 1B", "Campestre", "A. Obregón",
      "e_ric2", "Hidalgo 11 Depto 1B", "Campestre", "A. Obregón"
  )

Identificar con get_dupes()

ordenes_pizza %>%
  get_dupes() # todas las variables (por defecto)
# A tibble: 2 × 5
  IDusuario Direccion   Colonia   Alcaldia   dupe_count
  <chr>     <chr>       <chr>     <chr>           <int>
1 susana24  Fresnos 114 San Angel A. Obregón          2
2 susana24  Fresnos 114 San Angel A. Obregón          2
  • Añade una variable dupe_count que cuantifica las filas con valores duplicados

  • Acomoda las variables de interés al principio del data frame resultante

get_dupes() asignando una variable para definir nuestra unidad observacional

¿Alguien ordenó más de una vez?

ordenes_pizza %>%
  get_dupes(IDusuario)
# A tibble: 2 × 5
  IDusuario dupe_count Direccion   Colonia   Alcaldia  
  <chr>          <int> <chr>       <chr>     <chr>     
1 susana24           2 Fresnos 114 San Angel A. Obregón
2 susana24           2 Fresnos 114 San Angel A. Obregón

get_dupes() con combinaciones de variables

  • Filas repetidas en dirección * colonia

accepta funciones auxiliares de 📦 tidyselect

ordenes_pizza %>%
  get_dupes(Direccion, starts_with("Col"))
# A tibble: 5 × 5
  Direccion           Colonia   dupe_count IDusuario  Alcaldia  
  <chr>               <chr>          <int> <chr>      <chr>     
1 Hidalgo 11 Depto 1B Campestre          3 JuanManuel A. Obregón
2 Hidalgo 11 Depto 1B Campestre          3 JoaquinED  A. Obregón
3 Hidalgo 11 Depto 1B Campestre          3 e_ric2     A. Obregón
4 Fresnos 114         San Angel          2 susana24   A. Obregón
5 Fresnos 114         San Angel          2 susana24   A. Obregón

Descartar con distinct() de 📦 dplyr

ordenes_pizza %>%
  distinct() # todas las variables
# A tibble: 6 × 4
  IDusuario  Direccion           Colonia   Alcaldia  
  <chr>      <chr>               <chr>     <chr>     
1 Pablitoo   Cafetal 395 Casa 2  San Angel A. Obregón
2 susana24   Fresnos 114         San Angel A. Obregón
3 jd.adriana Cafetal 395 Casa 8  San Angel A. Obregón
4 JuanManuel Hidalgo 11 Depto 1B Campestre A. Obregón
5 JoaquinED  Hidalgo 11 Depto 1B Campestre A. Obregón
6 e_ric2     Hidalgo 11 Depto 1B Campestre A. Obregón

distinct() con la variables que definen las unidades observacionales

ordenes_pizza %>%
  distinct(IDusuario, .keep_all = TRUE)
# A tibble: 6 × 4
  IDusuario  Direccion           Colonia   Alcaldia  
  <chr>      <chr>               <chr>     <chr>     
1 Pablitoo   Cafetal 395 Casa 2  San Angel A. Obregón
2 susana24   Fresnos 114         San Angel A. Obregón
3 jd.adriana Cafetal 395 Casa 8  San Angel A. Obregón
4 JuanManuel Hidalgo 11 Depto 1B Campestre A. Obregón
5 JoaquinED  Hidalgo 11 Depto 1B Campestre A. Obregón
6 e_ric2     Hidalgo 11 Depto 1B Campestre A. Obregón

.keep_all para retener o no las demás columnas

distinct() con combinaciones de variables

  • Combinaciones únicas de dirección * colonia
ordenes_pizza %>%
  distinct(Direccion,Colonia)
# A tibble: 4 × 2
  Direccion           Colonia  
  <chr>               <chr>    
1 Cafetal 395 Casa 2  San Angel
2 Fresnos 114         San Angel
3 Cafetal 395 Casa 8  San Angel
4 Hidalgo 11 Depto 1B Campestre

⚠️ Si .keep_all = TRUE y hay valores duplicados en otras variables, distinct solo retiene la primera fila

ordenes_pizza %>%
  distinct(Direccion,Colonia, .keep_all = TRUE)
# A tibble: 4 × 4
  IDusuario  Direccion           Colonia   Alcaldia  
  <chr>      <chr>               <chr>     <chr>     
1 Pablitoo   Cafetal 395 Casa 2  San Angel A. Obregón
2 susana24   Fresnos 114         San Angel A. Obregón
3 jd.adriana Cafetal 395 Casa 8  San Angel A. Obregón
4 JuanManuel Hidalgo 11 Depto 1B Campestre A. Obregón

Ejercicio

  • Cargar el archivo murcielagos.csv

  • ¿De cuántas localidades únicas provienen estos registros?

  • Exportar un archivo con las localidades únicas y nombres de columna en español