1 Introduction

The purpose of this report is to analyse runners’ results data from the 46. Warsaw Marathon (Poland, 2024) by application of unsupervised learning methods listed below.

  • Dimension Reduction technique: PCA

  • Clustering Analysis methods: K-Means, PAM, CLARA, Hierarchical, DBSCAN

1.1 Data preprocessing

I will begin with preparing the dataset and showing some initial analysis of the data.

1.1.1 Dataset loading

df <- read.csv("results_clean.csv")

1.1.2 Check structure & remove unnecessary columns

colnames(df)
##  [1] "Position"        "Bib"             "Team"            "Country"        
##  [5] "Year.of.Birth"   "Category"        "Gender.Position" "Surname"        
##  [9] "Name"            "City"            "Age.Position"    "X5km"           
## [13] "X10km"           "X15km"           "X20km"           "X21km"          
## [17] "X25km"           "X30km"           "X35km"           "X40km"          
## [21] "Chip.Time"       "Gun.Time"

# Remove unnecessary columns
df <- df %>% dplyr::select(-Name, -Team, -Surname, -Bib, -City, -Gun.Time)

str(df)
## 'data.frame':    7080 obs. of  16 variables:
##  $ Position       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Country        : chr  "HUN" "POL" "UKR" "BEL" ...
##  $ Year.of.Birth  : int  2000 1997 1995 1997 1982 1980 1992 1991 1985 1988 ...
##  $ Category       : chr  "M20" "M20" "M20" "M20" ...
##  $ Gender.Position: int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Age.Position   : int  1 2 3 4 1 2 1 2 3 4 ...
##  $ X5km           : chr  "00:15:39" "00:15:40" "00:15:40" "00:15:40" ...
##  $ X10km          : chr  "00:30:57" "00:30:58" "00:30:57" "00:30:57" ...
##  $ X15km          : chr  "00:46:23" "00:46:23" "00:46:23" "00:46:22" ...
##  $ X20km          : chr  "01:02:19" "01:02:20" "01:02:20" "01:02:19" ...
##  $ X21km          : chr  "01:06:16" "01:05:50" "01:05:50" "01:05:49" ...
##  $ X25km          : chr  "01:18:12" "01:18:12" "01:18:12" "01:18:11" ...
##  $ X30km          : chr  "01:33:38" "01:33:39" "01:33:39" "01:33:44" ...
##  $ X35km          : chr  "01:48:45" "01:48:46" "01:49:33" "01:50:03" ...
##  $ X40km          : chr  "02:03:58" "02:04:18" "02:06:08" "02:06:20" ...
##  $ Chip.Time      : chr  "02:10:43" "02:11:15" "02:13:13" "02:13:29" ...

1.1.3 Separate Category column

# Separate the first character (K/M) into Gender and the rest into Age.Group
df <- df %>%
  mutate(Gender = substr(Category, 1, 1),
         Age.Group = substr(Category, 2, nchar(Category)))

# Convert Gender
df$Gender <- ifelse(df$Gender == "K", "Female", "Male")

# Check result
head(df[, c("Category", "Gender", "Age.Group")])
##   Category Gender Age.Group
## 1      M20   Male        20
## 2      M20   Male        20
## 3      M20   Male        20
## 4      M20   Male        20
## 5      M40   Male        40
## 6      M40   Male        40

1.1.4 Fix Country Column

df$Country <- substr(df$Country, nchar(df$Country) - 2, nchar(df$Country))
sum(df$Country == "")
## [1] 59
df$Country[df$Country == ""] <- "POL"

1.1.5 Convert variables

df$Country <- as.factor(df$Country)
df$Age <- as.numeric(format(Sys.Date(), "%Y")) - df$Year.of.Birth
df$Category <- as.factor(df$Category)
df$Gender.Position <- as.integer(df$Gender.Position)
df$Age.Position <- as.integer(df$Age.Position)
df <- df %>% select(-Year.of.Birth, -Age.Group)
df$Gender <- as.factor(df$Gender)
convert_to_minutes <- function(time_str) {
  hms <- strptime(time_str, format = "%H:%M:%S")
  return(as.numeric(hms$hour) * 60 + as.numeric(hms$min) + as.numeric(hms$sec) / 60)
}

time_columns <- c("X5km", "X10km", "X15km", "X20km", "X21km", 
                  "X25km", "X30km", "X35km", "X40km", "Chip.Time")

df[, time_columns] <- lapply(df[, time_columns], convert_to_minutes)
str(df)
## 'data.frame':    7080 obs. of  17 variables:
##  $ Position       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Country        : Factor w/ 65 levels "ALB","ARG","ARM",..: 25 49 62 7 62 38 25 49 18 49 ...
##  $ Category       : Factor w/ 14 levels "","K20","K30",..: 8 8 8 8 10 10 9 9 9 9 ...
##  $ Gender.Position: int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Age.Position   : int  1 2 3 4 1 2 1 2 3 4 ...
##  $ X5km           : num  15.7 15.7 15.7 15.7 15.7 ...
##  $ X10km          : num  31 31 31 31 31 ...
##  $ X15km          : num  46.4 46.4 46.4 46.4 46.4 ...
##  $ X20km          : num  62.3 62.3 62.3 62.3 62.4 ...
##  $ X21km          : num  66.3 65.8 65.8 65.8 65.8 ...
##  $ X25km          : num  78.2 78.2 78.2 78.2 78.3 ...
##  $ X30km          : num  93.6 93.7 93.7 93.7 94.5 ...
##  $ X35km          : num  109 109 110 110 111 ...
##  $ X40km          : num  124 124 126 126 128 ...
##  $ Chip.Time      : num  131 131 133 133 136 ...
##  $ Gender         : Factor w/ 2 levels "Female","Male": 2 2 2 2 2 2 2 2 2 2 ...
##  $ Age            : num  26 29 31 29 44 46 34 35 41 38 ...
summary(df) 
##     Position       Country        Category    Gender.Position  Age.Position   
##  Min.   :   1   POL    :6293   M30    :1792   Min.   :   1    Min.   :   0.0  
##  1st Qu.:1770   GBR    : 157   M40    :1700   1st Qu.: 884    1st Qu.: 202.8  
##  Median :3540   UKR    :  85   M20    :1358   Median :2214    Median : 501.0  
##  Mean   :3540   FRA    :  65   M50    : 691   Mean   :2462    Mean   : 632.7  
##  3rd Qu.:5309   USA    :  54   K30    : 454   3rd Qu.:3983    3rd Qu.:1027.0  
##  Max.   :7079   GER    :  38   K40    : 426   Max.   :5754    Max.   :1792.0  
##  NA's   :2      (Other): 388   (Other): 659   NA's   :2                       
##       X5km            X10km            X15km            X20km       
##  Min.   : 15.65   Min.   : 30.95   Min.   : 46.37   Min.   : 62.32  
##  1st Qu.: 25.32   1st Qu.: 50.53   1st Qu.: 75.48   1st Qu.:101.08  
##  Median : 27.93   Median : 55.85   Median : 83.38   Median :111.73  
##  Mean   : 28.50   Mean   : 56.54   Mean   : 84.34   Mean   :113.20  
##  3rd Qu.: 30.32   3rd Qu.: 60.87   3rd Qu.: 91.26   3rd Qu.:122.77  
##  Max.   :604.88   Max.   :638.02   Max.   :675.38   Max.   :712.63  
##  NA's   :7        NA's   :8        NA's   :9        NA's   :12      
##      X21km            X25km            X30km            X35km      
##  Min.   : 65.82   Min.   : 78.18   Min.   : 93.63   Min.   :108.8  
##  1st Qu.:106.80   1st Qu.:127.05   1st Qu.:153.37   1st Qu.:180.6  
##  Median :118.00   Median :140.06   Median :168.73   Median :198.2  
##  Mean   :119.60   Mean   :142.43   Mean   :172.26   Mean   :203.8  
##  3rd Qu.:129.80   3rd Qu.:155.14   3rd Qu.:188.78   3rd Qu.:223.8  
##  Max.   :720.92   Max.   :750.23   Max.   :788.22   Max.   :828.8  
##  NA's   :8        NA's   :8        NA's   :14       NA's   :18     
##      X40km         Chip.Time        Gender          Age       
##  Min.   :124.0   Min.   :130.7   Female:1325   Min.   :20.00  
##  1st Qu.:208.9   1st Qu.:220.6   Male  :5755   1st Qu.:32.00  
##  Median :230.6   Median :244.6                 Median :40.00  
##  Mean   :236.7   Mean   :249.9                 Mean   :40.23  
##  3rd Qu.:261.6   3rd Qu.:277.2                 3rd Qu.:47.00  
##  Max.   :869.3   Max.   :443.2                 Max.   :89.00  
##  NA's   :16      NA's   :2                     NA's   :2

1.1.6 Handle missing values

# Total number of missing values
sum(is.na(df))  
## [1] 108

# Fill missing values in time columns with average time for Category and Country
df <- df %>%
  group_by(Category, Country) %>%
  mutate(across(all_of(time_columns), ~ ifelse(is.na(.), mean(., na.rm = TRUE), .))) %>%
  ungroup()

colSums(is.na(df))
##        Position         Country        Category Gender.Position    Age.Position 
##               2               0               0               2               0 
##            X5km           X10km           X15km           X20km           X21km 
##               0               0               0               0               0 
##           X25km           X30km           X35km           X40km       Chip.Time 
##               0               0               0               0               2 
##          Gender             Age 
##               0               2

# Remove missing values
df <- na.omit(df) 

# Total number of missing values
sum(is.na(df)) 
## [1] 0

1.1.7 Outliers in time columns

1.1.7.1 Remove rows in time columns where any time value exceeds 500 minutes

df <- df[!apply(df[, time_columns] > 500, 1, any), ]
summary(df)
##     Position       Country        Category    Gender.Position  Age.Position   
##  Min.   :   1   POL    :6283   M30    :1790   Min.   :   1    Min.   :   1.0  
##  1st Qu.:1769   GBR    : 157   M40    :1699   1st Qu.: 884    1st Qu.: 203.0  
##  Median :3536   UKR    :  85   M20    :1356   Median :2214    Median : 501.0  
##  Mean   :3538   FRA    :  65   M50    : 691   Mean   :2462    Mean   : 632.6  
##  3rd Qu.:5306   USA    :  54   K30    : 453   3rd Qu.:3981    3rd Qu.:1027.0  
##  Max.   :7079   GER    :  38   K40    : 424   Max.   :5754    Max.   :1792.0  
##                 (Other): 388   (Other): 657                                   
##       X5km           X10km            X15km            X20km       
##  Min.   :15.65   Min.   : 30.95   Min.   : 46.37   Min.   : 62.32  
##  1st Qu.:25.32   1st Qu.: 50.53   1st Qu.: 75.48   1st Qu.:101.09  
##  Median :27.93   Median : 55.85   Median : 83.37   Median :111.72  
##  Mean   :27.87   Mean   : 55.91   Mean   : 83.71   Mean   :112.57  
##  3rd Qu.:30.32   3rd Qu.: 60.85   3rd Qu.: 91.22   3rd Qu.:122.73  
##  Max.   :47.33   Max.   :101.43   Max.   :155.53   Max.   :200.25  
##                                                                    
##      X21km            X25km            X30km            X35km      
##  Min.   : 65.82   Min.   : 78.18   Min.   : 93.63   Min.   :108.8  
##  1st Qu.:106.80   1st Qu.:127.04   1st Qu.:153.37   1st Qu.:180.6  
##  Median :118.00   Median :140.05   Median :168.73   Median :198.2  
##  Mean   :118.96   Mean   :141.79   Mean   :171.62   Mean   :203.2  
##  3rd Qu.:129.73   3rd Qu.:155.05   3rd Qu.:188.65   3rd Qu.:223.6  
##  Max.   :210.77   Max.   :249.60   Max.   :306.47   Max.   :356.9  
##                                                                    
##      X40km         Chip.Time        Gender          Age       
##  Min.   :124.0   Min.   :130.7   Female:1322   Min.   :20.00  
##  1st Qu.:208.9   1st Qu.:220.6   Male  :5748   1st Qu.:32.00  
##  Median :230.6   Median :244.6                 Median :40.00  
##  Mean   :236.1   Mean   :249.9                 Mean   :40.23  
##  3rd Qu.:261.5   3rd Qu.:277.2                 3rd Qu.:47.00  
##  Max.   :408.8   Max.   :443.2                 Max.   :89.00  
## 

1.2 Data Visualization

2 Dimension Reduction

Dimension reduction simplifies data, enhances analysis, and improves model performance while minimizing information loss. The goal is to reduce dimensions while retaining critical information. Keeping redundant features may not benefit models, whereas reducing dimensions saves time, optimizes storage, enhances pattern recognition, and improves data interpretation.

2.1 Scale numerical variables

df_scaled <- scale(df[, sapply(df, is.numeric)])
colnames(df_scaled)
##  [1] "Position"        "Country"         "Gender.Position" "Age.Position"   
##  [5] "X5km"            "X10km"           "X15km"           "X20km"          
##  [9] "X21km"           "X25km"           "X30km"           "X35km"          
## [13] "X40km"           "Chip.Time"       "Gender"          "Age"

2.2 Principal Component Analysis (PCA)

Principal Component Analysis (PCA) transforms data into a new space where each dimension captures the most variance from the original dataset. The new features are highly correlated with some original features but uncorrelated with each other. It works by computing the mean and covariance matrix, finding eigenvectors and eigenvalues, selecting the top components, and projecting data onto a lower-dimensional space. PCA is useful for visualisation and feature extraction but is sensitive to outliers and assumes linear relationships.






pca_result <- prcomp(df_scaled, center = TRUE, scale = TRUE)
df_pca <- as.data.frame(pca_result$x[, 1:10]) 
str(df_pca)
## 'data.frame':    7070 obs. of  10 variables:
##  $ PC1 : num  -9.8 -9.72 -9.64 -9.8 -9.57 ...
##  $ PC2 : num  -0.67 -0.692 -0.7 -0.616 -0.586 ...
##  $ PC3 : num  -0.448 0.996 1.749 -1.922 0.775 ...
##  $ PC4 : num  -2.762 -0.167 1.256 -4.442 1.923 ...
##  $ PC5 : num  -0.722 -0.841 -0.936 -0.757 -1.155 ...
##  $ PC6 : num  -0.365 -0.423 -0.471 -0.262 -0.275 ...
##  $ PC7 : num  0.637 0.617 0.583 0.611 0.506 ...
##  $ PC8 : num  0.09453 0.07763 0.05371 0.04935 0.00511 ...
##  $ PC9 : num  0.564 0.559 0.538 0.515 0.493 ...
##  $ PC10: num  -0.221 -0.221 -0.2 -0.198 -0.193 ...

2.2.1 PCA Visualization

fviz_eig(pca_result)

A Scree Plot helps determine the optimal number of principal components to retain in PCA. It shows the percentage of explained variance for each principal component (PC). The first principal component (PC1) explains the largest portion of the variance (over 70%). PC2 explains much smaller amount of variance (around 10%). PC3 explain less than 10% of variance. The plot suggests that 3 principal components may be sufficient to capture most of the variance in the data. Keeping fewer components helps reduce computational complexity without losing significant information.

explained_var <- cumsum(pca_result$sdev^2 / sum(pca_result$sdev^2))
ggplot(data.frame(PC = seq_along(explained_var), Explained = explained_var), aes(x = PC, y = Explained)) +
  geom_line() +
  geom_point() +
  geom_hline(yintercept = 0.9, linetype = "dashed", color = "red") +
  labs(title = "Cumulative Explained Variance", x = "Principal Component", y = "Variance Explained") +
  theme_minimal()

Cumulative Explained Variance Plot helps determine the number of principal components (PCs) to retain in PCA by showing how much variance is explained as more components are added. The first 3 principal components capture around 90% of the variance.

Below are visualizations presenting data using 3 principal components.

2.2.2 Contribution of Variables to Principal Components

## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the ggpubr package.
##   Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
##   Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

On the first plot the color gradient shows the contribution of variables to the principal components (red contribute the most, blue - the least). Longer arrows indicate variables that strongly influence the principal components. Dim1 explains most of the variance (71.1%), Dim2 explains 11.7% variance in the data.

Times, Age Position and Gender Position have a strong positive correlation with Dim1. Dim1 likely represents a performance-related factor.

Gender, Gender Position and Age Position have strong negative correlation with Dim2. Dim2 may reflect demographic influences.

On the second plot we can see also features for PC3 - strong positive correlation for Country and negative for Age.

3 Clustering

Clustering is an unsupervised machine learning technique used to group similar data points together based on shared characteristics. It helps in discovering natural patterns within data, which is useful for market segmentation, anomaly detection, and data simplification. A good clustering result ensures high intra-cluster similarity (data points within a cluster are similar) and high inter-cluster dissimilarity (clusters are distinct from each other).

3.1 Determine optimal number of clusters

Below code helps determine the optimal number of clusters by measuring how well each point fits within its assigned cluster. A higher silhouette width indicates better-defined clusters. The NbClust() function performs clustering using Euclidean distance and evaluates numbers of clusters from 2 to 10 using the k-means algorithm, providing recommendations based on different criteria. These methods help select the best K-value for k-means clustering.

3.1.1 Hopkins Statistic

hopkins_stat <- hopkins(df_pca3)
cat("Hopkins Statistic:", hopkins_stat, "\n")
## Hopkins Statistic: 0.9986139

Hopkins value is close to 1, what suggests that the data is highly structured and clusterable, indicating a strong presence of natural clustering patterns.

3.1.2 Elbow Method

fviz_nbclust(df_pca3, kmeans, method = "wss")
## Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## ℹ The deprecated feature was likely used in the ggpubr package.
##   Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
## ℹ Please use the `linewidth` argument instead.
## ℹ The deprecated feature was likely used in the ggpubr package.
##   Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

In elbow method, total within-cluster sum of squares (WSS) decreases as k increases. The sharpest drop occurs at k = 2, suggesting that 2 clusters provide the optimal balance between compactness and separation.

3.1.3 Silhouette Method

fviz_nbclust(df_pca3, kmeans, method = "silhouette")

Silhouette width peaks at k=2, indicating that this is the number of clusters where data points are well-separated and well-clustered. High silhouette value means that points are well assigned to their clusters.

3.1.4 Gap Statistic Method

gap_stat <- clusGap(df_pca3, FUN = kmeans, K.max = 10, B = 50)
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 353500)
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 353500)
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 353500)
fviz_gap_stat(gap_stat)

Gap statistics, for 10 tested clusters, shows the biggest difference between observed clustering performance and a random clustering reference for k=10. However, choosing k=10 may result in over-segmentation.

3.1.5 Using NbClust R Package

NbClust(df_pca3, distance = "euclidean", min.nc = 2, max.nc = 10, method = "kmeans")

## *** : The Hubert index is a graphical method of determining the number of clusters.
##                 In the plot of Hubert index, we seek a significant knee that corresponds to a 
##                 significant increase of the value of the measure i.e the significant peak in Hubert
##                 index second differences plot. 
## 

## *** : The D index is a graphical method of determining the number of clusters. 
##                 In the plot of D index, we seek a significant knee (the significant peak in Dindex
##                 second differences plot) that corresponds to a significant increase of the value of
##                 the measure. 
##  
## ******************************************************************* 
## * Among all indices:                                                
## * 7 proposed 2 as the best number of clusters 
## * 4 proposed 3 as the best number of clusters 
## * 3 proposed 4 as the best number of clusters 
## * 3 proposed 6 as the best number of clusters 
## * 2 proposed 7 as the best number of clusters 
## * 1 proposed 8 as the best number of clusters 
## * 1 proposed 9 as the best number of clusters 
## * 2 proposed 10 as the best number of clusters 
## 
##                    ***** Conclusion *****                            
##  
## * According to the majority rule, the best number of clusters is  2 
##  
##  
## *******************************************************************
## $All.index
##         KL       CH  Hartigan      CCC     Scott      Marriot    TrCovW
## 2   3.5376 7378.009 3013.1390 -20.9819  7304.794 1.159685e+13 341994244
## 3   3.6842 6767.266 1704.7113 -21.1856 12470.467 1.256628e+13 143345330
## 4   0.6077 6167.147 1479.3839 -18.1149 18590.653 9.400151e+12 112845747
## 5   0.8066 5962.758 1353.5413 -15.6150 21944.930 9.139217e+12  71810990
## 6   1.3820 5953.967 1090.2959  -8.1088 30148.730 4.124110e+12  58137427
## 7  54.1488 5908.325  651.6622  -2.7555 31351.876 4.734969e+12  44144575
## 8   0.0313 5623.829  768.9184  -2.8346 32872.900 4.987327e+12  35332002
## 9   1.1047 5551.966  690.1949   0.2223 33736.324 5.586435e+12  26700140
## 10  6.6376 5493.383  489.6385   2.9742 35504.236 5.370946e+12  21826922
##      TraceW Friedman  Rubin Cindex     DB Silhouette   Duda   Pseudot2   Beale
## 2  49559.38   1.8075 2.0439 0.2006 0.9781     0.4222 0.8741   755.6162  0.2451
## 3  34746.64   4.7448 2.9152 0.1868 1.0614     0.3600 1.9360 -1623.0067 -0.8227
## 4  27993.91   5.8517 3.6184 0.1585 1.0573     0.3832 1.5537 -1413.7029 -0.6064
## 5  23147.58   8.1726 4.3759 0.1683 1.0178     0.3672 1.4591  -741.8972 -0.5353
## 6  19425.89  14.5062 5.2143 0.1483 0.9850     0.3689 0.9591    94.9545  0.0726
## 7  16828.49  16.1855 6.0191 0.1445 1.0248     0.3510 2.1690 -1115.0917 -0.9163
## 8  15406.98  16.3654 6.5745 0.1360 1.0731     0.3442 1.5917  -889.1966 -0.6324
## 9  13894.16  18.0580 7.2903 0.1497 1.0507     0.3461 1.5337  -618.3950 -0.5920
## 10 12656.98  18.7246 8.0029 0.1536 1.0043     0.3545 1.2228  -354.2553 -0.3100
##    Ratkowsky      Ball Ptbiserial   Frey McClain   Dunn Hubert SDindex Dindex
## 2     0.1958 24779.689     0.5291 0.5918  0.5217 0.0023      0  0.9389 2.4011
## 3     0.2120 11582.212     0.5495 0.4283  0.8475 0.0023      0  0.9674 2.0024
## 4     0.3035  6998.477     0.5542 0.6261  1.0124 0.0008      0  0.9030 1.7512
## 5     0.2858  4629.516     0.5280 0.6198  1.2981 0.0030      0  1.0583 1.6018
## 6     0.2801  3237.648     0.5113 0.4607  1.4575 0.0029      0  1.0524 1.4547
## 7     0.2616  2404.070     0.4969 0.9838  1.6121 0.0028      0  1.1383 1.3477
## 8     0.2783  1925.872     0.4607 0.2252  1.9359 0.0020      0  1.5201 1.2842
## 9     0.2656  1543.796     0.4581 0.2985  1.9762 0.0027      0  1.5281 1.2332
## 10    0.2656  1265.698     0.4530 1.1929  2.0308 0.0045      0  1.5878 1.1834
##      SDbw
## 2  1.2998
## 3  0.6721
## 4  0.4817
## 5  0.3791
## 6  0.4109
## 7  0.3372
## 8  0.3334
## 9  0.2955
## 10 0.3050
## 
## $All.CriticalValues
##    CritValue_Duda CritValue_PseudoT2 Fvalue_Beale
## 2          0.7525          1725.8683       0.8648
## 3          0.7379          1192.2735       1.0000
## 4          0.7362          1421.3477       1.0000
## 5          0.7342           853.6986       1.0000
## 6          0.7312           817.7587       0.9747
## 7          0.7063           860.3939       1.0000
## 8          0.7276           895.6252       1.0000
## 9          0.7258           671.3624       1.0000
## 10         0.7249           737.6135       1.0000
## 
## $Best.nc
##                      KL       CH Hartigan     CCC    Scott      Marriot
## Number_clusters  7.0000    2.000    3.000 10.0000    6.000 6.000000e+00
## Value_Index     54.1488 7378.009 1308.428  2.9742 8203.799 5.625967e+12
##                    TrCovW   TraceW Friedman   Rubin Cindex     DB Silhouette
## Number_clusters         3    3.000   6.0000  7.0000  8.000 2.0000     2.0000
## Value_Index     198648914 8060.012   6.3336 -0.2495  0.136 0.9781     0.4222
##                   Duda PseudoT2  Beale Ratkowsky     Ball PtBiserial Frey
## Number_clusters 2.0000   2.0000 2.0000    4.0000     3.00     4.0000    1
## Value_Index     0.8741 755.6162 0.2451    0.3035 13197.48     0.5542   NA
##                 McClain    Dunn Hubert SDindex Dindex   SDbw
## Number_clusters  2.0000 10.0000      0   4.000      0 9.0000
## Value_Index      0.5217  0.0045      0   0.903      0 0.2955
## 
## $Best.partition
##    [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##   [38] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##   [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [112] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [149] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [186] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [223] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [260] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [297] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [334] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [371] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [408] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [445] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [482] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [519] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [556] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [593] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [630] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [667] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [704] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [741] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [778] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [815] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [852] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [889] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [926] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [963] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1000] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1037] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1074] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1111] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1148] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1185] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1222] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1259] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1296] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1333] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1370] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1407] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1444] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1481] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1518] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1555] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1592] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1629] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1666] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1703] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1740] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1777] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1814] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1851] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1888] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1925] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1962] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [1999] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2036] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2073] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2110] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2147] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2184] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2221] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2258] 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2295] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2332] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2369] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2406] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2443] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2480] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2517] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2554] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2591] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2628] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2665] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2702] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2739] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2776] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2813] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
## [2850] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2887] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2924] 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2961] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [2998] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3035] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3072] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3109] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3146] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3183] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1
## [3220] 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3257] 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3294] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3331] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1
## [3368] 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [3405] 1 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 2
## [3442] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1
## [3479] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1
## [3516] 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 2
## [3553] 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1
## [3590] 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 2
## [3627] 2 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 2 1 1 2 1 2 2 2 1 1 1 2 1 1 1 1
## [3664] 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 2 1 2
## [3701] 1 1 2 2 1 1 1 1 2 1 1 2 2 1 2 2 2 2 2 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1
## [3738] 1 1 2 2 1 1 1 1 2 1 1 2 1 1 1 2 1 2 1 1 1 2 1 1 2 1 1 2 1 2 1 1 1 2 1 2 2
## [3775] 1 2 1 1 2 1 2 1 2 1 1 1 2 2 1 2 2 2 2 1 1 1 2 1 2 1 2 2 2 1 1 1 1 1 2 2 2
## [3812] 2 1 1 1 2 1 2 1 1 1 2 2 1 1 2 2 2 2 1 1 1 2 2 1 2 2 2 2 1 1 2 2 1 2 1 1 1
## [3849] 1 1 1 2 1 2 2 1 2 2 1 1 1 2 1 1 1 2 2 1 2 1 2 1 1 2 1 2 1 1 1 1 2 1 2 2 2
## [3886] 2 2 2 1 2 2 1 2 2 1 2 1 1 1 1 2 1 1 2 1 2 1 1 2 1 1 2 1 2 2 1 2 1 1 1 2 2
## [3923] 1 2 1 1 2 2 2 1 1 1 1 2 1 1 2 2 1 1 1 1 1 2 1 1 1 1 1 2 2 2 2 1 2 1 2 1 2
## [3960] 1 2 2 2 1 2 2 1 1 1 2 1 1 1 1 1 2 2 2 2 2 1 2 1 1 2 2 1 2 1 2 2 1 1 2 2 1
## [3997] 1 1 2 1 2 1 1 1 1 2 1 2 1 2 1 2 2 1 2 2 1 1 2 1 2 2 1 2 1 2 2 2 2 1 2 1 1
## [4034] 2 2 1 1 1 2 2 1 1 1 1 2 1 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 1 1 2 1 2
## [4071] 1 2 1 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 1 1 2 1 2 1 2 1 1 2
## [4108] 2 2 2 2 2 2 1 1 2 1 2 1 2 2 2 2 2 1 1 2 2 2 1 2 1 1 2 1 2 1 2 2 1 1 2 2 1
## [4145] 2 2 2 2 1 2 2 2 2 2 2 1 1 2 2 2 1 2 1 2 2 2 2 2 2 2 2 1 2 1 2 1 2 2 2 2 2
## [4182] 2 2 1 2 2 1 2 1 2 2 1 2 2 2 1 2 1 1 2 2 2 1 2 1 2 1 2 2 2 2 2 2 2 1 2 1 2
## [4219] 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 1 1 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 2 2 1 1
## [4256] 2 2 2 1 1 2 2 2 1 2 2 2 2 2 1 2 1 1 2 1 2 2 2 1 2 2 1 2 2 2 2 1 1 2 2 1 2
## [4293] 1 2 2 2 1 1 2 1 2 2 2 2 2 2 1 2 2 2 1 2 2 1 2 2 2 1 2 2 2 2 2 2 2 2 2 1 2
## [4330] 2 2 2 2 1 1 1 2 2 2 2 2 2 1 2 2 1 2 2 2 1 2 2 2 2 2 2 2 2 2 2 1 2 2 1 1 1
## [4367] 2 2 1 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 1 2 2 2 1 2 2 2 1 2 2 2 1 1 1 2 2 2 2
## [4404] 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2
## [4441] 2 2 2 2 1 2 2 2 2 2 1 2 2 2 1 2 2 2 1 2 2 2 1 2 2 2 2 2 2 1 2 2 2 2 2 2 2
## [4478] 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2
## [4515] 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4552] 2 2 2 1 2 2 1 2 1 1 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4589] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2
## [4626] 2 2 1 2 1 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 1 2 2 2 2
## [4663] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1
## [4700] 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 1 2 2 2 2 2 1 2 2
## [4737] 2 2 2 2 2 2 2 2 2 2 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2
## [4774] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4811] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4848] 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2
## [4885] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2
## [4922] 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4959] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [4996] 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 1 2 2 2
## [5033] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5070] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5107] 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2
## [5144] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5181] 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5218] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5255] 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5292] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5329] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2
## [5366] 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5403] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5440] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5477] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2
## [5514] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5551] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5588] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5625] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5662] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2
## [5699] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5736] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5773] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5810] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5847] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5884] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5921] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5958] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [5995] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6032] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6069] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6106] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6143] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6180] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6217] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6254] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6291] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6328] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6365] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6402] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6439] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6476] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6513] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6550] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6587] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6624] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6661] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6698] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6735] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6772] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6809] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6846] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6883] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6920] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [6957] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2
## [6994] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [7031] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [7068] 2 2 2

NbClust package provides 30 indices for determining the number of clusters and proposes to user the best clustering scheme from the different results obtained by varying all combinations of number of clusters, distance measures, and clustering methods.

For further analysis I select k=2 as numbers of clusters, as suggested.

3.2 K-Means Clustering

K-means is a centroid-based clustering algorithm that partitions data into k clusters by minimizing the variance within each cluster. It iteratively assigns points to the nearest cluster center (centroid), then recalculates centroids based on the assigned points until convergence. It requires the number of clusters k to be predefined and struggles with non-spherical or unevenly sized clusters. It is sensitive to outliers and initial centroid placement, which can lead to different results across runs.

kmeans_result <- kmeans(df_pca3, 2)

3.2.1 K-Means Visualisation

fviz_silhouette(silhouette(kmeans_result$cluster, dist(df_pca3[, c("PC1", "PC2", "PC3")])))
##   cluster size ave.sil.width
## 1       1 3989          0.47
## 2       2 3081          0.36

The k-means clustering results show clusters with some overlapping data points in the center. Average silhouette width of is 0.42, which suggests moderate clustering quality. High silhouette score (>0.7) would suggest that points within clusters are well-grouped and distinct from other clusters. Low average silhouette width suggest that some data points may be misclassified or the clusters may not be well-separated.

Cluster 1 contains 3,989 data points with an average silhouette width of 0.47, which suggests moderate clustering quality.

Cluster 2 contains 3,081 data points with an average silhouette width of 0.36, suggesting that data points are more dispersed.

3.3 PAM Clustering

Partitioning Around Medoids (PAM) is a clustering algorithm that selects representative points, called medoids, as cluster centers instead of centroids like k-means. It begins with an initial set of medoids and iteratively replace them with non-medoid points to minimize the total dissimilarity within clusters. PAM is useful for small datasets and is more robust to noise and outliers since medoids are less influenced by extreme values than means.

pam_result <- pam(df_pca3, k = 2)

3.3.1 PAM Visualization

##   cluster size ave.sil.width
## 1       1 4095          0.46
## 2       2 2975          0.37

The PAM clustering results show that average silhouette width of is 0.42. Like for k-means clustering, this is moderate silhouette score.

Cluster 1 contains 4,095 data points with an average silhouette width of 0.46.

Cluster 2 contains 2,975 data points with an average silhouette width of 0.37, suggesting that data points are more dispersed.

3.4 CLARA Clustering

CLARA (Clustering Large Applications) is an extension of the PAM algorithm for large datasets. It works by drawing multiple samples from the dataset, applying PAM to each sample, and selecting the best clustering result. The main advantage of CLARA is its ability to cluster larger datasets efficiently compared to PAM. However, its effectiveness depends on the sample size, and if the sample is biased, the clustering results may not generalize well to the entire dataset.

clara_result <- clara(df_pca3, k = 2)

3.4.1 CLARA Visualisation

##   cluster size ave.sil.width
## 1       1 3524          0.48
## 2       2 3546          0.34

The CLARA clustering results show that average silhouette width of is 0.41. Like for 2 previous clustering methods, this is moderate silhouette score.

Cluster 1 contains 3,524 data points with an average silhouette width of 0.48.

Cluster 2 contains 3,546 data points with an average silhouette width of 0.34, suggesting that data points are more dispersed.

3.5 Hierarchical Clustering

Hierarchical clustering builds a hierarchy of clusters using either an agglomerative (bottom-up) or divisive (top-down) approach. It does not require the number of clusters to be predefined and can be visualized using a dendrogram, which helps determine the optimal number of clusters. However, it is computationally expensive for large datasets and does not allow for cluster reassignment after merging or splitting. Different linkage methods influence the shape and size of the resulting clusters.

hclust_result <- hclust(dist(df_pca3), method = "ward.D2")
hclust_clusters <- cutree(hclust_result, k = 2)

3.5.1 Hierarchical Clustering Visualization

Dendrogram visualizes the hierarchical clustering of data based on Ward’s method and the Euclidean distance. Y-axis represents the level at which clusters are merged. Higher values indicate that clusters are more dissimilar. The red rectangles indicate the cut-off level for defining two clusters. Since the two main clusters merge at a relatively high height, it suggests they are well-separated.

3.6 DBSCAN (Density-Based Spatial Clustering)

DBSCAN groups data points based on density, allowing for clusters of arbitrary shapes and the identification of noise and outliers. It defines core points with a minimum number of neighbors within a specified radius (eps) and expands clusters from them. Unlike k-means, it does not require specifying the number of clusters but is sensitive to the choice of parameters (eps and MinPts). It performs well with spatial and non-linearly separable data but struggles when clusters have varying densities.

dbscan_result <- dbscan::dbscan(df_pca3, eps = 1.5, minPts = 5)
db <- df_pca3[, c("PC1", "PC2")]

fviz_cluster(dbscan_result, db, stand = FALSE, frame = FALSE, geom = "point", main = "2D PCA with DBSCAN Clustering", ggtheme = theme_minimal())
## Warning: argument frame is deprecated; please use ellipse instead.

print(dbscan_result)
## DBSCAN clustering for 7070 objects.
## Parameters: eps = 1.5, minPts = 5
## Using euclidean distances and borderpoints = TRUE
## The clustering contains 2 cluster(s) and 8 noise points.
## 
##    0    1    2 
##    8 5746 1316 
## 
## Available fields: cluster, eps, minPts, metric, borderPoints
##   cluster size ave.sil.width
## 0       0    8         -0.13
## 1       1 5746          0.23
## 2       2 1316          0.33

First 2 plots show 2 clusters identified by DBSCAN, which contain the main data points (5746 and 1316 respectively). Black dots or cluster 0 represent noise points. Third plot presents silhouette analysis with low average silhouette width 0.25, indicating weak clustering quality. Cluster 2 has the highest silhouette width (0.33), meaning it is better defined than cluster 1 (0.23) and cluster 0 (noise) (-0.13), confirming that these points do not fit well into any cluster.

Despite the low silhouette score, clusters on the first 2 plots appear well-separated with minimal overlap. DBSCAN effectively identified groups of data points and filtered outliers.

Given the loading factors of PC2 (e.g., for Gender and Gender Position), the analysis of raw data groupped by Gender and clusters’ size after DBSCAN clustering reveals a clear separation of data points based on Gender. This suggests that Gender significantly influence the clustering structure, as captured by the second principal component (PC2).

3.7 Clustering Summary

The table below presents the cluster’s number assigned to few data point after applying various clustering methods.

df_pca3$kmeans_cluster <- factor(kmeans_result$cluster)
df_pca3$pam_cluster <- factor(pam_result$cluster)
df_pca3$clara_cluster <- factor(clara_result$cluster)
df_pca3$hclust_cluster <- factor(hclust_clusters)
df_pca3$dbscan_cluster <- factor(dbscan_result$cluster)

head(df_pca3)
##         PC1        PC2        PC3 kmeans_cluster pam_cluster clara_cluster
## 1 -9.795978 -0.6704226 -0.4476079              1           1             1
## 2 -9.718017 -0.6923608  0.9956973              1           1             1
## 3 -9.642648 -0.6999396  1.7485630              1           1             1
## 4 -9.802982 -0.6160933 -1.9222013              1           1             1
## 5 -9.565574 -0.5861277  0.7750301              1           1             1
## 6 -9.231714 -0.4519810 -1.0537336              1           1             1
##   hclust_cluster dbscan_cluster
## 1              1              1
## 2              1              1
## 3              1              1
## 4              1              1
## 5              1              1
## 6              1              1

3.8 Clustering Quality Measures

One of the quality measures for clustering is the silhouette method. Below is a summary table presenting the average silhouette scores for all applied clustering methods.

library(knitr)

sil1 <- silhouette(kmeans_result$cluster, dist(df_pca3[, c("PC1", "PC2", "PC3")]))
sil2 <- silhouette(pam_result$cluster, dist(df_pca3[, c("PC1", "PC2", "PC3")]))
sil3 <- silhouette(clara_result$cluster, dist(df_pca3[, c("PC1", "PC2", "PC3")]))
sil4 <- silhouette(hclust_clusters, dist(df_pca3[, c("PC1", "PC2", "PC3")]))
sil5 <- silhouette(dbscan_result$cluster, dist(df_pca3[, c("PC1", "PC2", "PC3")]))

mean_sil_scores <- data.frame(
  "Clustering Method" = c("K-Means", "PAM", "CLARA", "Hierarchical", "DBSCAN"),
  "Mean Silhouette Score" = c(
    mean(sil1[, 3]), 
    mean(sil2[, 3]), 
    mean(sil3[, 3]), 
    mean(sil4[, 3]), 
    mean(sil5[, 3])
  )
)

kable(mean_sil_scores)
Clustering.Method Mean.Silhouette.Score
K-Means 0.4221516
PAM 0.4226154
CLARA 0.4100061
Hierarchical 0.3940863
DBSCAN 0.2506902

Average silhouette scores, below 0.5, for all applied clustering methods indicate moderate or low clustering quality. This means that some data points may not be well classified, or clusters may not be well-separated.

3.9 Feature Distributions Across Clusters

Because PC1 mainly represents a performance-related factor, I wanted verify whether there are differences for the average times for each cluster across all clustering methods.

## # A tibble: 11 × 14
##    Cluster Method        X5km X10km X15km X20km X21km X25km X30km X35km X40km
##    <fct>   <chr>        <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 1       K-Means       25.3  50.7  75.7  101.  107.  127.  153.  180.  208.
##  2 2       K-Means       31.1  62.7  94.0  127.  134.  161.  196.  233.  272.
##  3 1       PAM           25.4  50.9  76.0  102.  108.  128.  154.  181.  209.
##  4 2       PAM           31.2  62.9  94.3  127.  135.  161.  196.  234.  273.
##  5 1       CLARA         25.0  50.0  74.7  100.  106.  125.  151.  177.  205.
##  6 2       CLARA         30.7  61.8  92.6  125.  132.  158.  192.  229.  267.
##  7 1       Hierarchical  25.8  51.7  77.3  104.  109.  130.  156.  184.  213.
##  8 2       Hierarchical  31.3  62.9  94.4  128.  135.  162.  197.  235.  274.
##  9 0       DBSCAN        33.9  70.9 108.   145.  153.  182.  221.  260.  299.
## 10 1       DBSCAN        27.5  55.0  82.4  111.  117.  139.  169.  200.  232.
## 11 2       DBSCAN        29.7  59.6  89.4  121.  128.  152.  184.  218.  252.
## # ℹ 3 more variables: Chip.Time <dbl>, Avg_Age <dbl>, Total_Count <int>

As presented above, differences in runners’ performance are clearly visible for K-Means, PAM, CLARA, and Hierarchical clustering methods, even though they were not so clear in previous analyses. Average silhouette scores for all applied clustering methods indicated moderate or low clustering quality, however, the clusters might be better defined if only Chip Time were considered.

For DBSCAN, the clustering rather reflects a gender split, as discussed earlier. The average times for clusters 1 and 2 are similar, whereas the noise cluster shows significantly higher average times.

4 Summary

Above analysis of runners’ performance data from the 46. Warsaw Marathon (2024) shows application of unsupervised learning techniques, including PCA for dimension reduction and clustering methods such as K-Means, Hierarchical, PAM, CLARA, and DBSCAN. K-Means performed best in terms of clustering quality, while DBSCAN effectively identified outliers but had much lower average silhouette width. NbClust package analysis indicated that two clusters provide the best separation. Overall, clustering helped reveal distinct performance groups among runners, with key insights into race times.