Το “Spotify Tracks Dataset” αντλήθηκε από το Kaggle σε μορφή CSV. Κάθε γραμμή του αντιπροσωπεύει ένα μεμονωμένο τραγούδι (καλύπτοντας συνολικά 125 διαφορετικά μουσικά είδη), ενώ οι στήλες καταγράφουν τα χαρακτηριστικά του.
Στην ανάλυση που θα κάνουμε, επιδιώκουμε να εντοπίσουμε αυτόματες ομαδοποιήσεις τραγουδιών με βάση τα ακουστικά τους χαρακτηριστικά, παρακάμπτοντας τις κλασικές ταμπέλες των μουσικών ειδών όπως τις γνωρίζουμε. Μέσα από αυτή την εξερεύνηση, στοχεύουμε στον ορισμό νέων, στατιστικά τεκμηριωμένων κατηγοριών, που προκύπτουν από μια βαθύτερη κατανόηση του dataset.
Σε πρώτη φάση, θα χρειαστεί να προβούμε σε εκκαθάριση των δεδομέων του dataset, καθώς ενδέχεται να υπάρχουν διπλότυπες εγγραφές οιοποίες αλλοιώνουν τα αποτελέσματα της ανάλυσής μας. Ωστόσο, η απαλοιφή των διπλοτύπων δεν είναι εύκολη, καθώς έχουμε κομμάτια που αφορούν το ίδιο τραγούδι αλλά καταχωρήθηκαν ως διαφορετικά επειδή ανήκουν σε άλλα άλμπουμ ή επανακυκλοφόρησαν. Επομένως, θα θεωρήσουμε ως κλειδί για τη μοναδικότητα τον συνδυασμό καλλιτέχνη και ονόματος τραγουδιού, κρατώντας κάθε φορά το πιο δημοφιλές αντίγραφο του ίδιου τραγουδιού. Παρακάτω φαίνεται ο συνολικός αριθμός μοναδικών εγγραφών (η εκκαθάριση έγινε από τις αρχικές 114.000 εγγραφές)
library(tidyverse)
track_data = unique(track_data)
spotify_final_clean <- track_data %>%
arrange(desc(popularity)) %>%
distinct(track_name, artists, .keep_all = TRUE)
# Έλεγχος: Πόσες γραμμές έφυγαν;
nrow(track_data) - nrow(spotify_final_clean)## [1] 32656
Επίσης, λόγω της αυξημένης πολυπλοκότητας της ιεραρχικής συσταδοποίησης, θα χρειαστεί να δουλέψουμε σε ένα δείγμα από το συνολικό καθαρό dataset, ενδεικτικά αποτελούμενο από 5.000 εγγραφές. Σε αυτό το σημείο για να αποφύγουμε περαιτέρω υπολογισμούς θα απομονώσουμε τα τεχνικά χαρακτηριστικά των κομματιών, τα οποία παρουσιάζουν το βασικό ενδιαφέρον για την ανάλυσή μας. Επιπλέον θα εκτελέσουμε μία κανονικοποίηση στις τιμές των μεταβλητών, καθώς θέλουμε να μην υπάρχουν ανομοιομορφίες που “φουσκώνουν” τις αποστάσεις που θα υπολογίσουμε.
Έχοντας έτοιμο το δείγμα από το Dataset, πάνω στο οποίο θα κάνουμε συσταδοποίηση, εξάγουμε το πρώτο διάγραμμα των συστάδων που προκύπτουν.
distances = dist(clustering_vars, method = "euclidean")
clusterTracks = hclust(distances, method = "ward.D2")
plot(clusterTracks,
main = "Διάγραμμα Ιεραρχικής Συσταδοποίησης",
col.main = "#1DB954",
col.lab = "#1DB954",
col.axis = "#1DB954",
sub = "",
ann = TRUE) Όπως παρατηρούμε έχουμε υπερβολικά πολλά επίπεδα συστάδων. Σε ένα τέτοιο δείγμα και λόγω της ευρείας ποικιλίας μουσικών ειδών που επιχειρούμε να αναδείξουμε, χωρίς όμως να εμβαθύνουμε σε πολύ εξειδικευμένες κατηγορίες, θα επιλέξουμε να εξάγουμε 7 clusters. Παρακάτω, φαίνεται το πλήθος παρατηρήσεων που συγκεντρώνονται ανά cluster.
library(knitr)
library(kableExtra)
library(dplyr)
library(plotly)
library(tidyr)
clusters = cutree(clusterTracks, k = 7)
spotify_sample$cluster <- as.factor(clusters)
# Υπολογισμός μέσων τιμών ανά cluster
cluster_profile <- spotify_sample %>%
group_by(cluster) %>%
summarise(across(c(danceability, energy, loudness, speechiness,
acousticness, instrumentalness, liveness, valence, tempo),
mean, na.rm = TRUE))
summary_horizontal <- spotify_sample %>%
count(cluster) %>%
mutate(cluster = paste("Cluster", cluster))%>%
pivot_wider(names_from = cluster, values_from = n)
summary_horizontal %>%
kbl(align = "c") %>%
kable_styling(full_width = TRUE,
bootstrap_options = c("condensed")) %>%
row_spec(0,
background = "#14833B",
color = "white",
bold = TRUE)| Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 | Cluster 5 | Cluster 6 | Cluster 7 |
|---|---|---|---|---|---|---|
| 255 | 1960 | 837 | 1047 | 549 | 303 | 49 |
Για καλύτερη εικόνα του προφίλ τιμών που λαμβάνουν οι μεταβλητές ανά cluster, δημιουργήθηκε πίνακας με τις μέσες τιμές κάθε μεταβλητής για όλα τα clusters που οπτικοποιείται στην διαδραστική προβολή που ακολουθεί.
# 1. Προετοιμασία των δεδομένων - Παραμένει το ίδιο
nodes <- colnames(cluster_profile)[-1]
create_radar_trace <- function(cluster_id) {
row_data <- cluster_profile %>% filter(cluster == cluster_id) %>% select(-cluster)
as.numeric(row_data)
}
# 2. ΚΑΤΑΣΚΕΥΗ ΤΟΥ PLOT - ΕΔΩ ΕΙΝΑΙ Η ΑΛΛΑΓΗ
# Αρχικοποιούμε το fig ΧΩΡΙΣ type και fill για να μην δημιουργηθεί trace ακόμα
fig <- plot_ly()
# Προσθέτουμε τα 10 clusters
for(i in 1:7) {
fig <- fig %>% add_trace(
type = 'scatterpolar', # Μεταφέραμε το type εδώ
fill = 'toself', # Μεταφέραμε το fill εδώ
r = c(create_radar_trace(i), create_radar_trace(i)[1]),
theta = c(nodes, nodes[1]),
name = paste("Cluster", i),
visible = (i == 1),
marker = list(color = "#1DB954"),
fillcolor = 'rgba(29, 185, 84, 0.4)'
)
}
# 3. Δημιουργία του "Menu" - Παραμένει το ίδιο
dropdown_buttons <- lapply(1:7, function(i) {
vis_list <- rep(FALSE, 7)
vis_list[i] <- TRUE
list(
method = "restyle",
args = list("visible", as.list(vis_list)),
label = paste("Cluster", i)
)
})
# 4. Τελική διαμόρφωση - Πρόσεξε το χρώμα στο font του μενού
fig <- fig %>% layout(
title = list(text = "Cluster Explorer: Music DNA", font = list(color = "#1DB954", size = 20)),
paper_bgcolor = "#191414",
plot_bgcolor = "#191414",
polar = list(
bgcolor = "#191414",
radialaxis = list(visible = T, range = c(0, 1), color = "gray"),
angularaxis = list(color = "white")
),
updatemenus = list(
list(
buttons = dropdown_buttons,
direction = "down",
showactive = TRUE,
x = 0.1, y = 1.2,
font = list(color = "#1DB954")
)
)
)
figCluster No 1
Δυναμικό cluster με υψηλό liveness και tempo, που παραπέμπει σε ζωντανές, γρήγορες και έντονες ηχογραφήσεις. Πολύ χαμηλό loudness και instrumentalness, δίνοντας έμφαση στη φωνή χωρίς ψηφιακά όργανα. Συνδυασμός έντονης ενέργειας (energy) με χαμηλό valence, δημιουργώντας μια ατμόσφαιρα σοβαρή, «σκοτεινή» αλλά γεμάτη ένταση.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 1) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 2
Χαρακτηρίζεται από μέγιστο tempo και υψηλή ενέργεια (energy), αποτελώντας την πιο γρήγορη και δυναμική ομάδα του δείγματος. Διαθέτει υψηλό valence και danceability, στοιχεία που παραπέμπουν σε ανεβαστική, ευδιάθετη και ρυθμική μουσική. Εμφανίζει ελάχιστο acousticness και instrumentalness, υποδηλώνοντας σύγχρονες παραγωγές.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 2) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 3
Διακρίνεται για το υψηλό acousticness και το γρήγορο tempo, συνδυάζοντας φυσικούς ήχους με ταχύτητα στον ρυθμό. Η ενέργεια και το loudness παραμένουν σε χαμηλά επίπεδα, υποδεικνύοντας «ήσυχες» αλλά σταθερά ρυθμικές συνθέσεις. Με χαμηλό valence και σχεδόν μηδενικό instrumentalness, το cluster παραπέμπει σε ακουστικά, μελαγχολικά κομμάτια που εστιάζουν στη φωνή.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 3) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 4
Διαθέτει πολύ υψηλό tempo και αυξημένη ενέργεια, προσφέροντας μια έντονη και γρήγορη ρυθμική βάση. Έχει ελάχιστο acousticness, instrumentalness και speechiness, στοιχείο που παραπέμπει σε σύγχρονες, αμιγώς ψηφιακές παραγωγές. Χαρακτηρίζεται από χαμηλό valence και loudness, δίνοντας έναν πιο συγκρατημένο ή «σοβαρό» τόνο παρά τη μεγάλη ταχύτητα.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 4) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 5
Εμφανίζει σημαντικά υψηλό instrumentalness και tempo, παραπέμποντας σε ορχηστρικά ή ηλεκτρονικά κομμάτια χωρίς φωνητικά. Η ενέργεια είναι υψηλή, αλλά το loudness και το valence παραμένουν χαμηλά, δημιουργώντας μια αίσθηση «έντονης αλλά ψυχρής» ατμόσφαιρας. Σχεδόν μηδενικό speechiness και acousticness, υπογραμμίζοντας τον αμιγώς ψηφιακό και μη λεκτικό χαρακτήρα της συγκεκριμένης ομάδας.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 5) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 6
Συνδυάζει εξαιρετικά υψηλό acousticness και instrumentalness, θυμίζοντας ορχηστρική ή ακουστική μουσική χωρίς φωνητικά. Παρά το υψηλό tempo, η ενέργεια και το loudness παραμένουν σε πολύ χαμηλά επίπεδα, προσφέροντας μια ήρεμη αλλά ρυθμική αίσθηση. Με σχεδόν μηδενικό danceability και valence, το cluster αυτό εστιάζει στην ατμοσφαιρική ακρόαση.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 6) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Cluster No 7
Κυριαρχείται από πολύ υψηλό speechiness και acousticness, υποδηλώνοντας κομμάτια με έμφαση στον λόγο (εν προκειμένω Podcasts) και φυσικό ήχο. Διαθέτει γρήγορο tempo και σημαντική liveness, προσφέροντας μια αίσθηση «ζωντανής» και ταχείας ροής στην ακρόαση. Η χαμηλή ένταση (loudness) και το μηδενικό instrumentalness επιβεβαιώνουν την επικέντρωση στη φωνή έναντι της ψηφιακής παραγωγής.
Χαρακτηριστικά Κομμάτια
spotify_sample %>%
filter(cluster == 7) %>%
slice_max(popularity, n = 5) %>%
select(track_name,artists,popularity)Η ανάλυση απέδειξε ότι η ιεραρχική συσταδοποίηση μπορεί να χαρτογραφήσει τη μουσική βάσει ακουστικού “DNA”, παρακάμπτοντας τις παραδοσιακές ταμπέλες των ειδών.
Mood vs Genre: Τα clusters ομαδοποιούν τα τραγούδια βάσει “διάθεσης”. Το Cluster 2 αποτελεί την πιο Mainstream ανεβαστική/χορευτική ζώνη, ενώ το Cluster 6 την “Focus” ζώνη, ανεξαρτήτως μουσικού είδους.
Αυτοματοποιημένος Διαχωρισμός: Ο αλγόριθμος απομόνωσε με ακρίβεια τον ομιλούμενο λόγο (Cluster 7 - Podcasts) και την ορχηστρική μουσική (Cluster 6).
Ακουστική Αντίθεση: Επιβεβαιώθηκε η σταθερή αρνητική συσχέτιση μεταξύ φυσικότητας (Acousticness) και έντασης (Energy/Loudness).
Μεγάλη σημασία έχει να δούμε πώς γίνεται να εκμεταλλευτούμε τα ευρήματα της ανάλυσης μέσω συσταδοποίησης. Σε πρακτικό επίπεδο, θα μπορούσε να προταθεί η αυτόματη δημιουργία λιστών για συγκεκριμένες δραστηριότητες (π.χ. Gym από το Cluster 2, Study από το Cluster 6). Επιπλέον, μπορούν να γίνονται προτάσεις τραγουδιών με παρόμοιο ηχητικό DNA για αύξηση του user engagement. Τέλος, η αναγνώριση των χρηστών που προτιμούν περιεχόμενο λόγου (Cluster 7) θα μπορούσε δυνητικά να είναι χρήσιμη για εξατομικευμένες καμπάνιες.