1 Gráfica inicial


En esta primera gráfica, mostramos el ranking de los 70 artistas más escuchados a nivel mundial. Esta visualización nos permite tener una referencia global sobre las tendencias musicales más populares.

Spotify
Spotify

2 Presentación


En los últimos años, la música peruana ha experimentado un crecimiento exponencial en las plataformas de streaming. Spotify, como uno de los líderes en el mercado, nos brinda una ventana única para analizar las preferencias musicales de los oyentes peruanos. Este análisis se basará en un estudio de los 50 artistas peruanos más escuchados en la plataforma, según datos de Spotify. A través de esta visualización, exploraremos las tendencias actuales en la música peruana, identificando a los artistas más populares y los géneros que dominan el panorama musical nacional.

Artistas Peruanos
Artistas Peruanos

3 Identificación de géneros principales


df_peru %>%
  count(genre) %>%  
  slice_max(n, n = 20) %>%  
  mutate(genre = fct_reorder(genre, n, .desc = FALSE)) %>%  
  ggplot(aes(x = genre, y = n)) +  
  geom_bar(stat = "identity", aes(fill = genre %in% c("cumbia", "rock")), show.legend = FALSE) +  
  scale_fill_manual(values = c("grey", "#1db954")) +  # 'red' for highlighted genres
  labs(x = "Género", y = "Frecuencia", title = "Top 20 géneros más frecuentes en Perú") + 
  theme_classic() + 
  coord_flip() 

4 Popularidad vs. Seguidores (Top 70)


La popularidad de los artistas y su base de seguidores son métricas clave para entender el alcance de la música peruana, tanto a nivel local como internacional. En esta gráfica, analizamos cómo estos dos factores se relacionan, enfocándonos en los 70 artistas más populares, pero también destacando a aquellos con menor reconocimiento (‘Otros’).

ggplot(df_plot, aes(x = popularity , y = followers.total, color = genre)) +
  geom_point(size = 4, alpha = 0.8) + scale_y_log10() +
  labs(
    title = "Popularidad vs Seguidores",
    x = "Popularidad",
    y = "Número de seguidores",
    color = "Género"
  ) +
  theme_bw()

5 Popularidad y seguidores con enfoque en Cumbia y Rock


  1. Cumbia: Destaca cómo la cumbia, un género profundamente arraigado en la cultura peruana, tiene una representación significativa entre los artistas más populares. Esto refleja su capacidad de conectar con una audiencia amplia y diversa.

  2. Rock: Aunque el rock tiene menor representación en el top 70, con solo dos artistas destacados, su presencia subraya la relevancia de este género en el Perú. Estos artistas tienen características específicas que les permiten sobresalir en un entorno dominado por otros estilos

ggplot(df_plot, aes(x = popularity , y = followers.total, color = genre)) +
  geom_point(size = 4, alpha = 0.8) +  # Se añaden los puntos con tamaño y opacidad especificados
  scale_y_log10() +  # Se aplica una escala logarítmica al eje Y (seguidores)
  labs(
    title = "Popularidad vs Seguidores",  # Título del gráfico
    x = "Popularidad",  # Etiqueta para el eje X
    y = "Numero de seguidores",  # Etiqueta para el eje Y
    color = "Género"  # Etiqueta para la leyenda de color
  ) +
  theme_bw() +  # Aplica el tema blanco y negro
  gghighlight(genre %in% c("cumbia", "rock"), use_direct_label = FALSE)  # Resalta los géneros 'cumbia' y 'rock'

Este análisis revela que los géneros más populares, como la cumbia y el rock, no solo dominan en frecuencia, sino que también logran establecer una fuerte conexión con su audiencia, reflejada en su número de seguidores y popularidad. Esto subraya la riqueza cultural de la música peruana y su capacidad de resonar tanto a nivel local como internacional.

6 Diversidad de géneros musicales en la cultura peruana


La música es un reflejo de la cultura y la identidad de un pueblo. En el caso de Perú, su rica diversidad cultural se manifiesta en una escena musical vibrante y en constante evolución. Este análisis visual, basado en datos de Spotify, nos permitirá adentrarnos en el corazón de la música peruana actual. Exploraremos cuáles son los artistas que están conquistando los oídos de millones de peruanos y cómo sus estilos musicales están moldeando el panorama sonoro del país.

Spotify
Spotify

7 Conclusión


En resumen, este gráfico no solo muestra datos sobre el consumo musical en Perú, sino que también cuenta una historia sobre identidad cultural y cómo los peruanos conectan con sus raíces a través de la música.

# lectura y procesamiento de datos
# lectura y procesamiento de datos
# lectura y procesamiento de datos

df_artists = read.csv("arperu.csv")

# Normalización de los nombres de los artistas, simplificando algunos nombres para mejorar la presentación
df_artists <- df_artists %>%
  mutate(name = case_when(
    name == "Orquesta Caribeños de Guadalupe" ~ "Caribeños",
    name == "Orquesta Kaliente de Iquitos" ~ "Orquesta Kaliente",
    name == "El Lobo Y La Sociedad Privada" ~ "El Lobo",
    name == "Marisol y La Magia Del Norte" ~ "Marisol",
    name == "Los Villacorta Orquesta" ~ "Los Villacorta",
    name == "Claveles de la Cumbia" ~ "Los Claveles",
    name == "Cliver y su Grupo Coralí" ~ "Grupo Coralí",
    name == "Yarita Lizeth Yanarico" ~ "Yarita Lizeth",
    name == "Combinacion De La Habana" ~ "Combinacion",
    name == "Azucena Calvay y Orquesta" ~ "Azucena Calvay",
    name == "Rossy War y Su Banda Kaliente" ~ "Rossy War",
    name == "Barbaro Fines y su Mayimbe" ~ "Mayimbe",
    name == "Internacional Mallanep" ~ "Mallanep",
    name == "Orquesta Clavito y su Chela" ~ "Orquesta Clavito",
    name == "Walther Lozada y Orquesta" ~ "Walther Lozada",
    name == "ZAPEROKO La Resistencia Salsera del Callao" ~ "Zaperoko",
    name == "Arturo \"Zambo\" Cavero" ~ "Zambo Cavero",
    TRUE ~ name # Deja los nombres restantes sin cambios
    

df_sub = df_artists %>%
  arrange(-popularity) %>%       # Ordena el dataframe `df_artists` por la columna `popularity` en orden descendente.
  head(100) %>%                  # Toma las 100 primeras filas (artistas) de este dataframe ordenado.
  mutate(circle = cropcircles::circle_crop(image_url)) # Crea una nueva columna llamada `circle` en `df_sub` que contiene la imagen cortada en forma de círculo usando la función `circle_crop()` del paquete `cropcircles` con la URL de la imagen (`image_url`).



border <- function(im) {                # Define una nueva función llamada `border` que toma un argumento `im` (una imagen).
  ii <- magick::image_info(im)          # Obtiene la información de la imagen usando la función `image_info()` del paquete `magick`, y guarda esta información en el objeto `ii`.
  ii_min <- min(ii$width, ii$height)    # Calcula el valor mínimo entre el ancho (`ii$width`) y la altura (`ii$height`) de la imagen, y lo guarda en `ii_min`.

  
  # Crear una imagen en blanco del tamaño mínimo de ancho/alto
  img <- image_blank(width = ii_min, height = ii_min, color = "none")
  drawing <- image_draw(img)
  
  # Dibujar un círculo blanco como borde
  symbols(ii_min/2, ii_min/2, circles = ii_min/2, bg = 'white', inches = FALSE, add = TRUE)
  dev.off()
  
  # Combina la imagen original con el borde
  x = image_composite(image_scale(drawing, "x430"), image_scale(im, "x400"), offset = "+15+15")
  x
}
plot_image_label <- function(image, label, font_color="black", top_bottom="top", hjust=0.2) {
  # Define la trayectoria del texto alrededor de la imagen
  t = seq(0, 1, length.out = 100) * pi * (5/4)

  # Establece la posición del texto (arriba o abajo)
  if (top_bottom == "top") { 
    data = data.frame(x = cos(t), y = sin(t)) 
    vjust = 1.1 
    ymax = 1.4 
    ymin = -0.96 
  } else if (top_bottom == "bottom") { 
    data = data.frame(x = cos(t), y = sin(t) * -1)
    vjust = -0.1 
    ymax = 1 
    ymin = -1.4 
  } 
  ggplot() +
    geom_image(aes(x = 0, y = 0, image = image), asp = 2.4/2.1, size = .7, image_fun = border) +
    scale_x_continuous(limits = c(-1.35, 1.35)) +
    scale_y_continuous(limits = c(ymin, ymax)) +
    geom_textpath(data = data, aes(x, y, label = toupper(label)), 
                  linecolor = NA, color = font_color, size = 14.5, fontface = "bold", 
                  vjust = vjust, hjust = hjust) +
    coord_equal() +
    theme_void() # Elimina los ejes y fondos
}
# Generacion de imagenes circulares con titulos
# Generacion de imagenes circulares con titulos
# Generacion de imagenes circulares con titulos
# Generacion de imagenes circulares con titulos


df_sub <- df_sub %>%                                         # Se toma el dataframe `df_sub` y se le asignan nuevas transformaciones.
  mutate(new_image_path = paste0(                             # Se crea una nueva columna llamada `new_image_path` usando la función `mutate()`.
    tolower(str_replace_all(name, "[^[:alnum:] ]", "")),       # Convierte el nombre de cada artista (columna `name`) a minúsculas y elimina cualquier carácter no alfanumérico ni espacio, usando `str_replace_all()`.
    ".png"                                                     # Luego, le añade la extensión ".png" al final de cada nombre procesado.
  )) %>%                                                       # El resultado de la operación anterior es asignado a la columna `new_image_path`.
  mutate(new_image_path = str_replace_all(new_image_path, " ", "_")) # En esta segunda mutación, reemplaza los espacios en los nombres de archivo por guiones bajos (`_`) para evitar problemas con rutas de archivo.
  
list_hjust = seq(0, 1, by = 0.1)
for (i in 1:nrow(df_sub)) {
  pos = sample(c("top", "bottom"), 1) # Posición del texto aleatoriamente arriba o abajo
  hjust = sample(list_hjust, 1) # Alineación horizontal aleatoria
  path = df_sub$new_image_path[i]
  
  # Generación del gráfico con la función personalizada
  plot = plot_image_label(
    image = df_sub$circle[i],
    label = df_sub$name[i],
    font_color = "#fada4b", # Color de la fuente
    top_bottom = pos, 
    hjust = hjust
  )
  
  # Guardar la imagen generada
  ggsave(filename = glue("{ruta}/imagenes/{path}"), plot = plot, height = 3.95, width = 4.5)
}
# Funcion subtitulos de nombres
# Funcion subtitulos de nombres
# Funcion subtitulos de nombres

social_caption<-function(es_1 ="Lucia Carbajal",
                         es_2 = "Nicholas Espinoza",
                         es_3 = NA, 
                         es_4 = NA,
                         es_5 = NA,
                         icon_color="black",
                         font_color="black",
                         bg_color="white",
                         font_family="Roboto"){
  
  icons = list(
    es_1 = "&#xf007",
    es_2 = "&#xf007",
    es_3 = "&#xf007",
    es_4 = "&#xf007",
    es_5 = "&#xf007"
  )  
  
  social = list(es_1 =es_1, es_2 =es_2, es_3 =es_3,es_4 =es_4, es_5 =es_5)
  social = social[!is.na(social)]
  
  caption = ""
  for (name in names(social)){
    icon = icons[name]
    info = social[name]
    html = glue("<span style='font-family:\"Font Awesome 6 Brands\";color:{icon_color};'>{icon};</span><span style='color:{bg_color};'>.</span><span style='font-family:{font_family};color:{font_color};'>{info}</span><span style='color:{bg_color};'>..</span>")
    caption = paste0(caption,html)
  }
  
  caption
}
# Generacion de grafica final
# Generacion de grafica final
# Generacion de grafica final
# Generacion de grafica final

sysfonts::font_add_google("Chivo","Chivo") # Fuente de Google Fonts
sysfonts::font_add("Gotham", regular = "fonts/Gotham-Light.otf", bold="fonts/Gotham-Bold.otf") # Fuente local Gotham
sysfonts::font_add('Font Awesome 6 Brands', 'fonts/FontAwesome.otf') # Fuente de iconos
showtext_auto() # Activar uso automático de showtext
showtext_opts(dpi=300) # Configuración de DPI para mejor calidad de texto

df_sub <- df_artists %>%
  arrange(-popularity) %>% head(50) %>%
  mutate(path = paste0(
    tolower(str_replace_all(name, "[^[:alnum:] ]", "")), # Elimina caracteres no alfanuméricos
    ".png"
  )) %>%
  mutate(path = str_replace_all(path, " ", "_")) # Reemplaza espacios por guiones bajos

#plot title, subtitle, and caption for ggtext (HTML/CSS)
title = "<span style='font-family:Gotham;color:white;font-size:35pt;'><span style='font-family:\"Font Awesome 6 Brands\";color:#1DB954;font-size:35pt;'>&#xf1bc;</span> *Artistas <span style='color:#F7D22F;'>Peruanos</span> más escuchados en Spotify*</span>"
subtitle = "<p>Top 50 artistas del Perú más escuchados. La popularidad está determinada por Spotify, en una escala de 0 a 100.</p>"
caption = paste0("<span>*Source: {spotifyr}*<span><br>",
                 social_caption(icon_color ="#1DB954", bg_color="black", font_color = "#D7DDDD",
                                font_family="sans",
                                es_3="Jarry Tafur", es_4="Fabricio Barrientos", es_5 = "Luis Arce"))


#create positional arguments (similar to beeswarm)
ruta <- setwd(dirname(rstudioapi::getActiveDocumentContext()$path))  # Establece la ruta de trabajo al directorio donde está el script actual.
df_plot <- df_sub %>%  # Toma el subconjunto df_sub como entrada.
  select(name, popularity, path) %>%  # Selecciona las columnas relevantes: nombre, popularidad y ruta de la imagen.
  group_by(popularity) %>%  # Agrupa por la columna popularity para calcular valores por grupo.
  mutate(
    group_count = n(),  # Calcula el número de artistas dentro de cada grupo de popularidad.
    row = row_number() - 1,  # Asigna un número a cada fila dentro del grupo, comenzando desde 0.
    type = case_when(  # Clasifica los grupos en even (pares) o odd (impares) según su tamaño.
      group_count %% 2 == 0 ~ "even",
      TRUE ~ "odd"
    ),
    spacer = case_when(  # Define el espaciado entre las imágenes según el rango de popularidad.
      popularity > 65 ~ 1.7, 
      popularity >= 60 ~ 1.35, 
      group_count < 15 ~ 0.9, 
      TRUE ~ 0.9
    ),
    max = 0 - ((group_count / 2) - 0.7) * spacer,  # Calcula la posición máxima inicial para distribuir las imágenes.
    pos = max + spacer * row  # Asigna la posición horizontal de cada imagen.
  ) %>%
  mutate(image = glue("{ruta}/imagenes/{path}"))  # Construye la ruta completa de la imagen para cada artista.


#plot - use geom image to plot images
ggplot() +  # Inicia un objeto ggplot vacío.
  geom_image(  # Agrega imágenes para artistas con popularidad igual a 67.
    data = df_plot |> filter(popularity == 67),  # Filtra los datos para este rango de popularidad.
    aes(y = popularity, x = pos, image = image),  # Asocia las posiciones (x, y) y la imagen al gráfico.
    position = position_jitter(width = 0, height = 0.1),  # Aplica un pequeño desplazamiento vertical aleatorio.
    size = 0.1,  # Tamaño de las imágenes.
    asp = 9.5 / 6  # Relación de aspecto para mantener proporciones.
  ) +
  geom_image(  # Agrega imágenes para popularidad entre 61 y 66.
    data = df_plot |> filter(popularity >= 61 & popularity < 67),
    aes(y = popularity, x = pos, image = image),
    position = position_jitter(width = 0, height = 0.1),
    size = 0.08,
    asp = 9.5 / 6
  ) +
  geom_image(  # Agrega imágenes para popularidad entre 56 y 60.
    data = df_plot |> filter(popularity > 55 & popularity < 61),
    aes(y = popularity, x = pos, image = image),
    position = position_jitter(width = 0, height = 0.12),
    size = 0.065,
    asp = 9.5 / 6
  ) +
  geom_image(  # Agrega imágenes para popularidad menor a 55.
    data = df_plot |> filter(popularity < 55),
    aes(y = popularity, x = pos, image = image),
    position = position_jitter(width = 0, height = 0.12),
    size = 0.05,
    asp = 9.5 / 6
  ) +
  coord_flip() +  # Intercambia los ejes (horizontal y vertical).
  scale_x_continuous(limits = c(-5.8, 5.8), expand = c(0, 0)) +  # Configura los límites del eje x sin margen adicional.
  labs(  # Agrega etiquetas al gráfico.
    title = title,  # Define el título del gráfico.
    subtitle = subtitle,  # Define el subtítulo.
    caption = caption  # Define el pie de gráfico.
  ) +
  theme(  # Personaliza el estilo del gráfico.
    panel.background = element_blank(),  # Elimina el fondo del panel.
    plot.background = element_rect(fill = "black", color = "black"),  # Establece un fondo negro para el gráfico.
    plot.title = element_textbox_simple(),  # Aplica estilo al título.
    plot.subtitle = element_textbox_simple(color = "#D7DDDD", size = 15, margin = margin(t = 8, b = 10)),  # Estilo del subtítulo.
    plot.caption = element_textbox_simple(color = "#D7DDDD", margin = margin(t = 15), size = 12),  # Estilo del pie de gráfico.
    plot.margin = margin(t = 20, r = 20, l = 20, b = 10),  # Define los márgenes del gráfico.
    text = element_text(color = "white"),  # Aplica color blanco al texto.
    panel.grid = element_blank(),  # Elimina las cuadrículas internas.
    panel.grid.major.x = element_line(color = "#178F41", linewidth = 0.25),  # Agrega líneas de cuadrícula verde al eje x.
    axis.text.y = element_blank(),  # Oculta las etiquetas del eje y.
    axis.text.x = element_text(family = "Chivo", color = "white", size = 12),  # Estiliza las etiquetas del eje x.
    axis.ticks = element_blank(),  # Oculta las marcas de las escalas en ambos ejes.
    axis.title = element_blank()  # Oculta los títulos de los ejes.
  )


factor = 19 / 14  #La proporción deseada entre la altura y el ancho del gráfico es 19:14
ggsave(
  filename = file.path(ruta, "popular-artistsA.png"),  # Especifica la ruta y el nombre del archivo de salida.
  plot = last_plot(),  # Guarda el último gráfico creado.
  height = 14 / factor, width = 14, unit = "in"  # Define las dimensiones del archivo guardado en pulgadas.
)