This analysis groups customers with similar survey responses to support market segmentation and targeting. The supplied Segmentation.csv contains 221 respondents, one ID column, and 21 numeric rating variables. Values range from 1 to 7. There are no missing values, duplicated IDs, or constant rating variables. The column names suggest clothing and shopping preferences; the file does not contain a questionnaire or scale labels. Therefore, the segment names below are tentative and assume that higher ratings indicate greater importance or a more positive evaluation. These results describe this sample and should not automatically be generalized to all customers.
The data are included below in a hidden chunk, so this report can be knitted without selecting or locating another file.
ID is a respondent identifier, so it is excluded from clustering and from calculations of average ratings. Each rating variable is standardized using its sample standard deviation so the variables contribute on a comparable scale.
rating_data <- mydata[, names(mydata) != "ID", drop = FALSE]
stopifnot(nrow(mydata) == 221L,
!anyNA(mydata),
!anyDuplicated(mydata$ID),
all(vapply(rating_data, is.numeric, logical(1))),
all(vapply(rating_data, sd, numeric(1)) > 0))
scaled_data <- scale(rating_data, center = TRUE, scale = TRUE)
data.frame(Respondents = nrow(mydata),
Rating_variables = ncol(rating_data),
Missing_values = sum(is.na(mydata)))
## Respondents Rating_variables Missing_values
## 1 221 21 0
The main analysis follows the supplied tutorial: first calculate Euclidean distances between standardized respondents, then calculate Euclidean distances between the rows of that distance matrix. Apply hierarchical clustering with complete linkage, R’s default for hclust(), and cut the tree into three groups.
Method note: The tutorial’s second distance calculation clusters respondents by their distance profiles. Conventional hierarchical clustering would pass the first distance object directly to hclust(). These approaches produce different memberships. This report preserves the tutorial method for the main answers and shows the conventional alternative at the end. Three clusters are specified by the exercise; they have not been established as the optimal number of market segments.
initial_distances <- dist(scaled_data, method = "euclidean")
profile_distances <- dist(as.matrix(initial_distances), method = "euclidean")
seg.hclust <- hclust(profile_distances, method = "complete")
groups.3 <- cutree(seg.hclust, k = 3)
plot(seg.hclust, labels = FALSE, hang = -1,
main = "Hierarchical clustering of respondent distance profiles",
xlab = "221 respondents", sub = "Complete linkage; three groups")
rect.hclust(seg.hclust, k = 3, border = c("#16697a", "#d98c28", "#7851a9"))
cluster_sizes <- data.frame(Cluster = 1:3,
Respondents = as.integer(table(groups.3)))
cluster_sizes$Percent <- round(100 * cluster_sizes$Respondents / nrow(mydata), 2)
knitr::kable(cluster_sizes, caption = "Respondents in each cluster")
| Cluster | Respondents | Percent |
|---|---|---|
| 1 | 9 | 4.07 |
| 2 | 170 | 76.92 |
| 3 | 42 | 19.00 |
Cluster 1 contains 9 respondents (4.07%), Cluster 2 contains 170 respondents (76.92%), and Cluster 3 contains 42 respondents (19.00%). Percentages may not sum to exactly 100% because of rounding. The very unequal sizes suggest that the three groups should be examined carefully before being used for business decisions.
The following means and medians use the original 1–7 ratings, which are easier to interpret than standardized values.
cluster_means <- aggregate(rating_data, list(Cluster = groups.3), mean)
cluster_medians <- aggregate(rating_data, list(Cluster = groups.3), median)
knitr::kable(t(cluster_means[, -1]), digits = 2,
col.names = paste("Cluster", cluster_means$Cluster),
caption = "Mean rating by cluster")
| Cluster 1 | Cluster 2 | Cluster 3 | |
|---|---|---|---|
| Fashn | 2.11 | 3.35 | 2.83 |
| Price | 5.56 | 4.62 | 4.67 |
| Convnience | 5.00 | 3.58 | 4.24 |
| ShpTime | 5.00 | 3.91 | 4.60 |
| Fitness | 4.00 | 5.92 | 5.31 |
| Perceptn | 3.22 | 4.41 | 3.93 |
| ChNoise | 4.67 | 4.07 | 4.45 |
| RetailEx | 5.44 | 5.30 | 5.12 |
| KnowdgStaf | 5.11 | 4.68 | 4.52 |
| Brand4Slf | 1.67 | 4.11 | 2.76 |
| Brand4Els | 1.44 | 4.42 | 2.62 |
| Populr | 1.78 | 4.06 | 2.40 |
| StrDisp | 2.00 | 3.68 | 3.02 |
| SaleStaf | 3.44 | 4.54 | 4.12 |
| Fabric | 2.89 | 5.66 | 4.86 |
| Cut | 3.33 | 6.02 | 5.79 |
| Seam | 1.44 | 5.34 | 4.76 |
| ShpOHngr | 2.33 | 4.36 | 3.62 |
| ShpOBody | 3.78 | 6.46 | 6.19 |
| Colrs | 3.22 | 5.48 | 4.60 |
| Match | 1.33 | 4.32 | 3.19 |
knitr::kable(t(cluster_medians[, -1]), digits = 2,
col.names = paste("Cluster", cluster_medians$Cluster),
caption = "Median rating by cluster")
| Cluster 1 | Cluster 2 | Cluster 3 | |
|---|---|---|---|
| Fashn | 1 | 3.0 | 2.0 |
| Price | 5 | 5.0 | 5.0 |
| Convnience | 5 | 3.0 | 4.0 |
| ShpTime | 5 | 4.0 | 5.0 |
| Fitness | 3 | 6.0 | 6.0 |
| Perceptn | 3 | 5.0 | 4.0 |
| ChNoise | 6 | 4.0 | 5.0 |
| RetailEx | 6 | 6.0 | 5.5 |
| KnowdgStaf | 6 | 5.0 | 5.0 |
| Brand4Slf | 1 | 4.0 | 2.0 |
| Brand4Els | 1 | 4.0 | 2.0 |
| Populr | 1 | 4.0 | 1.5 |
| StrDisp | 1 | 4.0 | 2.0 |
| SaleStaf | 2 | 5.0 | 4.0 |
| Fabric | 1 | 6.0 | 5.5 |
| Cut | 4 | 6.0 | 6.0 |
| Seam | 1 | 5.5 | 5.0 |
| ShpOHngr | 1 | 4.0 | 4.0 |
| ShpOBody | 4 | 7.0 | 6.5 |
| Colrs | 4 | 6.0 | 5.0 |
| Match | 1 | 4.0 | 2.0 |
This small group gives relatively high average ratings to Price (5.56), RetailEx (5.44), KnowdgStaf (5.11), and Convnience (5.00). Brand4Slf (1.67), Brand4Els (1.44), and Match (1.33) receive much lower ratings. Assuming the scale measures importance, these customers appear more interested in value and shopping convenience than in branding. A clothing retailer could test clear price promotions, convenient pickup, and accessible product assistance. With only nine respondents, this segment’s profile is particularly uncertain and should be validated with more data.
The largest group gives high ratings to ShpOBody (6.46), Cut (6.02), Fitness (5.92), Fabric (5.66), and Colrs (5.48). Compared with the other groups, it also gives higher ratings to both brand variables and Populr, although these are not its highest-rated attributes. A retailer could test messages emphasizing how clothing looks on the body, its cut, fabric, and available colors. Product demonstrations and styling content may appeal to this group. Its size makes it a candidate for further research, but sample size alone does not establish profitability.
This group gives high average ratings to ShpOBody (6.19), Cut (5.79), and Fitness (5.31). Compared with Cluster 2, it rates Convnience (4.24 versus 3.58) and ShpTime (4.60 versus 3.91) higher, while Brand4Slf (2.76) and Brand4Els (2.62) are lower. A retailer could test messages combining product fit with an easy shopping experience. Clear sizing information, efficient service, and convenient exchanges are possible tactics to evaluate. Cluster 2 also values fit, so this label describes a relative pattern rather than an exclusive preference.
selected <- c("Price", "Convnience", "Brand4Slf", "Fabric", "Cut", "ShpOBody")
barplot(as.matrix(cluster_means[, selected]), beside = TRUE,
col = c("#16697a", "#d98c28", "#7851a9"), ylim = c(0, 8),
ylab = "Mean rating (original scale)",
main = "Selected attributes by cluster",
legend.text = paste("Cluster", 1:3),
args.legend = list(x = "topleft", bty = "n", horiz = TRUE))
1. How many observations are in each cluster?
The three clusters contain 9, 170, and 42 respondents, respectively, for a total of 221.
2. Why examine the means or medians of variables in each cluster?
Cluster membership alone does not explain what a group represents. Comparing typical ratings helps identify shared preferences and differences between groups. These profiles support meaningful segment names and targeted marketing recommendations.
3. Should means or medians be used?
Both are useful. Because the rating responses are ordinal, medians summarize the middle response without assuming that every step on the scale represents an equal difference. Means are useful for showing smaller differences between clusters under that equal-spacing assumption. I would report both and check whether they support the same general interpretation, especially for the small first cluster.
4. Which descriptive measures are appropriate for targeting?
Cluster counts and percentages describe each group’s representation in this sample. Means and medians describe typical preferences, while standard deviations or interquartile ranges show how much customers within a group differ. Businesses should also collect spending, purchase frequency, and response-to-offer data before deciding which group is most profitable to target. Those outcomes are not provided here.
5. How do K-means and hierarchical clustering differ, and which do I prefer?
K-means requires a chosen number of clusters and repeatedly assigns observations to centroids to reduce within-cluster squared distances. Its results can depend on starting values, so multiple starts are useful. Agglomerative hierarchical clustering begins with individual observations and progressively merges groups according to a distance measure and linkage rule. Its dendrogram can be cut at different levels. I prefer hierarchical clustering for this exploratory class exercise because the dendrogram helps explain how groups form. For a much larger customer dataset, K-means may be a more practical option.
Optional question: Should aggregate() include ID?
Use the rating variables only. An average or median respondent ID has no substantive meaning and should not be used to describe a customer segment.
The tutorial’s job-search question is a separate task. An exact count of relevant jobs requires a specified search date, location, filters, and a definition of relevance; this analysis does not supply such a count.
When knitted, the following code saves respondent memberships, cluster sizes, mean ratings, and median ratings in a segmentation_results folder beside this report.
dir.create("segmentation_results", showWarnings = FALSE)
memberships <- data.frame(ID = mydata$ID, Cluster = groups.3)
write.csv(memberships, "segmentation_results/clusterID.csv", row.names = FALSE)
write.csv(cluster_sizes, "segmentation_results/cluster_sizes.csv", row.names = FALSE)
write.csv(cluster_means, "segmentation_results/cluster_means.csv", row.names = FALSE)
write.csv(cluster_medians, "segmentation_results/cluster_medians.csv", row.names = FALSE)
Removing the second distance calculation produces cluster sizes of 6, 90, and 125. These belong to a different analysis and should not be combined with the main profiles above. Cluster numbers are arbitrary labels, so the same number across the two methods does not imply the same customers.
standard_tree <- hclust(initial_distances, method = "complete")
standard_groups <- cutree(standard_tree, k = 3)
table(standard_groups)
## standard_groups
## 1 2 3
## 6 90 125
These are exploratory groups, not predictions of future purchases. The questionnaire and rating anchors should be checked before interpreting a higher score as greater importance. No optimal-cluster-number analysis or stability assessment was supplied in the tutorial. The distance-profile transformation and the small first cluster warrant further investigation before a business invests in a targeting strategy. Although the tutorial mentions PCA in its title, its supplied code does not perform PCA, and PCA is not used in this analysis.