Introduction

The purpose of this analysis is to identify groups of customers with similar clothing and shopping preferences. Businesses can use these groups to better understand their customers and develop more relevant marketing strategies. I used hierarchical cluster analysis to divide the survey respondents into three groups and compared their average survey ratings to understand how the groups differ.

Import and Check the Data

# Accept either filename
possible_files <- c(
  "Segmentation.csv",
  "customer_segmentation.csv"
)

available_files <- possible_files[file.exists(possible_files)]

if (length(available_files) == 0) {
  stop("Upload Segmentation.csv to the folder containing this R Markdown file.")
}

mydata <- read.csv(available_files[1])

if (!"ID" %in% names(mydata)) {
  stop("The dataset must contain an ID column.")
}

# Exclude ID from the survey variables
survey_data <- mydata[, names(mydata) != "ID", drop = FALSE]

if (!all(vapply(survey_data, is.numeric, logical(1)))) {
  stop("All survey variables must be numeric.")
}

# Remove respondents with missing survey responses
complete_rows <- complete.cases(survey_data)
analysis_data <- mydata[complete_rows, , drop = FALSE]
survey_data <- survey_data[complete_rows, , drop = FALSE]

if (nrow(survey_data) < 4) {
  stop("At least four complete respondents are needed.")
}

if (any(!is.finite(as.matrix(survey_data)))) {
  stop("Check the dataset for infinite or invalid values.")
}

# Exclude variables that have no variation
variable_sd <- vapply(survey_data, sd, numeric(1))
survey_data <- survey_data[, variable_sd > 0, drop = FALSE]

if (ncol(survey_data) == 0) {
  stop("No survey variables with variation remain.")
}

# Display the data
head(analysis_data)
##   ID Fashn Price Convnience ShpTime Fitness Perceptn ChNoise RetailEx
## 1  1     1     6          7       6       6        4       4        4
## 2  2     2     7          6       6       6        6       2        6
## 3  3     1     5          6       7       3        3       7        3
## 4  4     1     7          4       4       6        3       7        4
## 5  5     3     5          3       4       6        5       5        6
## 6  6     2     6          2       3       6        5       2        5
##   KnowdgStaf Brand4Slf Brand4Els Populr StrDisp SaleStaf Fabric Cut Seam
## 1          4         1         1      1       1        1      3   4    1
## 2          4         3         5      4       1        5      5   6    6
## 3          3         1         1      3       1        4      1   4    4
## 4          4         1         1      1       7        6      5   4    4
## 5          6         2         2      2       4        4      6   6    6
## 6          2         2         2      2       1        1      6   6    6
##   ShpOHngr ShpOBody Colrs Match
## 1        1        1     1     1
## 2        6        7     4     4
## 3        4        4     4     1
## 4        5        5     5     1
## 5        5        6     6     6
## 6        2        6     6     2
summary(survey_data)
##      Fashn           Price        Convnience       ShpTime         Fitness     
##  Min.   :1.000   Min.   :1.00   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:4.00   1st Qu.:2.000   1st Qu.:3.000   1st Qu.:5.000  
##  Median :3.000   Median :5.00   Median :4.000   Median :4.000   Median :6.000  
##  Mean   :3.204   Mean   :4.67   Mean   :3.765   Mean   :4.081   Mean   :5.724  
##  3rd Qu.:4.000   3rd Qu.:6.00   3rd Qu.:5.000   3rd Qu.:5.000   3rd Qu.:6.000  
##  Max.   :7.000   Max.   :7.00   Max.   :7.000   Max.   :7.000   Max.   :7.000  
##     Perceptn        ChNoise         RetailEx       KnowdgStaf   
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:3.000   1st Qu.:3.000   1st Qu.:4.000   1st Qu.:4.000  
##  Median :5.000   Median :4.000   Median :6.000   Median :5.000  
##  Mean   :4.271   Mean   :4.167   Mean   :5.271   Mean   :4.665  
##  3rd Qu.:5.000   3rd Qu.:6.000   3rd Qu.:6.000   3rd Qu.:6.000  
##  Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.000  
##    Brand4Slf       Brand4Els         Populr         StrDisp     
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:3.000   1st Qu.:2.000   1st Qu.:2.000  
##  Median :4.000   Median :4.000   Median :4.000   Median :4.000  
##  Mean   :3.756   Mean   :3.959   Mean   :3.656   Mean   :3.484  
##  3rd Qu.:5.000   3rd Qu.:5.000   3rd Qu.:5.000   3rd Qu.:4.000  
##  Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.000  
##     SaleStaf         Fabric           Cut             Seam          ShpOHngr   
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.00  
##  1st Qu.:4.000   1st Qu.:5.000   1st Qu.:5.000   1st Qu.:4.000   1st Qu.:3.00  
##  Median :5.000   Median :6.000   Median :6.000   Median :5.000   Median :4.00  
##  Mean   :4.412   Mean   :5.394   Mean   :5.864   Mean   :5.072   Mean   :4.14  
##  3rd Qu.:6.000   3rd Qu.:6.000   3rd Qu.:7.000   3rd Qu.:6.000   3rd Qu.:5.00  
##  Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.00  
##     ShpOBody         Colrs           Match      
##  Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:6.000   1st Qu.:4.000   1st Qu.:2.000  
##  Median :7.000   Median :6.000   Median :4.000  
##  Mean   :6.299   Mean   :5.222   Mean   :3.986  
##  3rd Qu.:7.000   3rd Qu.:6.000   3rd Qu.:6.000  
##  Max.   :7.000   Max.   :7.000   Max.   :7.000

The original dataset contains 221 respondents. After checking for missing responses, 221 respondents and 21 survey variables were used in the analysis.

The ID column was excluded because identification numbers do not measure customer preferences.

Standardize the Survey Variables

survey_scaled <- scale(survey_data)

head(survey_scaled)
##        Fashn     Price Convnience    ShpTime    Fitness   Perceptn     ChNoise
## 1 -1.3668217 0.8376530  1.8298065  1.1350645  0.2472547 -0.1834752 -0.09249362
## 2 -0.7465597 1.4673174  1.2642299  1.1350645  0.2472547  1.1681253 -1.19741737
## 3 -1.3668217 0.2079887  1.2642299  1.7266902 -2.4401202 -0.8592754  1.56489202
## 4 -1.3668217 1.4673174  0.1330768 -0.0481867  0.2472547 -0.8592754  1.56489202
## 5 -0.1262977 0.2079887 -0.4324997 -0.0481867  0.2472547  0.4923250  0.45996826
## 6 -0.7465597 0.8376530 -0.9980763 -0.6398123  0.2472547  0.4923250 -1.19741737
##     RetailEx KnowdgStaf  Brand4Slf  Brand4Els     Populr    StrDisp   SaleStaf
## 1 -0.9755685 -0.3752561 -1.4896869 -1.7234438 -1.4273102 -1.6491360 -2.1279568
## 2  0.5589556 -0.3752561 -0.4085020  0.6061041  0.1847966 -1.6491360  0.3668891
## 3 -1.7428306 -0.9394165 -1.4896869 -1.7234438 -0.3525724 -1.6491360 -0.2568224
## 4 -0.9755685 -0.3752561 -1.4896869 -1.7234438 -1.4273102  2.3340231  0.9906006
## 5  0.5589556  0.7530649 -0.9490944 -1.1410568 -0.8899413  0.3424436 -0.2568224
## 6 -0.2083065 -1.5035770 -0.9490944 -1.1410568 -0.8899413 -1.6491360 -2.1279568
##       Fabric        Cut       Seam    ShpOHngr   ShpOBody      Colrs
## 1 -1.7824430 -1.5363576 -2.4439778 -1.89734098 -4.9479613 -2.8226580
## 2 -0.2931428  0.1118707  0.5566838  1.12364141  0.6549394 -0.8168464
## 3 -3.2717432 -1.5363576 -0.6435808 -0.08475154 -2.1465110 -0.8168464
## 4 -0.2931428 -1.5363576 -0.6435808  0.51944494 -1.2126942 -0.1482425
## 5  0.4515073  0.1118707  0.5566838  0.51944494 -0.2788774  0.5203614
## 6  0.4515073  0.1118707  0.5566838 -1.29314450 -0.2788774  0.5203614
##          Match
## 1 -1.549754115
## 2  0.007044337
## 3 -1.549754115
## 4 -1.549754115
## 5  1.044909971
## 6 -1.030821298

Standardization puts the variables on a comparable scale, with a mean of zero and a standard deviation of one. This helps prevent variables with greater numerical variation from dominating the clustering.

Hierarchical Cluster Analysis

# Calculate Euclidean distances between respondents
customer_distances <- dist(
  survey_scaled,
  method = "euclidean"
)

# Perform complete-linkage hierarchical clustering
seg.hclust <- hclust(
  customer_distances,
  method = "complete"
)

# Plot the dendrogram
plot(
  seg.hclust,
  labels = FALSE,
  hang = -1,
  main = "Customer Segmentation Dendrogram",
  xlab = "Customers",
  ylab = "Merge Height",
  sub = ""
)

# Highlight three clusters
rect.hclust(
  seg.hclust,
  k = 3,
  border = c("steelblue", "darkorange", "darkgreen")
)

# Assign each respondent to a cluster
groups.3 <- cutree(seg.hclust, k = 3)

Hierarchical clustering groups respondents according to the similarity of their standardized survey ratings. The dendrogram shows how respondents and groups are merged. Groups that merge at greater heights are more different.

I selected three clusters to follow the lab exercise. Further analysis would be needed to determine whether three is the most appropriate number of segments.

Cluster Sizes

cluster_counts <- table(factor(groups.3, levels = 1:3))

cluster_sizes <- data.frame(
  Cluster = 1:3,
  Customers = as.integer(cluster_counts),
  Percentage = round(
    100 * as.integer(cluster_counts) / length(groups.3),
    1
  )
)

knitr::kable(
  cluster_sizes,
  caption = "Customer Counts by Cluster"
)
Customer Counts by Cluster
Cluster Customers Percentage
1 6 2.7
2 90 40.7
3 125 56.6

Cluster 1 contains 6 customers (2.7%), Cluster 2 contains 90 customers (40.7%), and Cluster 3 contains 125 customers (56.6%).

These percentages describe the analyzed sample. They do not necessarily represent the entire customer market.

Cluster Means and Medians

cluster_means <- aggregate(
  survey_data,
  by = list(Cluster = groups.3),
  FUN = mean
)

cluster_medians <- aggregate(
  survey_data,
  by = list(Cluster = groups.3),
  FUN = median
)

knitr::kable(
  cluster_means,
  digits = 2,
  caption = "Average Survey Ratings by Cluster"
)
Average Survey Ratings by Cluster
Cluster Fashn Price Convnience ShpTime Fitness Perceptn ChNoise RetailEx KnowdgStaf Brand4Slf Brand4Els Populr StrDisp SaleStaf Fabric Cut Seam ShpOHngr ShpOBody Colrs Match
1 2.50 5.17 5.00 5.17 4.83 3.33 3.67 5.67 5.00 3.00 2.67 2.67 2.50 1.67 2.17 2.33 1.50 1.33 2.17 2.17 1.33
2 3.44 4.91 3.71 4.06 5.96 4.66 3.77 4.66 3.66 4.08 4.03 3.69 3.56 4.07 5.52 5.87 5.32 4.28 6.38 5.09 3.73
3 3.06 4.47 3.74 4.05 5.60 4.04 4.48 5.70 5.38 3.56 3.97 3.68 3.48 4.79 5.46 6.03 5.06 4.18 6.44 5.46 4.30
knitr::kable(
  cluster_medians,
  digits = 2,
  caption = "Median Survey Ratings by Cluster"
)
Median Survey Ratings by Cluster
Cluster Fashn Price Convnience ShpTime Fitness Perceptn ChNoise RetailEx KnowdgStaf Brand4Slf Brand4Els Populr StrDisp SaleStaf Fabric Cut Seam ShpOHngr ShpOBody Colrs Match
1 1.5 5.5 5 5 5 3.5 3.5 6 5 3 2.5 2.5 2 1.5 2 2 1 1 2 2 1
2 3.0 5.0 3 4 6 5.0 4.0 5 4 4 4.0 4.0 4 4.0 5 6 5 4 7 5 3
3 3.0 5.0 4 4 6 4.0 5.0 6 6 4 4.0 4.0 4 5.0 6 6 5 4 7 6 5

The means and medians help explain what makes each group different. Comparing the same survey variable across clusters shows differences in response patterns. The questionnaire should be checked before interpreting a higher rating as greater importance or stronger agreement.

Interpretation of Each Cluster

The following interpretations compare each cluster’s average ratings with the averages for all other respondents. Positive differences indicate higher ratings, while negative differences indicate lower ratings.

for (cluster_number in 1:3) {

  in_cluster <- groups.3 == cluster_number

  group_means <- colMeans(
    survey_data[in_cluster, , drop = FALSE]
  )

  other_means <- colMeans(
    survey_data[!in_cluster, , drop = FALSE]
  )

  differences <- group_means - other_means

  # Find the three largest differences in either direction
  strongest <- names(
    sort(abs(differences), decreasing = TRUE)
  )[1:min(3, length(differences))]

  cat("### Cluster ", cluster_number, "\n\n", sep = "")

  cat(
    "This group contains ",
    sum(in_cluster),
    " respondents, representing ",
    round(100 * mean(in_cluster), 1),
    "% of the analyzed sample. ",
    "Its largest differences from the remaining respondents are shown below.\n\n",
    sep = ""
  )

  for (variable in strongest) {

    difference <- differences[variable]

    direction <- if (difference > 0) {
      "higher than"
    } else if (difference < 0) {
      "lower than"
    } else {
      "equal to"
    }

    cat(
      "- **", variable, "**: The cluster average is ",
      sprintf("%.2f", group_means[variable]),
      ", compared with ",
      sprintf("%.2f", other_means[variable]),
      " for the remaining respondents. This is ",
      sprintf("%.2f", abs(difference)),
      " points ", direction,
      " the comparison average.\n",
      sep = ""
    )
  }

  cat(
    "\nThese variables provide a starting point for describing this segment. ",
    "Their meaning and rating direction should be confirmed using the questionnaire ",
    "before assigning a marketing label or recommending a specific message.\n\n"
  )
}

Cluster 1

This group contains 6 respondents, representing 2.7% of the analyzed sample. Its largest differences from the remaining respondents are shown below.

  • ShpOBody: The cluster average is 2.17, compared with 6.41 for the remaining respondents. This is 4.25 points lower than the comparison average.
  • Seam: The cluster average is 1.50, compared with 5.17 for the remaining respondents. This is 3.67 points lower than the comparison average.
  • Cut: The cluster average is 2.33, compared with 5.96 for the remaining respondents. This is 3.63 points lower than the comparison average.

These variables provide a starting point for describing this segment. Their meaning and rating direction should be confirmed using the questionnaire before assigning a marketing label or recommending a specific message.

Cluster 2

This group contains 90 respondents, representing 40.7% of the analyzed sample. Its largest differences from the remaining respondents are shown below.

  • KnowdgStaf: The cluster average is 3.66, compared with 5.36 for the remaining respondents. This is 1.70 points lower than the comparison average.
  • RetailEx: The cluster average is 4.66, compared with 5.69 for the remaining respondents. This is 1.04 points lower than the comparison average.
  • ChNoise: The cluster average is 3.77, compared with 4.44 for the remaining respondents. This is 0.68 points lower than the comparison average.

These variables provide a starting point for describing this segment. Their meaning and rating direction should be confirmed using the questionnaire before assigning a marketing label or recommending a specific message.

Cluster 3

This group contains 125 respondents, representing 56.6% of the analyzed sample. Its largest differences from the remaining respondents are shown below.

  • KnowdgStaf: The cluster average is 5.38, compared with 3.74 for the remaining respondents. This is 1.64 points higher than the comparison average.
  • RetailEx: The cluster average is 5.70, compared with 4.72 for the remaining respondents. This is 0.98 points higher than the comparison average.
  • SaleStaf: The cluster average is 4.79, compared with 3.92 for the remaining respondents. This is 0.88 points higher than the comparison average.

These variables provide a starting point for describing this segment. Their meaning and rating direction should be confirmed using the questionnaire before assigning a marketing label or recommending a specific message.

Why Are Cluster Means and Medians Important?

Cluster membership identifies which respondents have similar survey responses, but the group numbers do not explain their preferences. Means and medians help describe those preferences and show how the groups differ. This information can help businesses develop different messages or offers for different customer segments.

For example, differences in ratings for Price could suggest that the groups respond differently to that item. However, the survey wording is needed to determine whether higher scores indicate greater concern about price or something else.

Should Means or Medians Be Used?

I would examine both measures. Medians are useful for ordered survey ratings because they identify the middle response without assuming equal distances between rating categories. Means provide a more detailed comparison of average responses, although they treat the numerical steps as equally spaced.

Comparing both measures helps determine whether the interpretation changes depending on how the responses are summarized.

Appropriate Summary Measures

Useful summaries include each cluster’s customer count, percentage of the sample, mean ratings, and median ratings. These measures describe the size and response patterns of each segment.

The results do not show which group is most profitable. Additional information, such as purchase frequency, spending, and customer retention, would be needed to evaluate profitability.

Hierarchical Clustering Compared with K-Means

Hierarchical clustering creates a tree of nested groups that can be displayed in a dendrogram. The analyst can examine the tree and divide it into different numbers of clusters.

K-means requires the number of clusters to be selected before fitting the model. It repeatedly assigns observations to cluster centers and updates those centers. Its results can depend on the starting centers, so multiple starts are useful.

For this assignment, I prefer hierarchical clustering because the dendrogram helps me understand how the customer groups form. Comparing the results with K-means would be a useful next step.

Marketing Applications

A local clothing retailer could use these customer profiles to develop and test more relevant marketing messages. After confirming the questionnaire’s meaning, the retailer could examine whether particular groups respond more strongly to price, convenience, fashion, or product features.

The business could then test different messages with each group and compare actual campaign results. This would help determine whether the differences found in the survey translate into differences in customer behavior.

Limitations

This analysis uses three clusters because the lab asks for a three-group solution. It does not establish that three is the best number of clusters. Results may also change with different variables, distance measures, or linkage methods.

Because these are survey ratings, some differences may reflect how respondents use the rating scale rather than distinct shopping priorities. The findings should therefore be treated as exploratory and checked against the questionnaire and additional customer information.

Export the Results

cluster_membership <- data.frame(
  ID = analysis_data$ID,
  Cluster = groups.3
)

write.csv(
  cluster_membership,
  "clusterID.csv",
  row.names = FALSE
)

write.csv(
  cluster_sizes,
  "cluster_sizes.csv",
  row.names = FALSE
)

write.csv(
  cluster_means,
  "cluster_means.csv",
  row.names = FALSE
)

write.csv(
  cluster_medians,
  "cluster_medians.csv",
  row.names = FALSE
)

The exported files contain each customer’s cluster assignment, the cluster sizes, and the mean and median survey ratings for each group.