tabla_1 |>
xxx_join(tabla_2, by = id)Joins e importación: como combinar, exportar e importar en R
Cuadernos prácticos de Software y Gestión de Bases de Datos del Máster de Bioestadística (curso 2024-2025)
1 Relacionando datos: joins
Al trabajar con datos no siempre tendremos la información en una sola tabla y a veces nos interesará cruzar la información de distintas fuentes.
Para ello usaremos un clásico de todo lenguaje que maneja datos: los famosos join, que nos permitirán cruzar una o varias tablas, haciendo uso de una columna identificadora de cada una de ellas (por ejemplo, imagina que cruzamos datos de hacienda y de antecedentes penales, haciendo join por la columna DNI).
La estructura básica es la siguiente:
Vamos a probar los distintos joins con un ejemplo sencillo
library(tidyverse)── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
tb_1 <- tibble("key" = 1:3, "val_x" = c("x1", "x2", "x3"))
tb_2 <- tibble("key" = c(1, 2, 4), "val_y" = c("y1", "y2", "y3"))tb_1# A tibble: 3 × 2
key val_x
<int> <chr>
1 1 x1
2 2 x2
3 3 x3
tb_2# A tibble: 3 × 2
key val_y
<dbl> <chr>
1 1 y1
2 2 y2
3 4 y3
1.1 Left join
left_join(): mantiene todos los registros de la primera tabla, y busca cuales tienen id también en la segunda (en caso de no tenerlo se rellena con NA los campos de la 2ª tabla).
En nuestra caso queremos incorporar a tb_1 la información de tb_2, identificando los registros por la columna key (by = "key", la columna por la que tiene que cruzar)
tb_1 |>
left_join(tb_2, by = "key")# A tibble: 3 × 3
key val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
tb_1 |>
left_join(tb_2, by = "key")# A tibble: 3 × 3
key val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
Fíjate que los registros de la primera cuya key no ha encontrado en la segunda les ha dado el valor de ausente.
1.2 Right join
right_join(): mantiene todos los registros de la segunda tabla, y busca cuales tienen id también en la primera.
Vamos ahora a incorporar a tb_2 la información de tb_1, identificando los registros por la columna key (by = "key")
tb_1 |>
right_join(tb_2, by = "key")# A tibble: 3 × 3
key val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 4 <NA> y3
tb_1 |>
right_join(tb_2, by = "key")# A tibble: 3 × 3
key val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 4 <NA> y3
Fíjate que ahora los registros de la segunda cuya key no ha encontrado en la primera son los que les ha dado el valor de ausente.
1.3 Claves y sufijos
Las columnas clave que usaremos para el cruce no siempre se llamarán igual.
tb_1 <- tibble("key_1" = 1:3, "val_x" = c("x1", "x2", "x3"))
tb_2 <- tibble("key_2" = c(1, 2, 4), "val_y" = c("y1", "y2", "y3"))by = c("key_2" = "key_2"): le indicaremos en qué columna de cada tabla están las claves por las que vamos a cruzar.
# Left
tb_1 |>
left_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 3 × 3
key_1 val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
# Right
tb_1 |>
right_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 3 × 3
key_1 val_x val_y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 4 <NA> y3
Además podemos cruzar por varias columnas a la vez (interpretará como igual registro aquel que tenga el conjunto de claves igual), con by = c("var1_t1" = "var1_t2", "var2_t1" = "var2_t2", ...). Modifiquemos el ejemplo anterior
tb_1 <- tibble("k_11" = 1:3, "k_12" = c("a", "b", "c"), "val_x" = c("x1", "x2", "x3"))
tb_2 <- tibble("k_21" = c(1, 2, 4), "k_22" = c("a", "b", "e"), "val_y" = c("y1", "y2", "y3"))# Left
tb_1 |>
left_join(tb_2,
by = c("k_11" = "k_21", "k_12" = "k_22"))# A tibble: 3 × 4
k_11 k_12 val_x val_y
<dbl> <chr> <chr> <chr>
1 1 a x1 y1
2 2 b x2 y2
3 3 c x3 <NA>
También podría suceder que al cruzar dos tablas, haya columnas de valores que se llamen igual
tb_1 <- tibble("key_1" = 1:3, "val" = c("x1", "x2", "x3"))
tb_2 <- tibble("key_2" = c(1, 2, 4), "val" = c("y1", "y2", "y3"))# Left
tb_1 |>
left_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 3 × 3
key_1 val.x val.y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
Fíjate que por defecto nos añade los sufijos .x y .y para indicarnos de que tabla vienen. Dicho sufijo podemos especificárselo en el argumento opcional suffix = ..., que nos permita distinguir las variables de una tabla y de otra.
# Left
tb_1 |>
left_join(tb_2, by = c("key_1" = "key_2"), suffix = c("_tabla1", "_tabla2"))# A tibble: 3 × 3
key_1 val_tabla1 val_tabla2
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
1.4 Full join
full_join(): mantiene todos los registros de ambas tablas.
Los dos anteriores casos forman lo que se conoce como outer joins: cruces donde se mantienen observaciones que salgan en al menos una tabla. El tercer outer join es el conocido como full_join() que nos mantendrá las observaciones de ambas tablas, añadiendo las filas que no casen con la otra tabla.
tb_1 |>
full_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 4 × 3
key_1 val.x val.y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
3 3 x3 <NA>
4 4 <NA> y3
1.5 Inner join
inner_join(): solo sobreviven los registros cuyo id esté en ambas tablas.
Frente a los outer join está lo que se conoce como inner join, con inner_join(): un cruce en el que solo se mantienen las observaciones que salgan en ambas tablas, solo mantiene aquellos registros matcheados.
tb_1 |>
inner_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 2 × 3
key_1 val.x val.y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
Fíjate que en términos de registros, inner_join si es conmutativa, nos da igual el orden de las tablas: lo único que cambia es el orden de las columnas que añade.
tb_1 |>
inner_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 2 × 3
key_1 val.x val.y
<dbl> <chr> <chr>
1 1 x1 y1
2 2 x2 y2
tb_2 |>
inner_join(tb_1, by = c("key_2" = "key_1"))# A tibble: 2 × 3
key_2 val.x val.y
<dbl> <chr> <chr>
1 1 y1 x1
2 2 y2 x2
1.6 Anti/semi join
Por último tenemos dos herramientas interesantes para filtrar (no cruzar) registros: semi_join() y anti_join(). El semi join nos deja en la primera tabla los registros que cuya clave está también en la segunda (como un inner join pero sin añadir la info de la segunda tabla). Y el segundo, los anti join, hace justo lo contrario (aquellos que no están).
# semijoin
tb_1 |>
semi_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 2 × 2
key_1 val
<int> <chr>
1 1 x1
2 2 x2
# antijoin
tb_1 |>
anti_join(tb_2, by = c("key_1" = "key_2"))# A tibble: 1 × 2
key_1 val
<int> <chr>
1 3 x3
1.7 💻 Tu turno
Intenta realizar los siguientes ejercicios sin mirar las soluciones
Para los ejercicios usaremos las tablas disponibles en el paquete {nycflights13} (echa un vistazo antes)
library(nycflights13)- airlines: nombre de aerolíneas (con su abreviatura).
- airports: datos de aeropuertos (nombres, longitud, latitud, altitud, etc).
- flights: datos de vuelos.
- planes: datos de los aviones.
- weather: datos meteorológicos horarios de las estaciones LGA, JFK y EWR.
📝 Del paquete {nycflights13} cruza la tabla flights con airlines. Queremos mantener todos los registros de vuelos, añadiendo la información de las aerolíneas a dicha tabla.
Code
flights_airlines <-
flights |>
left_join(airlines, by = "carrier")
flights_airlines📝 A la tabla obtenida del cruce del apartado anterior, cruza después con los datos de los aviones en planes, pero incluyendo solo aquellos vuelos de los que tengamos información de sus aviones (y viceversa).
Code
flights_airlines_planes <-
flights_airlines |>
inner_join(planes, by = "tailnum")
flights_airlines_planes📝 Repite el ejercicio anterior pero conservando ambas variables year (en una es el año del vuelo, en la otra es el año de construcción del avión), y distinguiéndolas entre sí
Code
flights_airlines_planes <-
flights_airlines |>
inner_join(planes, by = "tailnum",
suffix = c("_flight", "_build_aircraft"))
flights_airlines_planes📝 Al cruce obtenido del ejercicio anterior incluye la longitud y latitud de los aeropuertos en airports, distinguiendo entre la latitud/longitud del aeropuerto en destino y en origen.
Code
flights_airlines_planes %>%
left_join(airports %>% select(faa, lat, lon),
by = c("origin" = "faa")) |>
rename(lat_origin = lat, lon_origin = lon) |>
left_join(airports %>% select(faa, lat, lon),
by = c("dest" = "faa")) |>
rename(lat_dest = lat, lon_dest = lon)📝 Filtra de airports solo aquellos aeropuertos de los que salgan vuelos. Repite el proceso filtrado solo aquellos a los que lleguen vuelos
Code
airports |>
semi_join(flights, by = c("faa" = "origin"))
airports |>
semi_join(flights, by = c("faa" = "dest"))📝 ¿De cuántos vuelos no disponemos información del avión? Elimina antes los vuelos que no tengan identificar (diferente a NA) del avión
Code
flights |>
drop_na(tailnum) |>
anti_join(planes, by = "tailnum") |>
count(tailnum, sort = TRUE) # de mayor a menor ya de paso2 🐣Caso práctico I: Beatles y Rolling Stones
Vamos a empezar a practicar joins sencillos con la tabla band_members y band_instruments ya incluidos en el paquete {dplyr}.
library(dplyr)
band_members# A tibble: 3 × 2
name band
<chr> <chr>
1 Mick Stones
2 John Beatles
3 Paul Beatles
band_instruments# A tibble: 3 × 2
name plays
<chr> <chr>
1 John guitar
2 Paul bass
3 Keith guitar
En la primera tenemos una serie de artistas y la banda a la que pertenecen; en la segunda tenemos una serie de artistas y el instrumento que tocan. Además de realizar las acciones solicitadas intenta visualizar qué tabla final tendrías antes de ejecutar el código..
2.1 Pregunta 1
Dada la tabla
band_members, incorpora la información de qué instrumento toca cada miembro (band_instruments) de los que tienes en esa tabla.
Code
left_join_band <-
band_members |>
left_join(band_instruments, by = "name")2.2 Pregunta 2
Dadas las tablas
band_membersyband_instruments, ¿qué tipo de join deberías hacer para tener una tabla completa, sin ausencias, donde todos los miembros de la banda tengan la información de su instrumento, y cada instrumento tenga un miembro asociado a él?
Code
inner_join_band <-
band_members |>
inner_join(band_instruments, by = "name")2.3 Pregunta 3
Dada la tabla
band_instruments, ¿cómo incorporar la información de quién toca cada instrumento (en caso de que la conozcamos)?
Code
right_join_band <-
band_members |>
right_join(band_instruments, by = "name")
# other option
left_join_instruments <-
band_instruments |>
left_join(band_members, by = "name")2.4 Pregunta 4
Dadas las tablas
band_membersyband_instruments, ¿qué tipo de join deberías hacer para tener una tabla con toda la información, tanto de los miembros como de los instrumentos, aunque haya miembros cuyo instrumento no conozcas, e instrumentos cuyo portador no conozcas?
Code
full_join_band <-
band_members |>
full_join(band_instruments, by = "name")3 🐣 Caso práctico II: renta municipios
En el archivo municipios.csv tenemos guardada la información de los municipios de España a fecha de 2019.
La variable
LAU_coderepresenta el código como unidad administrativa local según la estandarización de la UE (ver más en https://ec.europa.eu/eurostat/web/nuts/local-administrative-units).La variable
codigo_ineestá construida uniendo el código de la provincia y el de la comunidad autónoma.
# datos 2019
mun_data <- read_csv(file = "./datos/municipios.csv")Rows: 8212 Columns: 8
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (8): codauto, ine.ccaa.name, cpro, ine.prov.name, cmun, name, LAU_CODE, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mun_data# A tibble: 8,212 × 8
codauto ine.ccaa.name cpro ine.prov.name cmun name LAU_CODE codigo_ine
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 01 Andalucía 04 Almería 001 Abla 04001 04001
2 01 Andalucía 04 Almería 002 Abrucena 04002 04002
3 01 Andalucía 04 Almería 003 Adra 04003 04003
4 01 Andalucía 04 Almería 004 Albanchez 04004 04004
5 01 Andalucía 04 Almería 005 Alboloduy 04005 04005
6 01 Andalucía 04 Almería 006 Albox 04006 04006
7 01 Andalucía 04 Almería 007 Alcolea 04007 04007
8 01 Andalucía 04 Almería 008 Alcóntar 04008 04008
9 01 Andalucía 04 Almería 009 Alcudia … 04009 04009
10 01 Andalucía 04 Almería 010 Alhabia 04010 04010
# ℹ 8,202 more rows
Por otro lado el archivo renta_mun contiene datos de la renta per capita edmai de cada unidad administrativa (municipios, distritos, provincias, comunidades autonónomas, etc) para diferentes años.
renta_mun <- read_csv(file = "./datos/renta_mun.csv")Warning: One or more parsing issues, call `problems()` on your data frame for details,
e.g.:
dat <- vroom(...)
problems(dat)
Rows: 55273 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): Unidad, codigo_ine
dbl (5): 2019, 2018, 2017, 2016, 2015
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
renta_mun# A tibble: 55,273 × 7
Unidad `2019` `2018` `2017` `2016` `2015` codigo_ine
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 44001 Ababuj NA NA NA NA NA 44001
2 4400101 Ababuj distrito 01 NA NA NA NA NA 4400101
3 4400101001 Ababuj sección 01001 NA NA NA NA NA 4400101001
4 40001 Abades 11429 10731 10314 9816 9904 40001
5 4000101 Abades distrito 01 11429 10731 10314 9816 9904 4000101
6 4000101001 Abades sección 01001 11429 10731 10314 9816 9904 4000101001
7 10001 Abadía 8954 8589 8207 7671 8416 10001
8 1000101 Abadía distrito 01 8954 8589 8207 7671 8416 1000101
9 1000101001 Abadía sección 01001 8954 8589 8207 7671 8416 1000101001
10 27001 Abadín 10791 10258 9762 9478 9116 27001
# ℹ 55,263 more rows
Antes de empezar vamos a normalizar nombres de variables haciendo uso de clean_names() del paquete {janitor}.
mun_data <-
mun_data |>
janitor::clean_names()
renta_mun <-
renta_mun |>
janitor::clean_names()3.1 Pregunta 1
Convierte a tidydata
renta_munobteniendo una tabla de 4 columnas:unidad,year,rentaycodigo_ine(sin ausentes y cada dato del tipo correcto)
Code
renta_mun_tidy <-
renta_mun |>
pivot_longer(cols = contains("x"), names_to = "year",
values_to = "renta", names_prefix = "x",
names_transform = list(year = as.numeric),
values_drop_na = TRUE)3.2 Pregunta 2
Si te fijas en la tabla anterior, tenemos datos de diferentes unidades administrativas que no siempre son municipios. Sabiendo que todos los municipios tienen un código de 5 caracteres (que representan todos ellos números), filtra sólo aquellos registros que correspondan a unidades municipales.
Code
renta_mun_tidy <-
renta_mun_tidy |>
filter(str_detect(codigo_ine, pattern = "[0-9]{5}") &
str_length(codigo_ine) == 5)3.3 Pregunta 3
A continuación separa adecuadamente la variable de unidad en dos columnas: una con el código (que ya tiene, por lo que debe eliminar uno de los dos) y el nombre. Elimina los espacios sobrantes (echa un vistazo a las opciones del paquete
{stringr}).
Code
renta_mun_tidy <-
renta_mun_tidy |>
separate(col = "unidad", into = c("cod_rm", "name"), sep = 5) |>
select(-cod_rm) |>
mutate(name = str_trim(name)) 3.4 Pregunta 4
¿En qué año fue mayor la renta media? ¿Y más baja? ¿Cuál fue la renta mediana de los municipios de España en 2019?
summary_renta <-
renta_mun_tidy |>
summarise("mean_renta" = mean(renta, na.rm = TRUE),
.by = year)
summary_renta |>
slice_min(mean_renta, n = 1)# A tibble: 1 × 2
year mean_renta
<dbl> <dbl>
1 2015 10083.
summary_renta |>
slice_max(mean_renta, n = 1)# A tibble: 1 × 2
year mean_renta
<dbl> <dbl>
1 2019 11672.
renta_mun_tidy |>
filter(year == 2019) |>
summarise("median_renta" = median(renta, na.rm = TRUE))# A tibble: 1 × 1
median_renta
<dbl>
1 11462
3.5 Pregunta 5
Haz lo que consideres para obtener la provincia con la renta media más alta en 2019 y la más baja. Asegúrate de obtener su nombre.
Code
summary_by_prov <-
renta_mun_tidy |>
filter(year == 2019) |>
left_join(mun_data, by = "codigo_ine", suffix = c("", "_rm")) |>
select(-contains("rm")) |>
summarise("mean_by_prov" = mean(renta, na.rm = TRUE),
.by = c("cpro", "ine_prov_name"))
summary_by_prov |>
slice_max(mean_by_prov, n = 1)# A tibble: 1 × 3
cpro ine_prov_name mean_by_prov
<chr> <chr> <dbl>
1 20 Gipuzkoa 15890.
Code
summary_by_prov |>
slice_min(mean_by_prov, n = 1)# A tibble: 1 × 3
cpro ine_prov_name mean_by_prov
<chr> <chr> <dbl>
1 06 Badajoz 8805.
3.6 Pregunta 6
Obten de cada ccaa el nombre del municipio con mayor renta en 2019.
Code
renta_mun_tidy |>
filter(year == 2019) |>
left_join(mun_data, by = "codigo_ine", suffix = c("", "_rm")) |>
select(-contains("rm")) |>
slice_max(renta, by = "codauto")# A tibble: 19 × 10
name codigo_ine year renta codauto ine_ccaa_name cpro ine_prov_name cmun
<chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <chr> <chr>
1 Navas… 40904 2019 20242 07 Castilla y L… 40 Segovia 904
2 Almar… 10019 2019 13940 11 Extremadura 10 Cáceres 019
3 Oleir… 15058 2019 16447 12 Galicia 15 Coruña, A 058
4 Laukiz 48053 2019 21672 16 País Vasco 48 Bizkaia 053
5 Lumbr… 26091 2019 24007 17 Rioja, La 26 Rioja, La 091
6 Murcia 30030 2019 11631 14 Murcia, Regi… 30 Murcia 030
7 Rajad… 08178 2019 23401 09 Cataluña 08 Barcelona 178
8 Alocén 19023 2019 19700 08 Castilla - L… 19 Guadalajara 023
9 Arguis 22037 2019 19219 02 Aragón 22 Huesca 037
10 Alcud… 04009 2019 14196 01 Andalucía 04 Almería 009
11 Arang… 31023 2019 15517 15 Navarra, Com… 31 Navarra 023
12 Santa… 35021 2019 15982 05 Canarias 35 Palmas, Las 021
13 Rocaf… 46216 2019 17872 10 Comunitat Va… 46 Valencia/Val… 216
14 Pozue… 28115 2019 26367 13 Madrid, Comu… 28 Madrid 115
15 Valld… 07063 2019 19565 04 Balears, Ill… 07 Balears, Ill… 063
16 Valde… 39093 2019 15574 06 Cantabria 39 Cantabria 093
17 Tever… 33072 2019 15409 03 Asturias, Pr… 33 Asturias 072
18 Ceuta 51001 2019 12038 18 Ceuta 51 Ceuta 001
19 Melil… 52001 2019 11463 19 Melilla 52 Melilla 001
# ℹ 1 more variable: lau_code <chr>