Skip to contents

A fixed 480-participant benchmark for evaluating train-once/project-many clustering workflows. Twelve numeric variables support at least four retained principal components and four balanced truth groups. Three four-level categorical variables provide strongly separated, class-dependent response patterns for categorical-method demonstrations, while DensityX and DensityY provide two unequal-density groups plus noise. TruthCluster and TruthDensityGroup are retained solely for teaching and test evaluation; they must not be used as clustering features.

Usage

data(SimulatedPhenotypeData)

Format

SimulatedPhenotypeData has 480 rows: 320 training participants and 160 projection participants. It contains Var1 through Var12, CatVar1 through CatVar3, density coordinates, truth fields, and the fixed cohort split. SimulatedPhenotypeVariableTypes is its complete labelled codebook.

Why the benchmark works

The twelve numeric variables are organized in blocks, and each cluster is high on a different block. That is the structure a clustering method is supposed to recover, and the reason a method that finds nothing here is genuinely failing rather than facing an impossible problem.

DensityX and DensityY are a separate and deliberately harder problem: two groups of unequal density plus a noise group, for methods that are meant to detect outliers rather than partition everything.

TruthCluster and TruthDensityGroup exist only so results can be scored. They must never be used as clustering features.

See also

SampleData for the labelling and reporting examples, and CreateClusterModel_SOM_MClust() or CreateClusterModel_PCA_KMeans() for the pipelines this benchmark was built for.

Examples

data(SimulatedPhenotypeData)

# 480 participants, with the truth clusters balanced across both cohorts
dim(SimulatedPhenotypeData)
#> [1] 480  21
table(SimulatedPhenotypeData$Cohort, SimulatedPhenotypeData$TruthCluster)
#>             
#>              Cluster 1 Cluster 2 Cluster 3 Cluster 4
#>   Projection        40        40        40        40
#>   Training          80        80        80        80

# \donttest{
data(SimulatedPhenotypeVariableTypes)

ShowTable <- function(x, caption = NULL, height = NULL) {
  htmltools::browsable(htmltools::HTML(as.character(
    kableExtra::kable_styling(
      knitr::kable(x, format = "html", caption = caption, row.names = FALSE),
      bootstrap_options = c("striped", "hover", "condensed"),
      full_width = FALSE
    )
  )))
}

# A slice: identifiers, clustering variables, and the truth labels
ShowTable(
  utils::head(
    SimulatedPhenotypeData[, c(
      "ParticipantID", "Cohort", "TruthCluster",
      paste0("Var", 1:6), "CatVar1", "DensityX", "DensityY"
    )],
    8
  ),
  "First eight participants"
)
First eight participants
ParticipantID Cohort TruthCluster Var1 Var2 Var3 Var4 Var5 Var6 CatVar1 DensityX DensityY
SIM001 Training Cluster 1 3.771776 2.7355646 2.863499 3.914050 3.059559 4.303554 Item 1: A -2.064189 -2.481255
SIM002 Training Cluster 1 4.232290 4.4791488 2.201403 2.872511 1.979931 4.024696 Item 1: A -2.337098 -1.959076
SIM003 Training Cluster 1 1.311219 1.9530996 3.169707 1.216638 1.516512 1.153082 Item 1: A -1.920937 -1.448738
SIM004 Training Cluster 1 1.706363 2.7635838 2.470655 2.859102 3.381282 1.753918 Item 1: A -2.108988 -2.276855
SIM005 Training Cluster 1 2.663587 1.8009096 2.448535 1.376916 1.658445 1.654548 Item 1: A -1.567415 -2.191837
SIM006 Training Cluster 1 1.275155 2.5438145 1.425758 2.698406 2.003458 2.184338 Item 1: A -2.150969 -1.945131
SIM007 Training Cluster 1 3.016928 2.0806831 3.405781 3.051471 2.414904 1.618949 Item 1: A -2.308742 -1.543729
SIM008 Training Cluster 1 1.603573 0.4652913 2.703841 2.903235 3.626558 3.405072 Item 1: A -2.736425 -1.936006
# Cluster means, showing the block structure df_Training <- SimulatedPhenotypeData[ SimulatedPhenotypeData$Cohort == "Training", ] df_ClusterMeans <- stats::aggregate( df_Training[paste0("Var", 1:12)], by = list(Cluster = df_Training$TruthCluster), FUN = mean ) df_ClusterMeans[-1] <- round(df_ClusterMeans[-1], 2) htmltools::browsable(htmltools::HTML(as.character( FreezeTableHeader(df_ClusterMeans, full_width = TRUE) )))
Cluster Var1 Var2 Var3 Var4 Var5 Var6 Var7 Var8 Var9 Var10 Var11 Var12
Cluster 1 2.49 2.39 2.58 2.56 2.62 2.52 2.63 2.47 2.56 0.23 -0.10 -0.08
Cluster 2 2.70 2.67 2.58 -2.52 -2.63 -2.80 -2.75 -2.65 -2.59 -0.44 -0.44 -0.15
Cluster 3 -2.47 -2.49 -2.48 2.56 2.32 2.38 -2.35 -2.62 -2.65 0.40 0.31 0.19
Cluster 4 -2.45 -2.44 -2.43 -2.58 -2.26 -2.51 2.64 2.48 2.30 0.98 1.09 1.10
# The density variables: two unequal groups plus noise table(SimulatedPhenotypeData$TruthDensityGroup) #> #> DenseA DenseB Noise #> 192 192 96 ggplot2::ggplot( SimulatedPhenotypeData, ggplot2::aes( x = DensityX, y = DensityY, colour = TruthDensityGroup ) ) + ggplot2::geom_point(alpha = 0.7, size = 1.6) + ggplot2::labs( title = "Density groups, with noise", colour = "Truth group" ) + ggplot2::theme_bw() # The companion codebook ShowTable(SimulatedPhenotypeVariableTypes, "SimulatedPhenotypeVariableTypes")
SimulatedPhenotypeVariableTypes
Variable Label Type Recode Code MissingCode
ParticipantID Participant identifier Categorical No NA NA
Cohort Cohort Categorical No NA NA
TruthCluster Simulated cluster (truth only) Categorical No NA NA
TruthDensityGroup Simulated density group (truth only) Categorical No NA NA
Var1 Var 1 Double No NA NA
Var2 Var 2 Double No NA NA
Var3 Var 3 Double No NA NA
Var4 Var 4 Double No NA NA
Var5 Var 5 Double No NA NA
Var6 Var 6 Double No NA NA
Var7 Var 7 Double No NA NA
Var8 Var 8 Double No NA NA
Var9 Var 9 Double No NA NA
Var10 Var 10 Double No NA NA
Var11 Var 11 Double No NA NA
Var12 Var 12 Double No NA NA
CatVar1 CatVar 1 Categorical No NA NA
CatVar2 CatVar 2 Categorical No NA NA
CatVar3 CatVar 3 Categorical No NA NA
DensityX Density X Double No NA NA
DensityY Density Y Double No NA NA
# }