Introduction

(Giới thiệu)

Phần phân tích này là phần thứ hai trong phân tích phân khúc khách hàng. Bài phân tích đầu tiên tập trung vào việc phân cụm K-Means trong R để phân khúc khách hàng thành các nhóm riêng biệt dựa trên thói quen mua hàng. Bài phân tích này có một cách tiếp cận khác, sử dụng Phân tích thành phần chính (PCA) trong R làm công cụ để xem các nhóm khách hàng. Bởi vì PCA tấn công vấn đề từ một góc độ khác với K-Mean, nên chúng ta có thể có được những hiểu biết sâu sắc khác nhau. Chúng ta sẽ so sánh cả kết quả K-Mean với hình ảnh trực quan của PCA. Hãy xem điều gì sẽ xảy ra khi chúng ta áp dụng PCA.

Table of Contents

(Mục lục)
  • Tại sao sử dụng PCA? (Why PCA?)
  • Chúng ta đang dừng ở đâu? (Where We Left Off)
  • Chuẩn bị sẵn sàng cho PCA (Getting Ready for PCA)
    • Lấy dữ liệu (Getting the Data)
    • Đọc dữ liệu (Reading the Data)
    • Khai phá dữ liệu (Manipulating the Data)
    • Phân cụm K-Means (K-Means Clustering)
  • Ứng dụng PCA (Applying PCA)
  • Trực quan hóa kết quả (Visualizing the Results)
    • Vẽ đồ thị của PC’s (Plotting the PC’s)
    • PCA Viz: K=4 Nhóm K-Means (PCA Viz: K=4 K-Means Grouping)
    • PCA Viz: K=5 Nhóm K-Means (PCA Viz: K=5 K-Means Grouping)
    • PCA Viz: Nhóm lại các kiểm tra trực quan (PCA Viz: Visual Inspection Grouping)
    • Kiểm tra nhóm 2 (Group 2 Inspection)
    • Kiểm tra nhóm 4 (Group 4 Inspection)
  • Kết luận (Conclusions)
  • Tổng kết lại (Recap)
  • Tham khảo (Further Reading)

Why PCA?

(Tại sao sử dụng PCA)

PCA là thuật toán giảm kích thước: PCA lấy dữ liệu của bạn và phân tách dữ liệu đó bằng cách sử dụng các phép biến đổi thành các thành phần chính (PC). Mỗi PC được chọn theo hướng trực giao để tối đa hóa phương sai tuyến tính của dữ liệu. Về bản chất, PCA không gì khác hơn là một thuật toán lấy dữ liệu số theo tọa độ x, y, z và thay đổi tọa độ thành x’, y’ và z’ để tối đa hóa phương sai tuyến tính. Một bài viết hay về nền tảng của PCA là Phân tích thành phần giá trị: Giải thích bằng trực quan. Như tiêu đề gợi ý, bài viết có hình ảnh tuyệt vời để giải thích thuật toán.

Điều này giúp ích như thế nào trong việc phân khúc khách hàng/phát hiện cụm tương đồng? Không giống như K-Mean, PCA không phải là giải pháp trực tiếp. Những gì PCA giúp là trực quan hóa bản chất của một tập dữ liệu. Vì PCA chọn PC dựa trên phương sai tuyến tính tối đa nên chúng ta có thể sử dụng một số PC đầu tiên để mô tả phần lớn tập dữ liệu mà không cần so sánh và đối chiếu từng tính năng. Bằng cách sử dụng PC1 và PC2, chúng ta có thể hiển thị dưới dạng 2D và kiểm tra các cụm. Chúng ta cũng có thể kết hợp các kết quả với các nhóm K-means để xem K-means được phát hiện so với các cụm trong hình ảnh trực quan PCA.

Trước khi chuyển sang PCA, bạn nên xem lại phần chúng ta đã dừng lại trong bài viết phân khúc khách hàng trước đó.

Where We Left Off

(Chúng ta đang dừng ở đâu?)

Trong bài phân tích đầu tiên, chúng ta đã sử dụng phân cụm K-means để phân tích tập dữ liệu bikes data set, một tập hợp các tệp excel chứa dữ liệu cho các cửa hàng xe đạp (khách hàng), xe đạp (sản phẩm) và đơn đặt hàng cho nhà sản xuất xe đạp Cannondale. Các cửa hàng xe đạp và đơn đặt hàng là hư cấu/mô phỏng (xem bài viết orderSimulatoR để tham khảo), nhưng xe đạp (sản phẩm) là mô hình thực tế từ trang web của Cannondale.

Một giả thuyết được hình thành rằng các cửa hàng xe đạp mua xe đạp dựa trên các đặc điểm của xe đạp như đơn giá (cao cấp so với giá cả phải chăng), loại chính (Núi so với Đường bộ), khung (nhôm so với carbon), v.v. Các đơn đặt hàng được kết hợp với khách hàng và thông tin sản phẩm và được nhóm lại để tạo thành ma trận bán hàng theo mẫu mã và khách hàng. Hàm kmeans() được chạy trên một loạt các cụm tiềm năng, k và hàm Silhouette() từ gói cluster được sử dụng để xác định số lượng cụm tối ưu.

Getting Ready for PCA

(Chuẩn bị sẵn sàng cho PCA)

Thay vì xem lại bài phân tích trước, bạn có thể sử dụng các phần bên dưới để chuẩn bị sẵn sàng mọi thứ cho PCA.

Getting the Data

(Chuẩn bị dữ liệu)

Chúng ta có thể truy cập dữ liệu ở đây nếu bạn muốn theo dõi. Chúng ta sẽ cần tải xuống các tệp sau:

  • order.xlsx: Chứa các đơn đặt hàng hư cấu cho Cannondale. customer.id trong tệp order.xlsx liên quan đến bike shop.id trong tệp bikeshops.xlsxproduct.id trong tệp order.xlsx liên quan đến bike.id trong tệp bikeshops.xlsx.

  • xe đạp.xlsx: Chứa thông tin về sản phẩm (ví dụ: mẫu xe đạp, danh mục chính, danh mục phụ, đơn giá, v.v.). bike.id là khóa chính.

  • bikeshops.xlsx: Chứa thông tin về khách hàng (ví dụ: tên và địa điểm của khách hàng). bikeshop.id là khóa chính.

Tập lệnh tải và định cấu hình dữ liệu vào ma trận xu hướng khách hàng được hiển thị bên dưới.

Reading the Data

(Đọc dữ liệu)

Kịch bản này sẽ đọc dữ liệu. Đảm bảo bạn có các tệp excel trong thư mục có tên “data” trong thư mục làm việc hiện tại của bạn trước khi chạy tập lệnh bên dưới.

# Read Cannondale orders data --------------------------------------------------
library(xlsx)   # Used to read bikes data set
customers <- read.xlsx2("./data/bikeshops.xlsx", sheetIndex = 1,
                       colClasses = c("numeric", "character", "character",
                                      "character", "character", "numeric"))
products <- read.xlsx2("./data/bikes.xlsx", sheetIndex = 1,
                        colClasses = c("numeric", "character", "character",
                                       "character", "character", "numeric"))
orders <- read.xlsx2("./data/orders.xlsx", sheetIndex = 1, colIndex = 2:7,
                     colClasses = c("numeric", "numeric", "Date", "numeric",
                                    "numeric", "numeric"))

Manipulating the Data

(Khai phá dữ liệu)

Tập lệnh bên dưới kết hợp các khung dữ liệu orders, customersproducts vào order.extends, đây là khung dữ liệu mô phỏng đầu ra mà chúng ta sẽ nhận được từ truy vấn SQL của cơ sở dữ liệu đơn đặt hàng/hệ thống ERP. Sau đó, dữ liệu được xử lý để tạo thành customerTrends, có cấu trúc dữ liệu sao cho các hàng chứa sản phẩm và các cột chứa số lượng mua hàng (dưới dạng phần trăm trên tổng số) theo khách hàng. Đầu ra, customerTrends, được sử dụng để phân cụm k-means.

# Combine orders, customers, and products data frames --------------------------
library(dplyr)
orders.extended <- merge(orders, customers, by.x = "customer.id", by.y="bikeshop.id")
orders.extended <- merge(orders.extended, products, by.x = "product.id", by.y = "bike.id")
orders.extended <- orders.extended %>%
  mutate(price.extended = price * quantity) %>%
  select(order.date, order.id, order.line, bikeshop.name, model,
         quantity, price, price.extended, category1, category2, frame) %>%
  arrange(order.id, order.line)

# Group by model & model features, summarize by quantity purchased -------------
library(tidyr)  # For spread function
customerTrends <- orders.extended %>%
  group_by(bikeshop.name, model, category1, category2, frame, price) %>%
  summarise(total.qty = sum(quantity)) %>%
  spread(bikeshop.name, total.qty)
customerTrends[is.na(customerTrends)] <- 0  # Remove NA's

# Convert price to binary high/low category ------------------------------------
library(Hmisc)  # Needed for cut2 function
customerTrends$price <- cut2(customerTrends$price, g=2)

# Convert customer purchase quantity to percentage of total quantity -----------
customerTrends.mat <- as.matrix(customerTrends[,-(1:5)])  # Drop first five columns
customerTrends.mat <- prop.table(customerTrends.mat, margin = 2)  # column-wise pct
customerTrends <- bind_cols(customerTrends[,1:5], as.data.frame(customerTrends.mat))

Dữ liệu customerTrends có mẫu như sau:

# Preview the data
orders.extended %>%
  DT::datatable(
    extensions = 'FixedColumns',
    options = list(
      dom = 't',
      scrollX = TRUE,
      fixedColumns = TRUE,
      scrollY = 420,
      scroller = TRUE
    )
  )

K-Means Clustering

(Phân cụm K-Means)

Chúng ta đã sử dụng hàm kmeans() để thực hiện phân cụm k-means. Bài phân tích K-Means đi sâu vào chi tiết về cách chọn đúng số cụm mà ta đã bỏ qua để cho ngắn gọn. Chúng ta sẽ sử dụng các nhóm cho cụm k=4k=5 với phân tích PCA. Năm cụm là giải pháp tốt nhất về mặt lý thuyết và bốn cụm là giải pháp tốt nhất khi kiểm tra dữ liệu.

# K-Means Clustering (used later) ----------------------------------------------
set.seed(11) # For reproducibility
km4.out <- kmeans(t(customerTrends[,-(1:5)]), centers = 4, nstart = 50)

set.seed(11) # For reproducibility
km5.out <- kmeans(t(customerTrends[,-(1:5)]), centers = 5, nstart = 50)

Applying PCA

(Ứng dụng PCA)

Bây giờ, quay lại trọng tâm chính của chúng ta: PCA. Việc áp dụng PCA rất đơn giản sau khi dữ liệu được định dạng. Chúng ta sử dụng hàm prcomp() có sẵn trong cơ sở R. Chúng ta thực sự khuyên bạn nên chia tỷ lệ và căn giữa dữ liệu (vì một số lý do, đây không phải là mặc định). Đọc thêm về PCA trong R có thể được tìm thấy ở đây.

# PCA using prcomp() -----------------------------------------------------------
pca <- prcomp(t(customerTrends[,-(1:5)]), scale. = T, center = T) # Perform PCA

Sau khi PCA được thực hiện, chúng ta nên xem xét phương sai được giải thích. Như đã nêu trước đây, mục tiêu của PCA là giảm kích thước của dữ liệu. PCA thực hiện điều này bằng cách chuyển đổi dữ liệu thành các kích thước trực giao với biến thể. Sự khác biệt được giải thích càng lớn thì PC càng tóm tắt được nhiều thông tin hơn. Hàm prcomp() trả về phương sai này bằng PC. Chúng ta có thể sử dụng summary(pca) để giải thích phương sai. Chúng ta hãy nhìn trực quan chín PC đầu tiên.

summary(pca)
## Importance of components:
##                           PC1    PC2    PC3     PC4    PC5     PC6     PC7
## Standard deviation     4.9852 4.2206 2.2308 2.18304 2.1169 2.03586 1.86265
## Proportion of Variance 0.2562 0.1836 0.0513 0.04913 0.0462 0.04273 0.03577
## Cumulative Proportion  0.2562 0.4399 0.4911 0.54028 0.5865 0.62921 0.66498
##                            PC8     PC9    PC10    PC11    PC12    PC13    PC14
## Standard deviation     1.80739 1.76329 1.70165 1.61928 1.55759 1.50889 1.46505
## Proportion of Variance 0.03368 0.03205 0.02985 0.02703 0.02501 0.02347 0.02213
## Cumulative Proportion  0.69865 0.73071 0.76056 0.78759 0.81260 0.83607 0.85820
##                           PC15    PC16    PC17    PC18    PC19    PC20    PC21
## Standard deviation     1.30348 1.26612 1.22705 1.18511 1.13653 1.04480 0.98599
## Proportion of Variance 0.01752 0.01653 0.01552 0.01448 0.01332 0.01125 0.01002
## Cumulative Proportion  0.87572 0.89224 0.90776 0.92224 0.93556 0.94681 0.95684
##                           PC22    PC23    PC24    PC25    PC26   PC27   PC28
## Standard deviation     0.87912 0.84801 0.80364 0.74209 0.69318 0.6607 0.5990
## Proportion of Variance 0.00797 0.00741 0.00666 0.00568 0.00495 0.0045 0.0037
## Cumulative Proportion  0.96480 0.97222 0.97888 0.98455 0.98951 0.9940 0.9977
##                           PC29      PC30
## Standard deviation     0.47169 2.214e-15
## Proportion of Variance 0.00229 0.000e+00
## Cumulative Proportion  1.00000 1.000e+00

PC1 và PC2 kết hợp giải thích 44% phương sai của dữ liệu và có sự chênh lệch lớn giữa PC2 và PC3. Điều này có nghĩa là việc vẽ đồ thị của PC 1 và 2 sẽ giúp chúng ta hiểu khá rõ về dữ liệu và việc thêm nhiều PC hơn PC2 sẽ mang lại sự cải thiện tối thiểu. Lưu ý rằng không phải lúc nào cũng có sự sụt giảm đáng kể sau PC2 và nếu có nhiều sự khác biệt về PC được giải thích, chúng ta cũng cần phải đánh giá chúng.

Visualizing the Results

(Trực quan hóa kết quả)

Chúng ta sẽ vẽ đồ thị PC1 và PC2, đồng thời ban đầu chúng ta sẽ tô màu các cụm bằng cách sử dụng các nhóm k-mean đã tạo trước đó. Để làm điều này, trước tiên chúng ta cần đưa dữ liệu pca vào khung dữ liệu kết hợp khách hàng với PC. May mắn thay, gói ggfortify có chức năng gọi là fortify() để thực hiện việc đó. Sau khi dữ liệu của chúng ta được củng cố, chúng ta sẽ tạo hai khung dữ liệu với các nhóm k-mean được thêm vào cuối để chúng ta có thể nhóm chúng. Chúng ta sẽ sử dụng nhóm k-means để tô màu kết quả PCA để có thể so sánh với k-means và hiểu rõ hơn.

# Manipulate data for PCA Analyis ----------------------------------------------
library(ggfortify) # For fortify()
pca.fortify <- fortify(pca) # fortify() gets pca into usable format

# Add group (short for color) column using k=4 and k=5 groups
pca4.dat <- cbind(pca.fortify, group=km4.out$cluster)
pca5.dat <- cbind(pca.fortify, group=km5.out$cluster)

Plotting the PC’s

(Vẽ đồ thị của PC’s)

Tập lệnh bên dưới cung cấp các tập lệnh ggplot mẫu để tạo sơ đồ chung cho PC1 và PC2. Tập lệnh sử dụng thư viện vẽ đồ thị tương tác yêu thích của chúng ta, plotly, để biến kết quả ggplot thành các đồ thị tương tác. Các tập lệnh được sử dụng cho phần tiếp theo cũng tuân theo quy trình chung tương tự, vì vậy chúng ta sẽ không hiển thị mã cho ngắn gọn.

# Plotting PC1 and PC2 using ggplot and plotly ---------------------------------
library(ggplot2)
library(plotly)
# Script for plotting k=4
gg2 <- ggplot(pca4.dat) +
  geom_point(aes(x=PC1, y=PC2, col=factor(group), text=rownames(pca4.dat)), size=2) +
  labs(title = "Visualizing K-Means Clusters Against First Two Principal Components") +
  scale_color_brewer(name="", palette = "Set1")
# Use plotly for inteactivity
plotly2 <- ggplotly(gg2, tooltip = c("text", "x", "y")) %>%
  layout(legend = list(x=.9, y=.99))

PCA Viz: K=4 K-Means Grouping

Phân đoạn PCA cho thấy một số kết quả thú vị. Có vẻ như có năm cụm thay vì bốn. Kết quả k=4 k-mean sẽ không xác định được điều này vì chúng tôi đã chọn bốn cụm. Có thể bạn nghĩ rằng phân tích hình bóng từ bài viết trước chỉ ra rằng chúng ta nên chọn k=5, vì vậy đây là chỗ chúng ta đã sai. Hãy xem điều gì xảy ra với năm cụm k-means.

PCA Viz: K=5 K-Means Grouping

Có vẻ như Nhóm 2 đã được phân loại sai. Từ bài phân tích của K-means, điều này thực sự có ý nghĩa: Chúng ta đã kiểm tra các cụm và Nhóm 2 và Nhóm 4 rất giống nhau về sở thích đối với xe đạp ở mức giá cấp thấp. Chúng ta quyết định kết hợp các cụm này bằng cách chuyển sang cụm k=4. Tiếp theo, chúng ta sẽ xem xét việc phân cụm dựa trên việc kiểm tra sơ đồ PCA.

PCA Viz: Visual Inspection Grouping

(PCA Viz: Nhóm lại các kiểm tra trực quan)

Từ hình ảnh PCA, chúng ta có thể thấy có hai cửa hàng xe đạp không thuộc Nhóm 5. Dựa trên kiểm tra trực quan, chúng ta có thể sửa đổi các nhóm (cột 128) bằng cách chuyển Nhóm 1 (San Antonio Bike Shop & Philadelphia Bike Shop) với các cửa hàng thuộc Nhóm 5 được phân loại không chính xác (Denver Bike Shop & Kansas City 29ers).

# Switch Group 2 Bike Shops with misclassified Bike Shops in Group 4 -----------
pca.final.dat <- pca5.dat
pca.final.dat[rownames(pca.final.dat) %in% 
                c("San Antonio Bike Shop", "Philadelphia Bike Shop"), 128] <- 5
pca.final.dat[rownames(pca.final.dat) %in% 
                c("Denver Bike Shop", "Kansas City 29ers"), 128] <- 1

Và kết qua được vẽ lại như sau:

Mọi thứ có vẻ ổn, nhưng chúng ta cần kiểm tra các tùy chọn của Nhóm 1 mới được tạo để đảm bảo rằng họ thực sự là một phân khúc khách hàng độc lập.

Group 1 Inspection

(Kiểm tra nhóm 1)

Tập lệnh bên dưới sửa đổi khung dữ liệu CustomerTrends để chỉ chọn các cột khách hàng nằm trong nhóm mà chúng ta muốn kiểm tra. Sau đó, tập lệnh lấy trung bình phần trăm số lượng đã mua của khách hàng so với tổng số lượng đã mua (các giá trị trong cột khách hàng). Khung dữ liệu được sắp xếp theo mức độ được mua thường xuyên nhất để chúng ta có thể thấy xu hướng trung tâm của nhóm. Đối với Nhóm 1, xu hướng trung tâm là ưa chuộng những chiếc xe đạp leo núi cấp thấp.

# Inspect Group 2 Preferences --------------------------------------------------
# Select only groups in group num and perform row-wise average of bike prefs
library(dplyr)
group.num <- 1 # Set group number
group.names <- rownames(pca.final.dat[pca.final.dat$group == group.num, ])
groupTrends <- customerTrends %>%
  select(model:price, match(group.names, names(.))) # Use match() to select column names
group.avg <- apply(groupTrends[6:ncol(groupTrends)], 1, mean) # Take average of values
groupTrends <- bind_cols(groupTrends, as_data_frame(group.avg)) %>%
  arrange(-group.avg) 

knitr::kable(head(groupTrends, 10)) # Top ten products by group avg. pct. purchased
model category1 category2 frame price Denver Bike Shop Kansas City 29ers value
Catalyst 2 Mountain Sport Aluminum [ 415, 3500) 0.0256410 0.0187266 0.0221838
Trail 5 Mountain Sport Aluminum [ 415, 3500) 0.0204259 0.0216076 0.0210168
F-Si Carbon 4 Mountain Cross Country Race Carbon [ 415, 3500) 0.0165146 0.0247767 0.0206456
Scalpel 29 4 Mountain Cross Country Race Aluminum [ 415, 3500) 0.0186875 0.0210314 0.0198595
Catalyst 4 Mountain Sport Aluminum [ 415, 3500) 0.0199913 0.0184385 0.0192149
F-Si 1 Mountain Cross Country Race Aluminum [ 415, 3500) 0.0204259 0.0175742 0.0190000
Trail 4 Mountain Sport Aluminum [ 415, 3500) 0.0152108 0.0218957 0.0185532
Trail 1 Mountain Sport Aluminum [ 415, 3500) 0.0204259 0.0164218 0.0184238
Trail 2 Mountain Sport Aluminum [ 415, 3500) 0.0208605 0.0158456 0.0183530
Beast of the East 1 Mountain Trail Aluminum [ 415, 3500) 0.0182529 0.0181504 0.0182017

Group 5 Inspection

(Kiểm tra nhóm 5)

Hãy so sánh với Nhóm 5. Chạy lại tập lệnh trước đó thay đổi group.num từ 1 thành 5. Chúng ta có thể thấy rằng sở thích của Nhóm 5 giống với Nhóm 1 ở chỗ cả hai nhóm đều thích xe đạp cấp thấp/giá cả phải chăng. Tuy nhiên, các giao dịch mua hàng đầu của Nhóm 5 bao gồm sự kết hợp giữa “Mountain and Road”, trong khi các giao dịch mua hàng đầu của Nhóm 1 chỉ có “Mountain”. Dường như có sự khác biệt!

library(dplyr)
group.num <- 5 # Set group number
group.names <- rownames(pca.final.dat[pca.final.dat$group == group.num, ])
groupTrends <- customerTrends %>%
  select(model:price, match(group.names, names(.))) # Use match() to select column names
group.avg <- apply(groupTrends[6:ncol(groupTrends)], 1, mean) # Take average of values
groupTrends <- bind_cols(groupTrends, as_data_frame(group.avg)) %>%
  arrange(-group.avg) 

#knitr::kable(head(groupTrends, 10)) # Top ten products by group avg. pct. purchased

groupTrends %>%
  DT::datatable(
    extensions = 'FixedColumns',
    options = list(
      dom = 't',
      scrollX = TRUE,
      fixedColumns = TRUE,
      scrollY = 420,
      scroller = TRUE
    )
  )

Conclusion

(Kết luận)

PCA có thể là một công cụ kiểm tra chéo có giá trị đối với K-means để phân khúc khách hàng. Mặc dù k-means giúp chúng ta tiếp cận các phân khúc khách hàng thực sự, nhưng việc đánh giá trực quan các nhóm bằng PCA đã giúp xác định một phân khúc khách hàng khác, một phân khúc mà giải pháp k=5 k-means không phát hiện được.

Recap

(Tổng kết lại)

Bài phân tích này mở rộng phương pháp phân khúc khách hàng của chúng ta bằng cách sử dụng PCA để kiểm tra trực quan các cụm. Chúng ta đã thao tác dữ liệu đơn đặt hàng của mình để có được định dạng liên quan đến sản phẩm với việc mua hàng của khách hàng. Chúng ta đã sử dụng hàm prcomp() để thực hiện PCA trên khung dữ liệu được định dạng của mình. Chúng ta đã củng cố đầu ra PCA bằng cách sử dụng hàm fortify() từ gói ggfortify, cho phép chúng ta vẽ biểu đồ PC theo khách hàng. Chúng ta đã thêm các nhóm cụm k-means vào khung dữ liệu được củng cố để kiểm tra trực quan các cụm k-mean. Chúng ta thấy rằng sự khác biệt có thể phát sinh do k-means xác định các cụm theo chương trình trong khi PCA cho phép chúng ta kiểm tra các cụm một cách trực quan. Kết quả cuối cùng là sự cải thiện về phân khúc khách hàng bằng cách giải quyết vấn đề phát hiện cộng đồng từ hai góc độ khác nhau và kết hợp các kết quả!

Further Reading

(Tham khảo)

1.Pricipal Component Analysis: Explained Visually: This article is an excellent place to start for those that are new to PCA or those that would like to understand the details.

2.Computing and Visualizing PCA in R: This is a great article that takes PCA to the next level in R with biplots, predictions, and the caret package.