Co grało radio?

Uwielbiam muzykę, lubię radio (chociaż słucham go właściwie tylko w samochodzie), kiedyś przez chwilę uczyłem się w krakowskich stacjach radiowych realizacji dźwięku. Po Więcej czadu miałem wieloletnią fazę, żeby zostać radiowcem. Radiowcem nie zostałem, ale nad stacjami radiowymi można się trochę poznęcać. Według badania Radio Track Millward Brown (ostatnie zestawienia można znaleźć na Wirtualnych Mediach) najpopularniejsze (top 5) w Polsce stacje radiowe to: RMF FM, Radio ZET, Jedynka, ESKA i Trójka. A że istnieje sobie taki serwis jak Radiospis.pl zawierający listę piosenek granych na antenie z ostatnich siedmiu dni to sobie porównamy kto co gra. Na początek przygotujemy funkcję, która zbierze nam dane ze strony:

library(tidyverse)
library(forcats)
library(stringr)
library(lubridate)
library(rvest)

# lista stacji
playlists_urls <- list(
   stacja = c("RMF FM", "Zet", "Jedynka", "ESKA", "Trójka"),
   url = c("https://radiospis.pl/stacja/rmf-fm",
           "https://radiospis.pl/stacja/zet",
           "https://radiospis.pl/stacja/jedynka",
           "https://radiospis.pl/stacja/eska",
           "https://radiospis.pl/stacja/trojka")) %>%
   transpose()

# funkcja pobierająca playlisty
getPlaylistTable <- function(verbose = TRUE) {
   playlist_tab <- tibble()
   
   # dla każdej stacji
   for(i in seq_len(length(playlists_urls))) {
      
      # bierzemy podstawowy url i nazwę stacji
      page_base_url <- playlists_urls[[i]]$url
      stacja <- playlists_urls[[i]]$stacja
      
      end <- FALSE
      page_no <- 1
      
      # dopóki jest link do następnej strony - przetwarzamy kolejne strony
      while(!end) {
         
         # progress bas :)
         if(verbose)
            cat(sprintf("%s - %02d\n", stacja, page_no))
         
         # wczytujemy stronę
         page_html <- read_html(sprintf("%s/page/%d", page_base_url, page_no))
         
         # data i godzina emisji utworu
         datetime <- page_html %>% html_nodes("div.hentry") %>% html_node("p") %>% html_text() %>% dmy_hm()
         
         # wykonawca i tytuł utwory
         song <- page_html %>% html_nodes("div.hentry") %>% html_node("h2") %>% html_text()
         
         # budujemy tymczasową tabele z wynikow
         temp_tab <- tibble(datetime, song, stacja) %>%
            # rozdzielamy piosenkę na tytul i wykonawce
            rowwise() %>%
            mutate(artist = str_split(song, " - ")[[1]][1],
                   title = str_split(song, " - ")[[1]][2]) %>%
            ungroup()
         
         # dołączamy tymczasowa tabele do pelnych wynikow
         playlist_tab <- bind_rows(playlist_tab, temp_tab)
         
         # czy to koniec? nie ma przycisku "Wcześniej" na stronie
         end <- page_html %>% html_nodes("div.morebtnall") %>% .[[2]] %>% html_text() %>% nchar() == 0
         
         # chwilę ajemy odsapnąć serwerom
         Sys.sleep(0.5)
         page_no <- page_no + 1
      }
   
   }
   
   return(playlist_tab)
}

Teraz wywołamy sobie tę funkcję i zapiszemy dane na później:

playlist_tab <- getPlaylistTable()
saveRDS(playlist_tab, file = "grabbed_data.RDS")

Mając te podstawowe dane możemy zacząć ich przegląd.

Najpopularniejsi wykonawcy według stacji radiowej

playlist_tab %>%
   count(artist, stacja) %>%
   ungroup() %>%
   group_by(stacja) %>%
   top_n(10, n) %>%
   ungroup() %>% 
   ggplot() + 
   geom_tile(aes(stacja, artist, fill = n), color = "gray10") +
   scale_fill_distiller(palette = "YlOrRd") +
   theme(legend.position = "bottom")

Widzimy tutaj dwie ciekawostki:

  • już na pierwszy rzut oka widać, że liczba prostokątów (czyli wystąpienia danego wykonawcy na antenie) jest różna w zależności od stacji. Mówi nam to o zróżnicowaniu w doborze playlisty. ESKA gra kilku wykonawców, Jedynka całe spektrum
  • to co jest grane w Jedynce prawie wcale nie jest grane w innych stacjach. Zaś to co jest grane w Esce jest często grane w RMF FM i Zetce. Trójka podobnie jak Jedynka - jest odrębna.

W sumie to dobrze z punktu widzenia słuchacza, stacje są wyraziste muzycznie. Jeśli podoba mi się muzyka w Jedynce to przy niej zostanę, bo inne stacje grają coś innego. Podobnie z Trójką. Ale większej różnicy między Eską, RMF i Zetką nie ma. Przynajmniej na poziomie doboru najpopularniejszych wykonawców.

Które stacje są muzyczne? W jakich godzinach jest najwięcej muzyki?

playlist_tab %>%
   mutate(hour = hour(datetime), day = wday(datetime)) %>%
   count(stacja, day, hour) %>%
   ungroup() %>%
   group_by(stacja, hour) %>%
   summarise(mean_n = mean(n)) %>%
   ungroup() %>%
   ggplot() +
   geom_col(aes(hour, mean_n, fill = stacja), show.legend = FALSE) +
   facet_wrap(~stacja, ncol = 5)

Za najbardziej muzyczną stację możemy traktować Eskę - tutaj mamy najwięcej odtworzonych piosenek na godzinę. Czy to jest prawda czy też mamy jakiś błąd w danych (około 18 piosenek na godzinę oznacza piosenki trwające średnio po 3 minuty i 20 sekund - typowo radiowe utwory, ale jest to podejrzane - gdzie czas na serwis i głupie gadki prowadzących?) to jeszcze będziemy rozstrzygać (ale nie poprawiać). W każdym razie w jakimś przybliżeniu nasz wykres ma sens - w Jedynce i Trójce więcej jest audycji gadanych (Jedynki nie słucham, Trójkę znam - w środku dnia na pewno więcej gadają niż w paśmie wieczornym koło 19-22; swoją drogą polecam te wieczorne audycje).

Liczba piosenek w sumie

Ile w badanym czasie zagrano piosenek?

playlist_tab %>%
   count(stacja) %>%
   ungroup() %>%
   mutate(n = color_tile("white", "orange")(n)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                 full_width = FALSE)
StacjaLiczba
ESKA1440
Jedynka320
RMF FM1000
Trójka200
Zet1000

Potwierdzają się nasze obserwacje: w Esce jest najwięcej muzyki, w Trójce najmniej. RMF i Zetka od zawsze były wzajemną konkurencją, a tutaj nawet identyczne liczby wyszły… Zobaczmy jak z unikalnością piosenek - czyli ile zagrano piosenek, ale już różnych, jak duża jest biblioteka?

playlist_tab %>%
   distinct(stacja, title, artist) %>%
   count(stacja) %>%
   ungroup() %>%
   mutate(n = color_tile("white", "orange")(n)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                 full_width = FALSE)
StacjaLiczba
ESKA541
Jedynka307
RMF FM608
Trójka165
Zet665

Dla łatwiejszego porównania złóżmy to razem i sprawdźmy jak muzycznie stacje są zróżnicowane (iloraz unikalne/wszystkie):

left_join(
   playlist_tab %>% count(stacja, sort = T) %>% ungroup() %>% rename(total=n),
   playlist_tab %>% distinct(stacja, title, artist) %>% count(stacja, sort = T) %>% ungroup() %>% rename(uniq = n),
   by = "stacja") %>%
   mutate(p = round(100*uniq/total, 1)) %>%
   arrange(desc(p)) %>% 
   mutate(p = color_tile("white", "orange")(p)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                 full_width = FALSE)
StacjaPiosenek łączniePiosenek unikalnychUnikalność
Jedynka32030795.9
Trójka20016582.5
Zet100066566.5
RMF FM100060860.8
ESKA144054137.6

Jedynka gra najbardziej zróżnicowanie (i jeśli komuś odpowiada dobór wykonawców to powinien być zadowolony). Trójka jest na drugim miejscu, ale Trójka zawsze była swego rodzaju popularyzatorem muzyki; to z Trójki w latach ’70 i ’80 ludzie dowiadywali się o nowych płytach, a Lista Przebojów była obrazem popularności. Teraz każda stacja ma swoją listę i jej zawartość jest odbiciem gustu słuchaczy. Według mnie nie ma jednego miejsca które powie jasno, że najbardziej popularną piosenką w Polsce jest Sławomir i “Miłość w Zakopanem” albo cokolwiek innego. Lista Trójki to jedno, lista RMF to drugie, Sylwester w TVP to trzecie, a Spotify to czwarte. Może jakby to wszystko poważyć liczbą słuchaczy/użytkowników to by coś dało… Wróćmy do tego co mamy i zerknijmy na

najpopularniejsze piosenki

według liczby odtworzeń:

most_popular_songs <- playlist_tab %>%
   count(song, sort = T) %>%
   ungroup() %>% 
   .$song %>%
   .[1:9]

playlist_tab %>%
   filter(song %in% most_popular_songs) %>%
   ggplot() +
   geom_point(aes(datetime, stacja, color = stacja), size = 2, show.legend = FALSE) +
   facet_wrap(~song, ncol = 3)

Wzięliśmy 9 najpopularniejszych utworów i jak widać wszystkie 9 swój wynik uzyskały w stacjach grających to samo: Esce, Zetce i RMFie. Zobaczmy jednak najpopularniejsze piosenki według stacji:

playlist_tab %>%
   count(song, stacja, sort = T) %>%
   ungroup() %>%
   group_by(stacja) %>% 
   top_n(5, n) %>% 
   left_join(playlist_tab, by = c("song" = "song", "stacja" = "stacja")) %>% 
   ggplot() + 
   geom_point(aes(datetime, song, color = stacja), show.legend = FALSE) + 
   facet_wrap(~stacja, scales = "free_y", ncol=1)

Tytuły z Trójki i Jedynki powtarzają się w jednym czy dwóch przypadkach. Listy Eski, RMFu i Zetki są mocno wspólne. Widać też, że Eska ma system grania jednej piosenki w kółko przez jeden dzień; taka piosenka dnia to na przykład:Przeanalizujmy to nieco dokładniej:

playlist_tab %>%
   filter(stacja == "ESKA") %>%
   mutate(day = wday(datetime)) %>%
   count(song, day) %>%
   ungroup() %>%
   top_n(10, n) %>%
   arrange(desc(n)) %>%
   mutate(n = color_tile("white", "orange")(n)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                 full_width = FALSE)
PiosenkaDzień tygodniaLiczba
Shanguy - La louze643
Lost Frequencies/zonderling - Crazy730
Liam Payne‚ Rita Ora - For you127
Shanguy - La louze216
Marta Galuszewska - Nie mow mi nie79
Cleo - Lowcy gwiazd68
Lost Frequencies/zonderling - Crazy18
Lost Frequencies/zonderling - Crazy68
C-Bool - Wonderland17
Cleo - Lowcy gwiazd77
Gromee / Lukas Meijer - Light me up67
Liam Payne‚ Rita Ora - For you67
Liam Payne‚ Rita Ora - For you77
Margaret - In my cabana77
Shanguy - La louze17

Pierwsza na liście La louze grana w 6 dzień (czyli w piątek, bo liczymy od niedzieli) tygodnia 43 razy. Druga - Crazy grana w sobotę:Niedzielę zdominowała For you:Te piosenki powtarzają się też w inne dni, ale już nie kilkadziesiąt razy a kilka. Ile razy na godzinę (w piątek) Eska potrafiła zagrać La louze?

playlist_tab %>%
   filter(stacja == "ESKA", song == "Shanguy - La louze") %>%
   mutate(day = wday(datetime), hour = hour(datetime)) %>%
   filter(day == 6) %>%
   count(hour) %>%
   ggplot() +
   geom_col(aes(hour, n), fill = "lightgreen", color = "darkgreen")

Zakładając, że nie ma błędów w danych mamy nawet pięć razy pomiędzy 9:00 a 10:00. O, dokładnie tak wyglądała poranna (6-10) playlista piątku:

playlist_tab %>%
   mutate(day = wday(datetime), hour = hour(datetime)) %>%
   filter(day == 6, stacja == "ESKA", hour %in% c(6, 7, 8)) %>%
   arrange(desc(datetime)) %>%
   mutate(song = fct_inorder(song)) %>%
   ggplot() +
   geom_point(aes(datetime, song))

Zobaczmy to chronologicznie w tabeli:

playlist_tab %>%
   mutate(day = wday(datetime), hour = hour(datetime)) %>%
   filter(day == 6, stacja == "ESKA", hour %in% c(6, 7, 8)) %>%
   arrange(datetime) %>% 
   select(datetime, artist, title) %>%
   mutate(artist = ifelse(artist == "Shanguy",
                          cell_spec(artist, "html", color = "red", bold = T),
                          cell_spec(artist, "html", color = "black")),
          title = ifelse(title == "La louze",
                          cell_spec(title, "html", color = "red", italic = T),
                          cell_spec(title, "html", color = "black"))) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover"))
Dzień i godzina emisjiWykonawcaTytuł
2018-03-09 06:00:00ShanguyLa louze
2018-03-09 06:16:00BandytkaDrogowskazy
2018-03-09 06:31:00ShanguyLa louze
2018-03-09 06:32:00BandytkaDrogowskazy
2018-03-09 06:33:00Lost Frequencies/zonderlingCrazy
2018-03-09 06:35:00Daft Punk‚ Pharrell WilliamsGet lucky
2018-03-09 06:39:00Kaen / Ewa FarnaEcho
2018-03-09 06:43:00ShanguyLa louze
2018-03-09 06:44:00Kaen / Ewa FarnaEcho
2018-03-09 06:46:00SiaMove your body (rmx)
2018-03-09 06:50:00Lanberry‚ FeelGotowi na wszystko
2018-03-09 06:54:00Avicii‚ Aloe BlaccWake me up
2018-03-09 07:03:00ShanguyLa louze
2018-03-09 07:04:00Avicii‚ Aloe BlaccWake me up
2018-03-09 07:05:00Axwell‚ Sebastian IngrossoMore than you know
2018-03-09 07:08:00Tom WalkerLeave a light on
2018-03-09 07:12:00ShanguyLa louze
2018-03-09 07:13:00Tom WalkerLeave a light on
2018-03-09 07:16:00Martin Solveig‚ AlmaAll stars
2018-03-09 07:19:00MargaretIn my cabana
2018-03-09 07:24:00Sylwia GrzeszczakMałe rzeczy
2018-03-09 07:31:00ShanguyLa louze
2018-03-09 07:34:00Merk & KremontSad story (out of lock)
2018-03-09 07:39:00Imagine DragonsBeliever
2018-03-09 07:43:00ShanguyLa louze
2018-03-09 07:44:00Imagine DragonsBeliever
2018-03-09 07:47:00AviciiWaiting for love
2018-03-09 07:51:00CleoLowcy gwiazd
2018-03-09 07:55:00Ed SheeranPerfect
2018-03-09 08:01:00ShanguyLa louze
2018-03-09 08:02:00Ed SheeranPerfect
2018-03-09 08:04:00ShanguyLa louze
2018-03-09 08:05:00Ed SheeranPerfect
2018-03-09 08:06:00Liam Payne‚ Rita OraFor you
2018-03-09 08:10:00Yugopolis‚ Maciej MaleńczukOstatnia nocka
2018-03-09 08:14:00ShanguyLa louze
2018-03-09 08:15:00Yugopolis‚ Maciej MaleńczukOstatnia nocka
2018-03-09 08:18:00Filatov & KarasTime won`t wait
2018-03-09 08:21:00Enrique Iglesias‚ Bad BunnyEl bano
2018-03-09 08:27:00Dawid PodsiadloW dobra strone
2018-03-09 08:33:00ShanguyLa louze
2018-03-09 08:34:00Camila Cabello / Young ThugHavana
2018-03-09 08:38:00Jax Jones‚ Ina WroldsenBreathe
2018-03-09 08:42:00ShanguyLa louze
2018-03-09 08:43:00Jax Jones‚ Ina WroldsenBreathe
2018-03-09 08:46:00Justin TimberlakeCan’t stop the feeling!
2018-03-09 08:50:00Charlie PuthAttention
2018-03-09 08:53:00C-BoolWonderland

Szczerze? Albo kogoś popierdoliło albo rzeczywiście w danych jest bałagan. Nie można (tzn. można, ale jaki to ma sens?) grać jednej piosenki na przemian z jakąś inną. Jedna to nasz hicior dnia (którego nie słyszałem), a druga - Ed Sheeran i Perfet (którego też nie słyszałem) zagrany w okolicach 8 rano. Macie tego Eda, może posłuchacie:To że są embedy niczego nie oznacza ;) Zapewne należałoby posprzątać dane według jakiegoś klucza. Chociażby takiego, że po jednym utworze musi być 10 innych, aby zagrać go ponownie. Kiedyś pracowałem przez chwilę w pewnym lokalnym krakowskim radiu i tam zasada była prosta: piosenka nie może się powtórzyć w ciągu trzech godzin.

Spotify

Wykorzystamy sobie teraz Spotify do przeanalizowania stacji pod kątem parametrów muzycznych (ot, takie hasło uknułem z braku lepszego pomysłu). Na początek potrzebujemy danych identyfikujących naszą aplikację (dostęp do API) na Spotify - trzeba ją założyć na stronie dla developerów, a potem uzyskać tymczasowy token:

library(httr)
library(jsonlite)
library(RCurl)

load("spotify_cred.rda") # zmienne client_id i client_secret - do uzyskania na https://developer.spotify.com/

# funkcja zwraca token
getSpotifyToken <- function() {
   res <- POST("https://accounts.spotify.com/api/token",
               body = list(grant_type = "client_credentials"),
               config = add_headers("Authorization" = paste0("Basic ", base64(paste(client_id,client_secret,sep=':'))[[1]])),
               encode = "form")
   
   if(res$status_code == 200) {
      return(content(res)$access_token)
   } else {
      return(NA)
   }
}

W danych mamy tylko tytuł i wykonawcę utworu, zaś wszystkie informacje o konkretnej piosence (to samo dotyczy wykonawcy czy albumu) pobieramy z API korzystając z ID utworu (analogicznie dla wykonawcy i albumu). Przygotujmy odpowiednią funkcję, która owo ID nam wyszuka (mniej lub bardziej dokładnie):

# funkcja szuka ID utworu na postawie wykonawcy i tytuły
getSpotifyTrackID <- function(track_artist, track_title) {
   
   # na potrzeby zapytania podmieniamy znaki typu spacja
   track_title <- gsub("[^a-zA-Z0-9]", "+", as.character(track_title))
   track_artist <- gsub("[^a-zA-Z0-9]", "+", as.character(track_artist))
   
   # wywołujemy API - wyszukiwanie utworu
   response <- GET(paste0("https://api.spotify.com/v1/search?q=", track_title, "+", track_artist, "&type=track"),
                   add_headers("Authorization:" = paste("Bearer", spotify_token),
                               "Content-Type:" = "application/json",
                               "Accept:" = "application/json"))
   
   # jeśli API coś zwróciło...
   if(response$status_code == 200) {
      
      # parsujemy JSONa z odpowiedzią
      response_json <- rawToChar(response$content)
      query_results <- fromJSON(response_json, flatten = TRUE)

      # jeśli w odpowiedzi jest więcej niż jeden utwór - bierzemy ten najbardziej popularny
      if(query_results$tracks$total != 0) {
         track_id <- query_results$tracks$items %>%
            filter(popularity == max(popularity)) %>% 
            .$id %>% 
            .[[1]]
      } else {
         track_id <- NA
      }
      
      return(track_id)
   } else {
      return(NA)
   }
}

Mając ID utworu możemy zapytać o jego cechy audio:

# Funkcja pobiera cechy audio utworu track_id
getSporifyTrackFeatures <- function(track_id) {
   
   # zapytanie o cechy
   response <- GET(paste0("https://api.spotify.com/v1/audio-features/", as.character(track_id)),
                   add_headers("Authorization:" = paste("Bearer", spotify_token),
                               "Content-Type:" = "application/json",
                               "Accept:" = "application/json"))
   
   # udało się?
   if(response$status_code == 200) {
      
      # parsujemy JSONa, odpowiednio zmieniając kodowanie liter
      # (bez tego całość lubi się wywalić w np. polskim Windowsie)
      response_json <- rawToChar(response$content) %>% iconv(from = "UTF-8", to = Sys.getenv("CHARSET"))
      query_results <- fromJSON(response_json, flatten = TRUE)
      
      # budujemy ramkę (dokładniej - wiersz) danych z cechami
      track_features <- tibble(
         id = query_results$id,
         danceability = query_results$danceability,
         energy = query_results$energy,
         loudness = query_results$loudness,
         mode = query_results$mode,
         speechiness = query_results$speechiness,
         acousticness = query_results$acousticness,
         instrumentalness = query_results$instrumentalness,
         liveness = query_results$liveness,
         valence = query_results$valence,
         tempo = query_results$tempo,
         time_signature = query_results$time_signature)
   } else {
      return(tibble())
   }
   
   return(track_features)
}

W powyższej funkcji możemy wykorzystać inną metodę API i zapytać od razu o maksymalnie 100 utworów. Dla czystości kodu zostawiłem jednak pytanie o każdy utwór z osobna. Dla przypomnienia (w ślad za dokumentacją Spotify oraz moim starym wpisem) poszczególne cechy to:

  • acousticness – czy utwór jest akustyczny? 1.0 to z bardzo dużym prawdopodobieństwem utwory instrumentalne
  • danceability – czy utwór “nadaje się” do tańca; wskaźnik opiera się na połączeniu różnych miar w tym tempa i rytmu. Im wyżej, tym bardziej taneczny kawałek. Na dancingi bierzcie te numery, które są z lat ’70-80 i mają wysokie danceability – powinno się sprawdzić, szczególnie jak doda się do tego filtr na wysoką popularność.
  • energy – zazwyczaj utwory energiczne są szybkie i głośne. Na przykład death metal ma wysoką energię, podczas gdy Bach raczej niską
  • instrumentalness – wskaźnik określający czy utwór zawiera wokale. Wszelakie “oooo” i “aaaaa” są traktowane jako części instrumentalne. Rap lub melodeklamacje są wyraźnie “wokalne”. Im wartość instrumentalness jest bliżej jedynki, tym większe prawdopodobieństwo, że utwór nie zawiera wokali. Wartości powyżej 0.5 mają reprezentować utwory instrumentalne, ale pewność jest większa gdy wartość ta osiąga 1.
  • liveness – wykrywa obecność publiczności w nagraniu. Wyższe wartości liveness reprezentują zwiększone prawdopodobieństwo, że utwór był wykonywany na żywo, przy wartości powyżej 0.8 mamy już nieomal pewność nagrania koncertowego
  • loudness – ogólna głośność utworu w decybelach (dB), wartości przyjmują wartości od -60 od 0 decybeli. Wartość jest uśredniona dla całego utworu, jest silnie skorelowana z amplitudą dźwięku
  • mode to określenie skali w jakiej jest melodia (minor lub major, po polsku odpowiednio molowa i durowa); gdzie skala molowa (minor) to 0, a durowa (major) to 1. Melodie odbierane jako wesołe są zazwyczaj w skali durowej, a smutne - w molowej. Chyba, że coś pokręciłem ;-) (w oryginale oczywiście tak - dzięki za zwrócenie uwagi w komentarzach!)
  • speechiness – obecność wypowiadanych słów w utworze – im więcej słów (na przykład nagrania audiobooków czy poezja śpiewana) tym bliżej do jedynki. Wartości powyżej 0.66 opisują utwory, które prawdopodobnie są wykonane w całości z wypowiadanych słów. Wartości pomiędzy 0.33 i 0.66 opisują utwory, które mogą zawierać zarówno muzykę jak i słowa – na przykład rap. Wartości poniżej 0.33 to najprawdopodobniej utwory instrumentalne
  • tempo – szacunkowe tempo wyrażone w BPM (beats per minute)
  • valence – miara opisująca nastrój; utwory o wysokiej wartości współczynnika brzmią bardziej pozytywne

Mając gotowe funkcje możemy przetworzyć całą bazę zgromadzonych playlist:

# token Spotify
spotify_token <- getSpotifyToken()

# bierzemy unikalne piosenki, żeby nie przetwarzać kilkukrotnie tych samych danych
unique_tracks <- playlist_tab %>% distinct(title, artist)
n_unique_tracks <- nrow(unique_tracks)

# szukamy dla nich ID w Sporify
# na pewno da się to zrobić w purrr-way :)
for(i in 1:n_unique_tracks) {
   cat(sprintf("\r%04d / %d", i, n_unique_tracks))
   unique_tracks[i, "trackID"] <- getSpotifyTrackID(unique_tracks[i, "artist"], unique_tracks[i, "title"])
}

# dla tych, które w Spotify się znalazły szukamy cech
unique_tracks <- filter(unique_tracks, !is.na(trackID))
nrow(unique_tracks)

# purrr way:
# unique_tracks_features <- unique_tracks$trackID %>% map_df(getSporifyTrackFeatures)

# odswiezamy token, gdyby wszystko za długo trwało
spotify_token <- getSpotifyToken()

# szukamy dla unkialnych piosenek ich cech w Spotify
tracks_features <- tibble()
n_unique_tracks <- nrow(unique_tracks)

for(i in 1:n_unique_tracks) {
   cat(sprintf("\r%04d / %d", i, n_unique_tracks))
   
   tmp_tracks_features <- getSporifyTrackFeatures(unique_tracks[i, "trackID"])

   tracks_features <- bind_rows(tracks_features, tmp_tracks_features)
}

# łączymy listę piosenek z cechami
unique_tracks_features <- left_join(unique_tracks,
                                    tracks_features, 
                                    by = c("trackID" = "id")) %>%
   filter(!is.na(energy)) %>%
   distinct()


# łączymy playlistę z cechami
playlist_tab_features <- left_join(playlist_tab,
                                   unique_tracks_features,
                                   by = c("title" = "title",
                                          "artist" = "artist")) %>%
   filter(!is.na(energy))
   

# zapisujemy sobie na później
saveRDS(playlist_tab_features, "playlist_tab_features.RDS")

Mając te wszystkie dane możemy zobaczyć

która stacja jest jaka?

uśredniając wartości cech dla poszczególnych stacji:

playlist_tab_features %>% 
   group_by(stacja) %>% 
   summarise_at(.cols = vars(danceability:tempo), .funs = mean) %>% 
   ungroup() %>%
   gather("feature", "value", -stacja) %>% 
   ggplot() + 
   geom_col(aes(stacja, value, fill = stacja), show.legend = FALSE) + 
   coord_flip() +
   facet_wrap(~feature, scales = "free_x", ncol = 5)

Podsumujmy to co widzimy, razem ze wskazaniem piosenek i stacji które je grają:

  • Jedynka jest najbardziej akustyczna, RMF najmniej
playlist_tab_features %>%
   filter(acousticness == min(acousticness) | acousticness == max(acousticness)) %>% 
   count(song, stacja, acousticness) %>%
   ungroup() %>%
   mutate(acousticness = color_tile("white", "orange")(acousticness)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover"))
PiosenkaStacjaacousticnessn
Avicii‚ Aloe Blacc - Wake me upESKA9.95e-012
Illusion - Kto jest winienTrójka2.61e-052
  • Eska, Zetka i RMF - najbardziej (i mniej więcej równo) taneczne
playlist_tab_features %>%
   filter(danceability == min(danceability) | danceability == max(danceability)) %>% 
   count(song, stacja, danceability) %>%
   ungroup() %>%
   mutate(danceability = color_tile("white", "orange")(danceability)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover"))
PiosenkaStacjadanceabilityn
Kardinal Offishall Feat. Akon - DangerousZet0.9722
The Cranberries - The gloryTrójka0.1611
  • najniższą energię gra Jedynka, zaś największą - Eska
playlist_tab_features %>%
   filter(energy == min(energy) | energy == max(energy)) %>% 
   count(song, stacja, energy) %>%
   ungroup() %>%
   mutate(energy = color_tile("white", "orange")(energy)) %>%
   kable(escape = FALSE) %>%
   kable_styling(bootstrap_options = c("striped", "hover"))
PiosenkaStacjaenergyn
Jennifer Lopez - Let’s get loudRMF FM0.1291
Kombii - AwinionRMF FM0.9861
  • Trójka jest najbardziej instrumentalna, ale nie jest to tak że mamy do czynienia z utworami instrumentalnymi (średnio 0.04 to niewiele)
  • Jedynka i Trójka grają utwory cichsze i jednocześnie bardziej durowe (wartość mode bliższa jedynki)
  • wygląda też na to, że w Esce jest najwięcej przegadanych piosenek

Zobaczmy na koniec jak wygląda rozkład energii (według cech ze Spotify) poszczególnych stacji w ciągu dnia:

playlist_tab_features %>%
   mutate(hour = hour(datetime)) %>%
   group_by(stacja, hour) %>% 
   summarise(m_energy = mean(energy)) %>% 
   ungroup() %>%
   ggplot() + 
   geom_area(aes(hour, m_energy, fill = stacja), alpha = 0.6, show.legend = FALSE) + 
   geom_line(aes(hour, m_energy, color = stacja), size = 2, show.legend = FALSE) + 
   facet_wrap(~stacja, ncol = 5)

Ten dołek w środku dnia w Trójce i mocne spadki w okolicach północy w Jedynce to wynik małej ilości danych. Widać jednak (jak się dobrze przyjrzeć), że rano (od 5 do 10) energia lekko rośnie w większości stacji. Radio stara budzić się ludzi do pracy?