Το dataset περιέχει ιστορικές ημερήσιες τιμές της μετοχής της Microsoft (MSFT):
MSFT <- read.csv("MSFT.csv")
#Φόρτωση βιβλιοθηκών
library(ggplot2) # Για οπτικοποίηση
# Μετατροπή της στήλης Date σε ημερομηνία
MSFT$Date <- as.Date(MSFT$Date)
# Τα δεδομένα
head(MSFT)## Date Open High Low Close Adj.Close Volume
## 1 1986-03-13 0.088542 0.101563 0.088542 0.097222 0.061434 1031788800
## 2 1986-03-14 0.097222 0.102431 0.097222 0.100694 0.063628 308160000
## 3 1986-03-17 0.100694 0.103299 0.100694 0.102431 0.064725 133171200
## 4 1986-03-18 0.102431 0.103299 0.098958 0.099826 0.063079 67766400
## 5 1986-03-19 0.099826 0.100694 0.097222 0.098090 0.061982 47894400
## 6 1986-03-20 0.098090 0.098090 0.094618 0.095486 0.060337 58435200
## Open High Low
## Min. : 0.08854 Min. : 0.09201 Min. : 0.08854
## 1st Qu.: 4.05078 1st Qu.: 4.10205 1st Qu.: 4.02734
## Median : 26.82000 Median : 27.10000 Median : 26.52000
## Mean : 41.32494 Mean : 41.76089 Mean : 40.87849
## 3rd Qu.: 40.03500 3rd Qu.: 40.44375 3rd Qu.: 39.50000
## Max. :344.62000 Max. :349.67001 Max. :342.20001
## Close Volume
## Min. : 0.09028 Min. :2.304e+06
## 1st Qu.: 4.07520 1st Qu.:3.461e+07
## Median : 26.84000 Median :5.203e+07
## Mean : 41.33563 Mean :5.875e+07
## 3rd Qu.: 39.93750 3rd Qu.:7.265e+07
## Max. :343.10998 Max. :1.032e+09
Για τη συσταδοποίηση θα χρησιμοποιήσουμε τρία χαρακτηριστικά που περιγράφουν την κίνηση της μετοχής κάθε ημέρα:
# Υπολογισμός χαρακτηριστικών
MSFT$daily_return <- c(NA, diff(MSFT$Close) / MSFT$Close[-nrow(MSFT)] * 100)
MSFT$volatility <- MSFT$High - MSFT$Low
# Αφαίρεση NA (πρώτη γραμμή)
MSFTclean <- MSFT[!is.na(MSFT$daily_return), ]
# Επιλογή χαρακτηριστικών
features <- MSFTclean[, c("daily_return", "volatility", "Volume")]
# Κανονικοποίηση (scale) για την συσταδοποίηση
features_scaled <- scale(features)
cat("Διαστάσεις δεδομένων για clustering:", dim(features_scaled), "\n")## Διαστάσεις δεδομένων για clustering: 9082 3
Η ιεραρχική συσταδοποίηση ομαδοποιεί τις παρατηρήσεις σταδιακά, χτίζοντας ένα δέντρο (dendogram). Λόγω του μεγάλου αριθμού παρατηρήσεων, χρησιμοποιούμε δείγμα 300 ημερών για να είναι το δέντρο αναγνώσιμο.
# Δείγμα 300 παρατηρήσεων για το dendogram
set.seed(42)
sample_idx <- sample(nrow(features_scaled), 300)
sample_data <- features_scaled[sample_idx, ]
# Υπολογισμός αποστάσεων (Euclidean)
dist_matrix <- dist(sample_data, method = "euclidean")
# Ιεραρχική συσταδοποίηση με μέθοδο Ward
hc <- hclust(dist_matrix, method = "ward.D2")
# Dendogram
plot(hc,
main = "Ιεραρχική Συσταδοποίηση MSFT",
xlab = "Παρατηρήσεις",
ylab = "Απόσταση",
labels = FALSE, # δεν θα φαίνονται ετικέτες λόγω πολλώ παρατηρήσεων
cex.main = 1.2)
# Προσθήκη ορίου για 3 συστάδες
rect.hclust(hc, k = 3, border = c("red", "blue", "green"))
legend("topright",
legend = c("Συστάδα 1", "Συστάδα 2", "Συστάδα 3"),
fill = c("red", "blue", "green"),
cex = 0.8)# Κόβουμε το δέντρο σε 3 συστάδες
hc_clusters <- cutree(hc, k = 3)
# Κατανομή παρατηρήσεων ανά συστάδα
table(hc_clusters)## hc_clusters
## 1 2 3
## 265 17 18
# Οπτικοποίηση συστάδων (daily_return & volatility)
plot(sample_data[, "daily_return"],
sample_data[, "volatility"],
col = hc_clusters,
pch = 19,
cex = 0.6,
main = "Ιεραρχική Συσταδοποίηση: Απόδοση vs Μεταβλητότητα",
xlab = "Ημερήσια Απόδοση (scaled)",
ylab = "Μεταβλητότητα (scaled)")
legend("topright",
legend = paste("Συστάδα", 1:3),
col = 1:3,
pch = 19,
cex = 0.8)Από το δενδρλογραμμα και το scatter plot παρατηρούμε ότι:
Ο αλγόριθμος K-means χωρίζει τα δεδομένα σε k συστάδες ελαχιστοποιώντας την διακύμανση. Εφαρμόζω K-means σε όλο το dataset.
# Elbow method: υπολογισμός WSS για k = 1 έως 8
set.seed(42)
wss <- sapply(1:8, function(k) {
kmeans(features_scaled, centers = k, nstart = 10)$tot.withinss
})
# Γράφημα Elbow
plot(1:8, wss,
type = "b",
pch = 19,
col = "steelblue",
main = "Elbow Method",
xlab = "Αριθμός Συστάδων (k)",
ylab = "Συνολική Διακύμανση (WSS)")
abline(v = 3, lty = 2, col = "red")
text(3.2, max(wss)*0.9, "k=3", col = "red")set.seed(42)
km <- kmeans(features_scaled, centers = 3, nstart = 25)
# Αποτελέσματα
cat("Κατανομή παρατηρήσεων ανά συστάδα:\n")## Κατανομή παρατηρήσεων ανά συστάδα:
##
## 1 2 3
## 1645 6889 548
##
## Μέσοι όροι (scaled) ανά συστάδα:
## daily_return volatility Volume
## 1 0.937 -0.200 1.269
## 2 -0.220 -0.204 -0.255
## 3 -0.049 3.169 -0.605
# Οπτικοποίηση K-Means
plot(features_scaled[, "daily_return"],
features_scaled[, "volatility"],
col = km$cluster,
pch = 19,
cex = 0.4,
main = "K-Means Απόδοση vs Μεταβλητότητα",
xlab = "Ημερήσια Απόδοση (scaled)",
ylab = "Μεταβλητότητα (scaled)")
# Κέντρα συστάδων
points(km$centers[, "daily_return"],
km$centers[, "volatility"],
col = 1:3,
pch = 8,
cex = 2.5,
lwd = 2)
legend("topright",
legend = c(paste("Συστάδα", 1:3), "Κέντρα"),
col = c(1:3, 1),
pch = c(19, 19, 19, 8),
cex = 0.8)# Κατανομή ημερήσιας απόδοσης ανά συστάδα
MSFTclean$cluster_km <- km$cluster
boxplot(daily_return ~ cluster_km,
data = MSFTclean,
col = c("salmon", "lightblue", "lightgreen"),
main = "Ημερήσια Απόδοση ανά Συστάδα (K-Means)",
xlab = "Συστάδα",
ylab = "Ημερήσια Απόδοση (%)",
names = paste("Συστάδα", 1:3))
abline(h = 0, lty = 2, col = "gray50")Τα αποτελέσματα του K-means έχουν τρεις ξεκάθαρες ομάδες συμπεριφοράς της μετοχής:
## Ιεραρχική vs K-Means:
## KMeans
## Hierarchical 1 2 3
## 1 44 217 4
## 2 12 5 0
## 3 0 4 14
Και οι δύο μέθοδοι (ιεραρχική και K-means) κατέληξαν σε παρόμοια αποτελέσματα, επιβεβαιώνοντας την αξιοπιστία της ανάλυσης. Η K-means είναι πιο αποδοτική για μεγάλα datasets, ενώ η ιεραρχική παρέχει το πλεονέκτημα της οπτικής αναπαράστασης μέσω του dendogram.