You were given a dataset of Top 200 Weekly Global charts of Spotify. By using data analytics skills, you are asked to find

  1. insights from the interrelationship between attributes

  2. the key attributes that have the highest influence on the number of streams, and

  3. managerial implications for song producers who wish to create a phenomenal song on spotify.

Membaca Dataset

library(tidyverse)
library(dplyr)
library(tidyr)
library(reactable)
df <- read.csv("spotify_dataset.csv")
reactable(head(df))
str(df)
## 'data.frame':    1556 obs. of  23 variables:
##  $ Index                    : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Highest.Charting.Position: int  1 2 1 3 5 1 3 2 3 8 ...
##  $ Number.of.Times.Charted  : int  8 3 11 5 1 18 16 10 8 10 ...
##  $ Week.of.Highest.Charting : chr  "2021-07-23--2021-07-30" "2021-07-23--2021-07-30" "2021-06-25--2021-07-02" "2021-07-02--2021-07-09" ...
##  $ Song.Name                : chr  "Beggin'" "STAY (with Justin Bieber)" "good 4 u" "Bad Habits" ...
##  $ Streams                  : chr  "48,633,449" "47,248,719" "40,162,559" "37,799,456" ...
##  $ Artist                   : chr  "MÃ¥neskin" "The Kid LAROI" "Olivia Rodrigo" "Ed Sheeran" ...
##  $ Artist.Followers         : int  3377762 2230022 6266514 83293380 5473565 5473565 8640063 6080597 36142273 3377762 ...
##  $ Song.ID                  : chr  "3Wrjm47oTz2sjIgck11l5e" "5HCyWlXZPP0y6Gqq8TgA20" "4ZtFanR9U6ndgddUvNcjcG" "6PQ88X9TkUIAUIZJHW2upE" ...
##  $ Genre                    : chr  "['indie rock italiano', 'italian pop']" "['australian hip hop']" "['pop']" "['pop', 'uk pop']" ...
##  $ Release.Date             : chr  "2017-12-08" "2021-07-09" "2021-05-21" "2021-06-25" ...
##  $ Weeks.Charted            : chr  "2021-07-23--2021-07-30\n2021-07-16--2021-07-23\n2021-07-09--2021-07-16\n2021-07-02--2021-07-09\n2021-06-25--202"| __truncated__ "2021-07-23--2021-07-30\n2021-07-16--2021-07-23\n2021-07-09--2021-07-16" "2021-07-23--2021-07-30\n2021-07-16--2021-07-23\n2021-07-09--2021-07-16\n2021-07-02--2021-07-09\n2021-06-25--202"| __truncated__ "2021-07-23--2021-07-30\n2021-07-16--2021-07-23\n2021-07-09--2021-07-16\n2021-07-02--2021-07-09\n2021-06-25--2021-07-02" ...
##  $ Popularity               : int  100 99 99 98 96 97 94 95 96 95 ...
##  $ Danceability             : num  0.714 0.591 0.563 0.808 0.736 0.61 0.762 0.78 0.644 0.75 ...
##  $ Energy                   : num  0.8 0.764 0.664 0.897 0.704 0.508 0.701 0.718 0.648 0.608 ...
##  $ Loudness                 : num  -4.81 -5.48 -5.04 -3.71 -7.41 ...
##  $ Speechiness              : num  0.0504 0.0483 0.154 0.0348 0.0615 0.152 0.0286 0.0506 0.118 0.0387 ...
##  $ Acousticness             : num  0.127 0.0383 0.335 0.0469 0.0203 0.297 0.235 0.31 0.276 0.00165 ...
##  $ Liveness                 : num  0.359 0.103 0.0849 0.364 0.0501 0.384 0.123 0.0932 0.135 0.178 ...
##  $ Tempo                    : num  134 170 167 126 150 ...
##  $ Duration..ms.            : int  211560 141806 178147 231041 212000 137876 208867 199604 206710 173347 ...
##  $ Valence                  : num  0.589 0.478 0.688 0.591 0.894 0.758 0.742 0.342 0.44 0.958 ...
##  $ Chord                    : chr  "B" "C#/Db" "A" "B" ...

Remove NA

Menghapus NA atau missing values yang terdapat pada dataset

df <- na.omit(df)
dim(df)
## [1] 1545   23

Week.of.Highest.Charting masih memiliki – diantara kedua tanggal sehingga nantinya tidak bisa dilakukan analisis berdasarkan time series, untuk itu kita harus menghilangkan tanda “–” dan memindahkan data tersebut di dua kolom berbeda, pada kolom Charting.Starts dan Charting.Ends

reactable(head(df[,c("Week.of.Highest.Charting", "Release.Date")]))
# Seperate "--" from "Week.of.Highest.Charting"
chart_week <- df %>% select(Week.of.Highest.Charting) %>% 
  mutate(Week.of.Highest.Charting = str_split_fixed(Week.of.Highest.Charting, "--", 2)) 
chart_week <- as.data.frame(chart_week$Week.of.Highest.Charting)
colnames(chart_week) <- c("Charting.Starts", "Charting.Ends")
chart_week$Index <- seq.int(nrow(chart_week))
df <- right_join(x = df, y = chart_week, by = "Index")

Date Formatting

Lalu akan dilakukan perubahan format dari kolom Release.Date, Charting.Starts dan Charting.Ends ke dalam tipe date.

library(lubridate)
df <- df %>% 
  mutate(Release.Date = ymd(Release.Date),
         Charting.Starts = ymd(Charting.Starts),
         Charting.Ends = ymd(Charting.Ends)) %>% 
  select(-Week.of.Highest.Charting)
df %>% head() %>% reactable()

String Cleansing

Lalu data Streams masih memiliki koma (,) di dalamnya yang juga harus dihilangkan. Lalu data streams yang sudah berupa angka tersebut akan di ubah menjadi tipe integer/numerik sehingga nantinya bisa diinterpretasi.

df$Streams <- str_remove_all(string = df$Streams, pattern = ",")
df <- df %>% mutate(Streams = as.integer(Streams))

insights from the interrelationship between attributes

Mencari korelasi di setiap atribut pada musik menggunakan metode pearson dan didapatkan beberapa insight dari data tersebut:

library(GGally)
df %>% select_if(is.numeric) %>% ggcorr(label = T, hjust = 1, label_size = 2)

Scatter plot

Berikut adalah scatter plot dari 3 data atribut

Berikut adalah scatter plot dari Energy dan Loudness dari sebuah lagu, di dapat bahwa semakin tinggi level energi dari sebuah lagu maka kebisingan juga meningkat

df %>% 
  ggplot(aes(Energy, Loudness)) +
  geom_point()

Berikut adalah scatter plot dari Acousticness dan Energy. di dapat bahwa Semakin tinggi acousticness dari sebuah lagu maka lebel energy nya menurun,

df %>% 
  ggplot(aes(Energy, Acousticness)) +
  geom_point()

Berikut adalah scatter plot dari Loudness dan Acousticness. hampir sama seperti sebelumnya, acousticness memiliki korelasi negatif dengan kebisingan, semakin tinggi tingkat akustik dari sebuah lagu maka kebisingan daru lagu itu menurun.

df %>% 
  ggplot(aes(Loudness, Acousticness)) +
  geom_point()

The Key attributes that have the highest influence on the number of streams

Berikut adalah pengaruh beberapa atribut pada jumlah stream sebuah lagu. terlihat bahwa tidak ada atribut pada sebuah lagu yang memiliki korelasi dengan jumlah stream. Diantara semua atribut pada lagu Valence dan energy lumayan memiliki influence terhadap jumlah stream, semakin tinggi energi dan valence maka stream juga akan bertambah banyak, akan tetapi korelasi antar keduanya cukup lemah.

df %>% select_if(is.numeric) %>% select(-c(Index, Highest.Charting.Position, Number.of.Times.Charted, Artist.Followers, Popularity, Loudness, Tempo, Duration..ms.)) %>% 
  pivot_longer(cols = c("Danceability", "Energy", "Speechiness", "Acousticness", "Liveness", "Valence"), names_to = "Attributes") %>% 
  ggplot(aes(x = value, y = Streams)) +
  geom_point() +
  facet_wrap(~Attributes)

Managerial Implications for song producers who wish to create a phenomenal song on spotify.

Insight Genre

string yang terdapat pada kolom Genre merupakan data list yang terdiri dari beberapa genre disini kita akan mengambil data genre yang pertama saja karena diasumsikan bahwa genre pertama adalah genre utama.

df$Genre %>% head()
## [1] "['indie rock italiano', 'italian pop']"
## [2] "['australian hip hop']"                
## [3] "['pop']"                               
## [4] "['pop', 'uk pop']"                     
## [5] "['lgbtq+ hip hop', 'pop rap']"         
## [6] "['lgbtq+ hip hop', 'pop rap']"

kita akan mengambil dengan melakukan manipulasi string untuk mengambil kata pertama pada setiap row Genre.

df_genre <- df %>% filter(Genre != "[]")
df_genre$Genre <- gsub("',.*", "", df_genre$Genre)
df_genre$Genre <- str_remove(df_genre$Genre, pattern = "']")
df_genre$Genre <- str_remove(df_genre$Genre, pattern = "'")
df_genre$Genre <- gsub("\\[", "", df_genre$Genre)
df_genre$Genre %>% unique() %>% length()
## [1] 169

Genre Number of Times Charted

df_genre %>% 
  select(Genre, Number.of.Times.Charted) %>% 
  group_by(Genre) %>% 
  summarise(Number.of.Times.Charted = sum(Number.of.Times.Charted)) %>% 
  arrange(-Number.of.Times.Charted) %>% head(10) %>% 
  ggplot(aes(x = Number.of.Times.Charted, y = reorder(Genre, Number.of.Times.Charted))) +
  geom_col(fill = "red")

Lagu dengan genre dance pop, latin dan pop sering kali masuk top chart sehingga manager perlu meng-consider untuk mengarahkan genre lagunya ke genre ini.

Insight Genre with frequent charting

Lalu disini kita akan melihat genre manakah yang lagu nya sering terdaftar pada trending songs. Disini kita akan melakukan filtrasi sehingga lagu-lagu yang terpilih adalah lagu-lagu yang masuk trending di atas rata-rata.

df_genre %>% 
  filter(Number.of.Times.Charted >= mean(Number.of.Times.Charted)) %>% 
  select(Genre, Song.Name, Danceability, Energy, Speechiness, Acousticness, 
         Liveness, Valence) %>%  
  group_by(Genre) %>% 
  summarise(Song.Name = n(),
            Danceability = mean(Danceability), 
            Energy = mean(Energy), 
            Speechiness = mean(Speechiness), 
            Acousticness = mean(Acousticness), 
            Liveness = mean(Liveness), 
            Valence = mean(Valence)) %>% 
  arrange(-Song.Name) %>% head(5) %>% reactable()

Lalu disini dapat dilihat sebanyak 68 lagu yang bertemakan dance pop sering kali masuk pada trending music, dilanjutkan dengan latin dan pop sehingga sebagai produser mungkin sebaiknya membuat lagu bertemakan dance-pop karena sekarang dance-pop adalah salah satu lagu yang sering-kali trending di spotify.

Selain itu kita juga dapat melihat ciri khas seperti apa lagu-lagu tersbut dari atribut-atribut pada musik.

Insight Genre with the number of Times the song charted

df_genre %>%  
  group_by(Genre) %>% 
  select(Number.of.Times.Charted, Genre, Song.Name, Danceability, Energy, 
         Speechiness, Acousticness, Liveness, Valence) %>%  
  summarise(Song.Name = n(),
            Number.of.Times.Charted = mean(Number.of.Times.Charted),
            Danceability = mean(Danceability), 
            Energy = mean(Energy), 
            Speechiness = mean(Speechiness), 
            Acousticness = mean(Acousticness), 
            Liveness = mean(Liveness), 
            Valence = mean(Valence)) %>% 
  arrange(-Song.Name) %>% head(5) %>% reactable()

Lalu berikut adalah semua lagu secara keseluruhan beserta rata-rata lagu-lagu pada genre tersebut masuk pada chart.

Time.to.Trend

Lalu disini akan kita melakukan filtrasi lagi dengan mencari lagu-lagu yang waktu trendingnya cepat, disini kita dapat mencarinya dengan melihat selisih waktu antara waktu lagu rilis dan kapan pertama kali lagu tersebut trending. Lalu kita akan melakukan filtrasi lagi pada lagu-lagu yang masuk tren kurang dari satu bulan.

df_genre %>% select(Song.Name, Genre, Number.of.Times.Charted, Release.Date, Charting.Starts) %>% 
  mutate(Time.to.Trend = Charting.Starts - Release.Date) %>% 
  filter(Time.to.Trend > 0,
         Time.to.Trend < 30) %>% 
  group_by(Genre) %>%
  summarise(Song.Name = n()) %>% 
  arrange(-Song.Name) %>% head(5) %>% reactable()

Dapat dilihat lagu-lagu latin sering kali masuk dalam lagu trending dalam jarak waktu yang dekat dari rilisnya. mungkin strategi yang bisa diberikan oleh produser adalah mengincar lagu-lagu latin.

Days to Release song

Lalu bagi seorang produser kita perlu mengetahui juga apakah ada advantage yang bisa didapatkan dari merilis lagu pada hari tertentu.

df_genre %>% select(Song.Name, Genre, Number.of.Times.Charted, Release.Date, Charting.Starts) %>% 
  mutate(Time.to.Trend = Charting.Starts - Release.Date,
         Day.of.Week = weekdays(Release.Date),
         Day.of.Month = days_in_month(Release.Date)) %>% 
  filter(Number.of.Times.Charted >= mean(Number.of.Times.Charted)) %>% 
  group_by(Day.of.Week) %>% 
  summarise(Song.Name = n()) %>% 
  ggplot(aes(x = Day.of.Week, y = Song.Name)) +
  geom_col()

Dari data tersebut di terlihat banyak dari lagu-lagu tren dirilis pada hari jumat. sehingga hal tersebut juga dapat dikonsiderasi.

Date in month

Lalu Dari kita juga dapat mencari insight dari tanggal, pada tanggal berapa waktu

df_genre %>% select(Song.Name, Genre, Number.of.Times.Charted, Release.Date, Charting.Starts) %>% 
  mutate(Time.to.Trend = Charting.Starts - Release.Date,
         Day.of.Week = weekdays(Release.Date),
         Day.of.Month = mday(Release.Date)) %>% 
  filter(Number.of.Times.Charted >= mean(Number.of.Times.Charted)) %>% 
  group_by(Day.of.Month) %>% 
  summarise(Song.Name = n()) %>% 
  ggplot(aes(x = Day.of.Month, y = Song.Name)) +
  geom_col() 

Dari days of month tidak bisa digambarkan dengan pasti pada tanggal berapa yang berpengaruh karena masih random karena mungkin ada data tren mingguan yang mempengaruhi hasil ini.

Chords

df %>% group_by(Chord) %>% 
  summarise(Number.of.Times.Charted = sum(Number.of.Times.Charted)) %>% 
  arrange(-Number.of.Times.Charted) %>% 
  ggplot(aes(x = Number.of.Times.Charted, y = reorder(Chord, Number.of.Times.Charted))) +
  geom_col(fill = "red")

  • Lagu dengan chord dasar C#, C, dan G# seringkali masuk dalam Top Chart.
  • Manager/produser lagu sebaiknya melakukan key shift ke 3 chord dasar tersebut.

Menghitung jumlah lagu yang masuk Chart untuk tiap genre lagu.

Interpretasi

Sebagai seorang tim managerial pertama, kita dapat:

  • Melihat genre apa yang dapat menjadi celah untuk memiliki lagu tren. Pada Contoh ini Dance Pop , Latin dan Pop adalah genre yang paling mudah untuk menembus trending

  • Melihat genre apa yang time to trend dari lagunya cepat, pada kasus ini Dance Pop juga terpilih sebagai lagu yang sangat cepat untuk mendapat trend.

  • Lalu kapan waktu untuk melakukan rilis lagu juga perlu diperhatikan, strategi yang akan digunakan adalah merilis lagu pada hari jumat

category_trend <- youtube %>% 
            filter(category_id == input$select_category2) %>% 
            group_by(category_id, trending_date) %>% 
            summarise(trending_videos = n()) %>%
            ggplot(aes(x = ymd(trending_date), y = trending_videos, group = category_id, 
                       col = category_id)) +
            geom_point() + geom_line() +
            scale_y_continuous(breaks = seq(-4,44,4)) +
            scale_x_date(date_breaks = "2 week", date_labels = "%b %d") +
            theme_minimal() +
            theme(legend.position = "bottom") +
            labs(x = "", y = "Frequency",
                 col = NULL)
        
        category_trend