In this assignment, I used cluster analysis to examine customer survey data and identify groups of customers with similar responses. Market segmentation helps businesses understand their customers and develop better marketing strategies.
First, I imported the customer segmentation dataset into RStudio.
mydata <- read.csv("customer_segmentation.csv")
names(mydata) <- trimws(names(mydata))
head(mydata)
## ID CS_helpful Recommend Come_again All_Products Profesionalism Limitation
## 1 1 2 2 2 2 2 2
## 2 2 1 2 1 1 1 1
## 3 3 2 1 1 1 1 2
## 4 4 3 3 2 4 1 2
## 5 5 2 1 3 5 2 1
## 6 6 1 1 3 2 1 1
## Online_grocery delivery Pick_up Find_items other_shops Gender Age Education
## 1 2 3 4 1 2 1 2 2
## 2 2 3 3 1 2 1 2 2
## 3 3 3 2 1 3 1 2 2
## 4 3 3 2 2 2 1 3 5
## 5 2 3 1 2 3 2 4 2
## 6 1 2 1 1 4 1 2 5
dim(mydata)
## [1] 22 15
str(mydata)
## 'data.frame': 22 obs. of 15 variables:
## $ ID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ CS_helpful : int 2 1 2 3 2 1 2 1 1 1 ...
## $ Recommend : int 2 2 1 3 1 1 1 1 1 1 ...
## $ Come_again : int 2 1 1 2 3 3 1 1 1 1 ...
## $ All_Products : int 2 1 1 4 5 2 2 2 2 1 ...
## $ Profesionalism: int 2 1 1 1 2 1 2 1 2 1 ...
## $ Limitation : int 2 1 2 2 1 1 1 2 1 1 ...
## $ Online_grocery: int 2 2 3 3 2 1 2 1 2 3 ...
## $ delivery : int 3 3 3 3 3 2 2 1 1 2 ...
## $ Pick_up : int 4 3 2 2 1 1 2 2 3 2 ...
## $ Find_items : int 1 1 1 2 2 1 1 2 1 1 ...
## $ other_shops : int 2 2 3 2 3 4 1 4 1 1 ...
## $ Gender : int 1 1 1 1 2 1 1 1 2 2 ...
## $ Age : int 2 2 2 3 4 2 2 2 2 2 ...
## $ Education : int 2 2 2 5 2 5 3 2 1 2 ...
summary(mydata)
## ID CS_helpful Recommend Come_again
## Min. : 1.00 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.: 6.25 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:1.000
## Median :11.50 Median :1.000 Median :1.000 Median :1.000
## Mean :11.50 Mean :1.591 Mean :1.318 Mean :1.455
## 3rd Qu.:16.75 3rd Qu.:2.000 3rd Qu.:1.000 3rd Qu.:2.000
## Max. :22.00 Max. :3.000 Max. :3.000 Max. :3.000
## All_Products Profesionalism Limitation Online_grocery delivery
## Min. :1.000 Min. :1.000 Min. :1.0 Min. :1.000 Min. :1.000
## 1st Qu.:1.250 1st Qu.:1.000 1st Qu.:1.0 1st Qu.:2.000 1st Qu.:2.000
## Median :2.000 Median :1.000 Median :1.0 Median :2.000 Median :3.000
## Mean :2.091 Mean :1.409 Mean :1.5 Mean :2.273 Mean :2.409
## 3rd Qu.:2.000 3rd Qu.:2.000 3rd Qu.:2.0 3rd Qu.:3.000 3rd Qu.:3.000
## Max. :5.000 Max. :3.000 Max. :4.0 Max. :3.000 Max. :3.000
## Pick_up Find_items other_shops Gender
## Min. :1.000 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.:2.000 1st Qu.:1.000 1st Qu.:1.250 1st Qu.:1.000
## Median :2.000 Median :1.000 Median :2.000 Median :1.000
## Mean :2.455 Mean :1.455 Mean :2.591 Mean :1.273
## 3rd Qu.:3.000 3rd Qu.:2.000 3rd Qu.:3.750 3rd Qu.:1.750
## Max. :5.000 Max. :3.000 Max. :5.000 Max. :2.000
## Age Education
## Min. :2.000 Min. :1.000
## 1st Qu.:2.000 1st Qu.:2.000
## Median :2.000 Median :2.500
## Mean :2.455 Mean :3.182
## 3rd Qu.:3.000 3rd Qu.:5.000
## Max. :4.000 Max. :5.000
The dataset contains 22 customer responses and 15 columns. These columns include customer service, product availability, shopping behavior, and demographic information.
Before performing cluster analysis, I standardized the data so that variables with different numerical ranges would not dominate the analysis.
# Remove customer ID
mydata2 <- mydata[, -1]
# Standardize the variables
mydata.scaled <- scale(mydata2)
# View standardized data
head(mydata.scaled)
## CS_helpful Recommend Come_again All_Products Profesionalism Limitation
## [1,] 0.5572385 1.0548991 0.7385489 -0.08536162 1.0009877 0.6236096
## [2,] -0.8049001 1.0548991 -0.6154575 -1.02433946 -0.6929915 -0.6236096
## [3,] 0.5572385 -0.4922862 -0.6154575 -1.02433946 -0.6929915 0.6236096
## [4,] 1.9193772 2.6020844 0.7385489 1.79259406 -0.6929915 0.6236096
## [5,] 0.5572385 -0.4922862 2.0925553 2.73157191 1.0009877 -0.6236096
## [6,] -0.8049001 -0.4922862 2.0925553 -0.08536162 -0.6929915 -0.6236096
## Online_grocery delivery Pick_up Find_items other_shops Gender
## [1,] -0.3554390 0.8049001 1.4623535 -0.6774335 -0.4212692 -0.598293
## [2,] -0.3554390 0.8049001 0.5161248 -0.6774335 -0.4212692 -0.598293
## [3,] 0.9478374 0.8049001 -0.4301040 -0.6774335 0.2916479 -0.598293
## [4,] 0.9478374 0.8049001 -0.4301040 0.8129201 -0.4212692 -0.598293
## [5,] -0.3554390 0.8049001 -1.3763327 0.8129201 0.2916479 1.595448
## [6,] -1.6587154 -0.5572385 -1.3763327 -0.6774335 1.0045650 -0.598293
## Age Education
## [1,] -0.6154575 -0.7284586
## [2,] -0.6154575 -0.7284586
## [3,] -0.6154575 -0.7284586
## [4,] 0.7385489 1.1207055
## [5,] 2.0925553 -0.7284586
## [6,] -0.6154575 1.1207055
Standardization makes it easier to compare the survey variables during clustering.
Hierarchical clustering helps identify groups of customers with similar characteristics.
For this analysis, I used complete linkage and Euclidean distances. I followed the classroom examples’ distance calculation.
# Calculate distances
d <- dist(mydata.scaled)
# Match the classroom example procedure
d2 <- dist(as.matrix(d))
# Perform hierarchical clustering
seg.hclust <- hclust(
d2,
method = "complete"
)
# Display dendrogram
plot(
seg.hclust,
labels = mydata$ID,
main = "Hierarchical Cluster Analysis",
xlab = "Customer ID",
ylab = "Distance",
sub = "",
hang = -1
)
# Highlight three clusters
rect.hclust(
seg.hclust,
k = 3,
border = c("red", "blue", "green")
)
The dendrogram shows how customers are grouped based on similarities in their survey responses.
Note: The additional distance calculation matches the classroom
examples. A more conventional approach would be
hclust(dist(mydata.scaled), method = "complete"), which can
produce different clusters.
I divided the respondents into three clusters to understand the differences between customer groups.
# Create three clusters
groups.3 <- cutree(
seg.hclust,
k = 3
)
# Display cluster membership
groups.3
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
## 1 1 1 2 3 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 2
# Count respondents in each cluster
table(groups.3)
## groups.3
## 1 2 3
## 17 2 3
# Cluster percentages
round(prop.table(table(groups.3)) * 100, 2)
## groups.3
## 1 2 3
## 77.27 9.09 13.64
The analysis identified three customer groups:
Cluster 1 contains most of the respondents, while Clusters 2 and 3 represent smaller groups with different response patterns.
The next step is to compare the average responses of each cluster.
# Calculate mean values
cluster.mean <- aggregate(
mydata2,
by = list(Cluster = groups.3),
FUN = mean
)
# Display cluster means
cluster.mean
## Cluster CS_helpful Recommend Come_again All_Products Profesionalism
## 1 1 1.294118 1.117647 1.235294 1.823529 1.235294
## 2 2 3.000000 2.500000 1.500000 3.000000 1.500000
## 3 3 2.333333 1.666667 2.666667 3.000000 2.333333
## Limitation Online_grocery delivery Pick_up Find_items other_shops Gender
## 1 1.352941 2.235294 2.235294 2.705882 1.294118 2.647059 1.176471
## 2 2.000000 3.000000 3.000000 2.500000 2.000000 1.500000 1.000000
## 3 2.000000 2.000000 3.000000 1.000000 2.000000 3.000000 2.000000
## Age Education
## 1 2.411765 3.117647
## 2 2.500000 5.000000
## 3 2.666667 2.333333
# Display formatted table
knitr::kable(
round(cluster.mean, 2),
caption = "Mean Values for Each Cluster"
)
| Cluster | CS_helpful | Recommend | Come_again | All_Products | Profesionalism | Limitation | Online_grocery | delivery | Pick_up | Find_items | other_shops | Gender | Age | Education |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1.29 | 1.12 | 1.24 | 1.82 | 1.24 | 1.35 | 2.24 | 2.24 | 2.71 | 1.29 | 2.65 | 1.18 | 2.41 | 3.12 |
| 2 | 3.00 | 2.50 | 1.50 | 3.00 | 1.50 | 2.00 | 3.00 | 3.00 | 2.50 | 2.00 | 1.50 | 1.00 | 2.50 | 5.00 |
| 3 | 2.33 | 1.67 | 2.67 | 3.00 | 2.33 | 2.00 | 2.00 | 3.00 | 1.00 | 2.00 | 3.00 | 2.00 | 2.67 | 2.33 |
The mean values help explain how each customer group responded to the survey.
Cluster 1 generally has lower numerical responses for customer service helpfulness, recommendations, and returning to the store.
Cluster 2 has higher numerical responses for customer service helpfulness and recommendation questions.
Cluster 3 shows a different response pattern for returning to the store and pickup services.
These differences can help businesses identify potential customer segments and evaluate areas for improvement.
I also calculated the median responses to identify typical values within each cluster.
# Calculate median values
cluster.median <- aggregate(
mydata2,
by = list(Cluster = groups.3),
FUN = median
)
# Display median values
cluster.median
## Cluster CS_helpful Recommend Come_again All_Products Profesionalism
## 1 1 1 1.0 1.0 2 1.0
## 2 2 3 2.5 1.5 3 1.5
## 3 3 2 1.0 3.0 3 2.0
## Limitation Online_grocery delivery Pick_up Find_items other_shops Gender Age
## 1 1 2 2 3.0 1 2.0 1 2.0
## 2 2 3 3 2.5 2 1.5 1 2.5
## 3 1 2 3 1.0 2 3.0 2 2.0
## Education
## 1 2
## 2 5
## 3 2
# Format median table
knitr::kable(
cluster.median,
caption = "Median Values for Each Cluster"
)
| Cluster | CS_helpful | Recommend | Come_again | All_Products | Profesionalism | Limitation | Online_grocery | delivery | Pick_up | Find_items | other_shops | Gender | Age | Education |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 1.0 | 1.0 | 2 | 1.0 | 1 | 2 | 2 | 3.0 | 1 | 2.0 | 1 | 2.0 | 2 |
| 2 | 3 | 2.5 | 1.5 | 3 | 1.5 | 2 | 3 | 3 | 2.5 | 2 | 1.5 | 1 | 2.5 | 5 |
| 3 | 2 | 1.0 | 3.0 | 3 | 2.0 | 1 | 2 | 3 | 1.0 | 2 | 3.0 | 2 | 2.0 | 2 |
Median values are useful because they represent the middle response in each cluster.
Since the survey uses coded responses, the median can help identify common response patterns without relying entirely on averages.
Comparing both the mean and median gives businesses a better understanding of differences among customer groups.
I also used Principal Component Analysis (PCA) to visualize the customer segments.
# Perform PCA
pca <- prcomp(
mydata.scaled,
center = FALSE,
scale. = FALSE
)
# PCA summary
summary(pca)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 1.7763 1.5393 1.3418 1.2575 1.12172 1.00800 0.78243
## Proportion of Variance 0.2254 0.1692 0.1286 0.1129 0.08988 0.07258 0.04373
## Cumulative Proportion 0.2254 0.3946 0.5232 0.6361 0.72602 0.79860 0.84233
## PC8 PC9 PC10 PC11 PC12 PC13 PC14
## Standard deviation 0.76198 0.67310 0.62577 0.5999 0.45093 0.43527 0.17180
## Proportion of Variance 0.04147 0.03236 0.02797 0.0257 0.01452 0.01353 0.00211
## Cumulative Proportion 0.88380 0.91616 0.94413 0.9698 0.98436 0.99789 1.00000
# Create colors for clusters
colors <- c("red", "blue", "green")
# Plot PCA
plot(
pca$x[,1],
pca$x[,2],
col = colors[groups.3],
pch = 19,
xlab = "Principal Component 1",
ylab = "Principal Component 2",
main = "PCA Customer Segmentation"
)
# Add customer ID labels
text(
pca$x[,1],
pca$x[,2],
labels = mydata$ID,
pos = 3,
cex = 0.7
)
# Add legend
legend(
"topright",
legend = c("Cluster 1", "Cluster 2", "Cluster 3"),
col = colors,
pch = 19
)
The PCA visualization helps show differences between customer responses in two dimensions.
The cluster analysis identified three different customer groups.
Cluster 1: This is the largest group, containing 17 customers. The group generally has lower numerical response codes for several customer service and shopping-related questions.
Cluster 2: This group contains two customers. Their responses differ from the majority, particularly regarding customer service helpfulness and recommendations.
Cluster 3: This group contains three customers. Their responses show noticeable differences in return visits and pickup-related questions.
These findings demonstrate how cluster analysis can help businesses identify customer groups and develop more focused marketing strategies.
However, the original survey does not provide the meaning of each numerical response code. Therefore, I cannot determine which cluster is more satisfied without reviewing the original survey questions and response options.
Cluster 1 contains 17 customers, Cluster 2 contains 2 customers, and Cluster 3 contains 3 customers. There are 22 respondents in total. Cluster 1 represents the largest segment, with approximately 77.3% of all respondents.
Comparing the mean and median helps identify differences between customer groups. These values show how respondents answered questions about customer service, product availability, and shopping preferences. Businesses can use these differences to better understand customer behavior.
I would focus more on the median because the survey uses numerical codes to represent customer responses. The median provides a useful way to identify typical responses. However, I would still use the mean to compare general numerical patterns between clusters.
Cluster analysis can help businesses understand different groups of customers. Instead of treating every customer the same, companies can identify differences in preferences and behaviors. This information can help improve customer service, develop targeted marketing strategies, and improve customer experiences.
K-means clustering divides observations into a selected number of groups based on similarities. Hierarchical clustering creates a tree diagram called a dendrogram that shows how observations are grouped. Both methods can help businesses identify customer segments and understand consumer behavior.
This analysis helped me understand how marketing analysts use data to make business decisions. Through my Marketing Analytics coursework at CSUB, I have gained experience using RStudio, Excel, and data visualization. These skills can help prepare me for positions such as Marketing Analyst, Market Research Analyst, or Data Analyst.
I still want to improve my skills in SQL, Power BI, and advanced statistical analysis to become more competitive in the marketing analytics field.
Overall, this assignment helped me understand how hierarchical clustering can be used for market segmentation. By analyzing 22 customer responses, I identified three clusters with different response patterns. I also learned how to use RStudio to create a dendrogram, calculate mean and median values, and visualize customer groups using PCA.
This experience showed me how businesses can use data analytics to better understand customers and improve marketing decisions.
# Save cluster assignments
cluster.results <- data.frame(
ID = mydata$ID,
Cluster = groups.3
)
write.csv(
cluster.results,
"customer_cluster_results.csv",
row.names = FALSE
)
# Save means
write.csv(
cluster.mean,
"customer_cluster_means.csv",
row.names = FALSE
)
# Save medians
write.csv(
cluster.median,
"customer_cluster_medians.csv",
row.names = FALSE
)