Manejo de datos con R (I):
the tidyverse way

Módulo práctico de ITA
(Grupo M)

Data munging: the tidyverse way



Aprendimos a cargar datos, pero es raro que los datos estén preparados para empezar nuestro análisis, así que hay que “arreglar/limpiar” los datos. Para ello, tenemos que:

  1. Hacer nuestros datos TIDY

  2. Arreglarlos/modificarlos para que sean útiles para nuestros propósitos

Arreglando los datos

  • El procesado/limpieza de los datos suele ocupar un 80% del tiempo de un análisis de datos; así que el workflow sería más bien así:

  • Aprenderemos a limpiar y transformar datos en R. Priorizaremos la nueva forma de hacer las cosas en R (o workflow) conocido como tidyverse: un conjunto de paquetes que trabajan en armonía y que permiten una nueva forma de escribir/programar en R.

principales pkgs del Tidyverse

  • tidyr: convertir a tidy data

  • dplyr: para manipular datos

  • ggplot2: para hacer gráficos

  • … y algunos más

aquí nos centraremos principalmente en dos paquetes: dplyr y tidyr

“Filosofía” del Tidyverse

Programs must be written for people to read, and only incidentally for machines to execute – Hal Abelson

dos principios del tidyverse

  • Los scripts deben ser “fácilmente” legibles por las personas

  • Resolver problemas complejos encadenando funciones simples con el operador pipe (%>%)

The pipe ( %>% )



  • El operador %>% es crucial en el tidyverse. Hay que saber usarlo y entenderlo

  • Es fácil. Pronto os sentiréis cómodos con él.

The pipe

  • The pipe es un operador que pasa el elemento que está a su izquierda como un argumento de la función que tiene a la derecha.



Igual os resulta más fácil entenderlo con ejemplos


  • Las siguientes 3 expresiones hacen exactamente lo mismo:
head(iris, n = 4)         #- forma habitual de llamar/usar la función head()

iris %>% head(. , n = 4)  #- usando el operador pipe (con el punto actuando como placeholder)

iris %>% head(n = 4)      #- usando el operador pipe (SIN el punto)

The pipe (más ejemplos)

¿Qué hace la siguiente expresión?

4 %>% head(iris, .)

¿por qué no funciona la siguiente expresión?

4 %>% head(iris)

Pipe nativa (|>)

  • En 2021, la versión 4.1.0 de R supuso la aparición de una pipe nativa en R: |>

  • Funciona igual que %>%, salvo que su placeholder no es el punto, sino el guion bajo: _

¿qué pasará en el futuro?

Tidy data



  • Para trabajar à la tidyverse es crucial que los datos sean “tidy”
  • El concepto de datos tidy es sencillo. Hacer los datos tidy no tanto pero tenemos un paquete para hacerlo fácil: el pkg tidyr

Un aspecto importante del tidyverse es hacer los datos tidy

  • Unos datos son tidy si: (1) cada columna es una variable, (2) cada fila es una observación y (3) cada valor está en, o tiene, su propia celda.

Parece fácil, y lo es: realmente es la situación a la que estamos acostumbrados, pero …
… mejor desarrollarlo con unos ejemplos

Tidy data … ejemplo I

¿Estos datos son tidy?

df_1 <- data.frame(
            year    = c("2023", "2024", "2025"),  
            España  = c(83.8, 84.0, 84.2), 
            Francia = c(83.1, 83.3, 83.4), 
            Italia  = c(83.5, 83.6, 83.8)  )
DT::datatable(df_1)

Tidy data … ejemplo II

¿Estos datos son tidy?

df_2 <- data.frame(pais = c("España", "Francia", "Italia"), 
                   vida_2023 = c(83.8, 83.1, 83.5), 
                   vida_2024 = c(84.0, 83.3, 83.6),
                   vida_2025 = c(84.2, 83.4, 83.8)   )
gt::gt(df_2)
pais vida_2023 vida_2024 vida_2025
España 83.8 84.0 84.2
Francia 83.1 83.3 83.4
Italia 83.5 83.6 83.8

Tidy data … ejemplo III

¿Estos datos son tidy?

df_3 <- data.frame(
            pais = rep(c("España", "Francia", "Italia"), times = 3),  
            year = rep(c("2023", "2024", "2025"), each = 3),
            esperanza_vida = c(83.8, 83.1, 83.5, 84.0, 83.3, 83.6, 84.2, 83.4, 83.8) )
gt::gt(df_3)
pais year esperanza_vida
España 2023 83.8
Francia 2023 83.1
Italia 2023 83.5
España 2024 84.0
Francia 2024 83.3
Italia 2024 83.6
España 2025 84.2
Francia 2025 83.4
Italia 2025 83.8
  • Sí, estos sí son datos tidy

  • Son más difíciles de leer por las personas, pero es que los datos los leen las máquinas!!

Los datos tidy suelen estar en formato largo o long

Tidy data en formato LONG



  • Es importante aprender a pasar de datos WIDE a LONG y viceversa

  • Con el pkg tidyr es sencillo pero …

De wide a LONG format con … tidyr::pivot_longer()

pivot_longer() convierte entre formatos: de wide a long format
df_wide %>% pivot_longer(cols = 2:4, names_to = "periodo", values_to = "esperanza_vida")
WIDE (ancho) df_wide: 3 filas × 4 columnas
tidyr::pivot_longer(df_wide)→
LONG (largo) df_long: 9 filas × 3 columnas
(Pasa el ratón por encima de un dato y verás dónde acaba en la otra tabla)

Tarea: convierte “df_wide” a formato LARGO

Aquí tienes un df en formato ANCHO, pásalo a formato LARGO

df_wide <- data.frame(pais = c("España", "Francia", "Italia"), 
                      vida_2023 = c(83.8, 83.1, 83.5), 
                      vida_2024 = c(84.0, 83.3, 83.6),
                      vida_2025 = c(84.2, 83.4, 83.8) )

Está en formato ancho:

pais vida_2023 vida_2024 vida_2025
España 83.8 84.0 84.2
Francia 83.1 83.3 83.4
Italia 83.5 83.6 83.8

Lo quiero en formato largo:

pais periodo esperanza_vida
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8


  • Te ayudará visualizar df_wide y ver que columnas contienen valores de la misma variable


  • Tienes que usar tidyr::pivot_longer() y en su primer argumento le tienes que decir que columnas hay que convertir a formato “largo”


El df original en formato ancho

pais vida_2023 vida_2024 vida_2025
España 83.8 84.0 84.2
Francia 83.1 83.3 83.4
Italia 83.5 83.6 83.8
df_long <- df_wide %>% 
             tidyr::pivot_longer(cols = 2:4)


  • Solo es necesario un argumento, el argumento cols, donde tienes que explicitar las columnas que hay que pasar a formato largo

  • … PERO fíjate en los nombres de las columnas nuevas: como no se los hemos dicho, R pone name y value. Funciona, pero no se entiende qué es cada cosa

pais name value
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8
df_long <- df_wide %>% 
             tidyr::pivot_longer(cols = 2:4, 
                                 names_to = "periodo", 
                                 values_to = "esperanza_vida")


  • cols = 2:4: qué columnas pasamos a formato largo

  • names_to = "periodo": cómo se llama la columna nueva donde van los títulos de esas columnas

  • values_to = "esperanza_vida": cómo se llama la columna nueva donde van los valores

  • Ojo: names_to y values_to van entre comillas, porque son columnas que todavía no existen: te las inventas tú

pais periodo esperanza_vida
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8

De long a WIDE format con tidyr::pivot_wider()

pivot_wider() convierte entre formatos: de long a wide format
df_long %>% pivot_wider(names_from = periodo, values_from = esperanza_vida)
LONG (largo) df_long: 9 filas × 3 columnas
tidyr::pivot_wider(df_long)→
WIDE (ancho) df_wide2: 3 filas × 4 columnas
(Pasa el ratón por encima de un dato y verás dónde acaba en la otra tabla)


¿Por qué querríamos convertirlo a formato ANCHO?

  • Para presentar los datos en una tabla: el formato ancho es más fácil de leer por las personas

  • Porque algunas funciones y programas (por ejemplo Excel) esperan los datos en formato ancho

Tarea: convierte “df_long” a formato ANCHO

  • df_long está en formato LARGO, pásalo a formato ANCHO

  • Tienes que distribuir los datos de esperanza de vida en columnas: una columna para cada año

Está en formato largo:

gt::gt(df_long)
pais periodo esperanza_vida
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8

Lo quiero en formato ancho: (con una columna para cada año)

pais vida_2023 vida_2024 vida_2025
España 83.8 84.0 84.2
Francia 83.1 83.3 83.4
Italia 83.5 83.6 83.8
  • Tienes que usar tidyr::pivot_wider()


  • En el argumento values_from le tienes que decir qué columna quieres que sus valores se distribuyan entre varias columnas


  • En el argumento names_from le tienes que decir qué columna quieres que sus valores aparezcan como los títulos de las nuevas columnas que se crearán

El df en formato largo:

gt::gt(df_long)
pais periodo esperanza_vida
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8
df_wide2 <- df_long %>% 
              tidyr::pivot_wider(values_from = esperanza_vida,  
                                 names_from = periodo)


gt::gt(df_wide2)
pais vida_2023 vida_2024 vida_2025
España 83.8 84.0 84.2
Francia 83.1 83.3 83.4
Italia 83.5 83.6 83.8

Extensiones



  • Otra tarea más de pivot_wider()

  • Dos funciones más de tidyr: separate() y unite()

Tarea: convierte OTRA VEZ “df_long” a formato ANCHO

  • df_long está en formato LARGO, pásalo a formato ANCHO

  • Tienes que distribuir los datos de esperanza de vida en columnas: PERO esta vez quiero que haya una columna para cada país (España, Francia, …)

Está en formato largo:

gt::gt(df_long)
pais periodo esperanza_vida
España vida_2023 83.8
España vida_2024 84.0
España vida_2025 84.2
Francia vida_2023 83.1
Francia vida_2024 83.3
Francia vida_2025 83.4
Italia vida_2023 83.5
Italia vida_2024 83.6
Italia vida_2025 83.8

Lo quiero en formato ancho:

(con una columna para cada PAÍS)

periodo España Francia Italia
vida_2023 83.8 83.1 83.5
vida_2024 84.0 83.3 83.6
vida_2025 84.2 83.4 83.8
  • Igual que en la tarea anterior: tidyr::pivot_wider() con sus argumentos values_from y names_from


  • PERO, CUIDADO!!! piensa bien qué columna tiene que ir ahora en names_from
df_wide2 <- df_long %>% 
              tidyr::pivot_wider(values_from = esperanza_vida, 
                                 names_from = pais)


gt::gt(df_wide2)
periodo España Francia Italia
vida_2023 83.8 83.1 83.5
vida_2024 84.0 83.3 83.6
vida_2025 84.2 83.4 83.8

las funciones separate() y unite()

  • El paquete tidyr tiene muchas funciones. Veremos dos: separate() y unite()


  • separate() y unite() nos facilitarán poder separar y unir columnas


  • Por ejemplo, fíjate en el siguiente dataframe: ¿es tidy?
personajes year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904

Tarea: Utilizando tidyr::separate()

  • Separa la columna personajes en dos columnas: nombre y apellido
df <- data.frame( personajes = c("Pedro Navaja", "Bob Dylan", "Peter Pan"), 
                  year  = c(1978, 1941, 1904) )
gt::gt(df)
personajes year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904
  • Has de usar tidyr::separate()


  • Tendrás que darle los valores de 3 argumentos: col, into y sep

    • en el argumento col tienes que decir qué columna quieres separar

    • en into tienes que decir qué nombres quieres que tengan las nuevas columnas

    • en sep tienes que decir qué caracter separa los dos elementos

df_1 <- df %>% 
  tidyr::separate(col = personajes, 
                  into = c("nombre", "apellido"), 
                  sep = " ")


gt::gt(df)
personajes year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904
gt::gt(df_1)
nombre apellido year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904

Tarea: Utilizando tidyr::unite()

  • Unir las columnas nombre y apellido en una sola columna
nombre apellido year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904
  • Has de usar tidyr::unite()


  • Tendrás que darle los valores de “3” argumentos:

    • en el argumento col tienes que decir el nombre de la nueva columna que se va a crear

    • después tienes que decir qué columnas quieres unir

    • en sep tienes que decir qué carácter separará los dos elementos que vamos a unir

df_2 <- df %>% 
       unite(col = nombre_y_apellido, 
             c(apellido, nombre), 
             sep = ", ")


gt::gt(df)
nombre apellido year
Pedro Navaja 1978
Bob Dylan 1941
Peter Pan 1904
gt::gt(df_2)
nombre_y_apellido year
Navaja, Pedro 1978
Dylan, Bob 1941
Pan, Peter 1904