Uniones entre tablas

Función merge

Hay dos formas de escribir uniones entre tablas usando data.table. La primera es mediante la función merge() de r base. Los argumentos básicos de esta función son:

merge(x, y, by = intersect(names(x), names(y)),
      by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,
      sort = TRUE, suffixes = c(".x",".y"), no.dups = TRUE,
      incomparables = NULL, ...)

x, y = son los objetos data.table que se quieren unir

by, by.x, by.y = nombres de las columnas que funcionan como índice para hacer la unión. Cuando ambas tablas tienen el mismo nombre de columna se utiliza by, cuando no, se puede especificar el nombre en x (by.x) y el nombre en y (by.y)

all, all.x, all.y = especifican el tipo de unión. all = T es para hacer full join (mantiene todas las filas de ambas tablas), all.x = T es para hacer left join (mantiene todas las filas de la tabla x), all.y es para hacer right join (mantiene todas las filas de y), all = F es para hacer inner join (solo mantiene las filas que están en ambas tablas)

library(data.table)

dt1 = data.table(id = seq(1,10), letter1 = LETTERS[sample(1:10, replace = T)])

dt2 = data.table(id = seq(6,15), letter2 = LETTERS[sample(1:10, replace = T)])

dt1
       id letter1
    <int>  <char>
 1:     1       J
 2:     2       B
 3:     3       D
 4:     4       I
 5:     5       F
 6:     6       E
 7:     7       B
 8:     8       J
 9:     9       A
10:    10       B
dt2
       id letter2
    <int>  <char>
 1:     6       C
 2:     7       H
 3:     8       C
 4:     9       I
 5:    10       D
 6:    11       G
 7:    12       G
 8:    13       I
 9:    14       D
10:    15       A
# inner join
merge(dt1,dt2,by = "id")
Key: <id>
      id letter1 letter2
   <int>  <char>  <char>
1:     6       E       C
2:     7       B       H
3:     8       J       C
4:     9       A       I
5:    10       B       D
# left join
merge(dt1,dt2,by = "id", all.x = T)
Key: <id>
       id letter1 letter2
    <int>  <char>  <char>
 1:     1       J    <NA>
 2:     2       B    <NA>
 3:     3       D    <NA>
 4:     4       I    <NA>
 5:     5       F    <NA>
 6:     6       E       C
 7:     7       B       H
 8:     8       J       C
 9:     9       A       I
10:    10       B       D
# right join
merge(dt1,dt2,by = "id", all.y = T)
Key: <id>
       id letter1 letter2
    <int>  <char>  <char>
 1:     6       E       C
 2:     7       B       H
 3:     8       J       C
 4:     9       A       I
 5:    10       B       D
 6:    11    <NA>       G
 7:    12    <NA>       G
 8:    13    <NA>       I
 9:    14    <NA>       D
10:    15    <NA>       A
# full join
merge(dt1,dt2,by = "id", all = T)
Key: <id>
       id letter1 letter2
    <int>  <char>  <char>
 1:     1       J    <NA>
 2:     2       B    <NA>
 3:     3       D    <NA>
 4:     4       I    <NA>
 5:     5       F    <NA>
 6:     6       E       C
 7:     7       B       H
 8:     8       J       C
 9:     9       A       I
10:    10       B       D
11:    11    <NA>       G
12:    12    <NA>       G
13:    13    <NA>       I
14:    14    <NA>       D
15:    15    <NA>       A

Sintaxis de data.table

La segunda forma de hacer uniones entre tablas es usando la sintaxis de data.table.

DT1[DT2, nomatch = 0]

Si lo traducimos a la sintaxis de la función anterior entonces la tabla que está por fuera es x, la tabla que está por dentro es y y el argumento nomatch te permite indicar qué hacer con las claves para las que no encuentra coincidencia en ambas tablas. Cuando nomatch = 0 no incluye las claves que no se comparten entre tablas.

# inner join
dt1[dt2, on = "id", nomatch=0]
      id letter1 letter2
   <int>  <char>  <char>
1:     6       E       C
2:     7       B       H
3:     8       J       C
4:     9       A       I
5:    10       B       D
# left join
dt1[dt2, on = "id"]
       id letter1 letter2
    <int>  <char>  <char>
 1:     6       E       C
 2:     7       B       H
 3:     8       J       C
 4:     9       A       I
 5:    10       B       D
 6:    11    <NA>       G
 7:    12    <NA>       G
 8:    13    <NA>       I
 9:    14    <NA>       D
10:    15    <NA>       A
# right join
dt2[dt1, on = "id"]
       id letter2 letter1
    <int>  <char>  <char>
 1:     1    <NA>       J
 2:     2    <NA>       B
 3:     3    <NA>       D
 4:     4    <NA>       I
 5:     5    <NA>       F
 6:     6       C       E
 7:     7       H       B
 8:     8       C       J
 9:     9       I       A
10:    10       D       B

Ejercicio

Para el siguiente ejercicio vamos a cargar varias tablas. Representan la información de muchas canciones de Spotify. A diferencia de la tabla que hemos estado utilizando estas canciones son de musica electrónica de la compañía Beatport. Las tablas fueron descargadas de la siguiente página

Las tablas se relacionan de la siguiente manera:

Nota: El objeto de tracks tiene 10M+ de filas, pesa 2.6 GB cuando está cargado en memoria. Si tu computadora tiene poca memoria puedes elegir no juntar todos los archivos de las canciones (por ejemplo tracks <- do.call(rbind,data_files[5:6]) para solo cargar dos de los 4 archivos)

# Enlistar los archivos de canciones (guardados en la carpeta de tracks)
tracks <- list.files("data/bd/tracks", full.names = T)

# Enlistar los archivos restantes en la carpeta bd
files <- c(list.files("data/bd", full.names = T, pattern = ".csv*"), tracks)

# Leer los archivos usando fread
data_files <- lapply(files,fread)
names(data_files) <- c(list.files("data/bd", pattern = ".csv*"), list.files("data/bd/tracks"))

# Guardar los datos en objetos diferentes
genre <- data_files$bp_genre.csv
subgenre <- data_files$bp_subgenre.csv
artist <- data_files$bp_artist.csv.gz
artist_track <- data_files$bp_artist_track.csv.gz
tracks <- do.call(rbind,data_files[5:8])

# Borrar la lista de archivos
rm(data_files)

Nos gustaría explorar la distribución de canciones de diferentes géneros a lo largo del tiempo. Para esto primero tenemos que unir las tablas tracks, genre y subgenre. En el diagrama puedes ver las columnas que las unen.

Code
# Union de tablas
tracks_genre <- merge(tracks,genre[,.(genre_id,genre_name)],by = "genre_id", all.x = T) 

# Visualiza el número de canciones por genero que se publicaron cada año (release_date) del 2000 al 2024

freq_tracks <- tracks_genre[,.N,by = .(genre_name, Yr = format(release_date,"%Y"))]

# Opcional: 
labels <- freq_tracks[, .SD[which.max(N)], by = genre_name][order(-N)][1:5]

colors <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "gray")
names(colors) <- c(labels$genre_name, "other")

freq_tracks[,color := ifelse(genre_name %in% labels$genre_name, genre_name, "other")] 

freq_tracks %>% 
      ggplot(aes(x = as.numeric(Yr), y = N, group = genre_name))+
      geom_line(color = "gray", size = 1, alpha = 0.8)+
      geom_line(data = freq_tracks[genre_name %in% labels$genre_name], aes(color = factor(color, levels = c(labels$genre_name, "other"))), size = 2)+
      geom_text_repel(data = labels, aes(label = genre_name, color = factor(genre_name, levels = c(labels$genre_name, "other"))), nudge_y= 500)+
      scale_color_manual(values = colors)+
      scale_x_continuous(limits = c(2000,2025))+
      labs(x = "Year", y = "N songs", color = "Subgenre")+
      theme_bw()

Genera una gráfica similar en la que muestres la distribución de los 5 subgéneros con más canciones en el tiempo.