Bối cảnh: Trong lĩnh vực E-commerce và FMCG, dữ liệu khách hàng thường bị phân mảnh ở nhiều nguồn (CRM, Website, Mạng xã hội) và chứa nhiều lỗi định dạng do thao tác nhập liệu thủ công.
Mục tiêu: Ứng dụng tư duy thuật toán bằng R và Kỹ thuật Prompt Engineering để tự động hóa luồng công việc: Thu thập \(\rightarrow\) Làm sạch \(\rightarrow\) Hợp nhất \(\rightarrow\) Làm giàu bằng AI \(\rightarrow\) Trực quan hóa.
Để minh họa, dự án khởi tạo hai tập dữ liệu mô phỏng tình trạng “rác” thường gặp thực tế tại các doanh nghiệp: dữ liệu CRM (thiếu nhất quán) và dữ liệu Phản hồi (chưa được phân loại).
# 2.1. Dữ liệu CRM nội bộ (Chứa nhiều lỗi định dạng)
crm_data <- data.frame(
CustomerID = c("C001", "C002", "C002", "C003", "C004", "C005"), # Có trùng lặp C002
FullName = c("Nguyễn Văn A", "Trần Thị B", "Trần Thị B", "Lê Văn C", "phạm thị d", "Hoàng E"),
Phone = c("0901234567", "84987654321", "84987654321", "091-234-5678", "sai_so_dien_thoai", "0933334444"),
Email = c("nva@gmail.com", "ttb@yahoo.com", "ttb@yahoo.com", "lvc@.com", "ptd@company.vn", "hoange@gmail"),
Raw_Address = c("Q1, TPHCM", "Ba Đình, HN", "Ba Đình, HN", "Đà Nẵng", "quận 1, hồ chí minh", "Cần Thơ")
)
# 2.2. Dữ liệu Hành vi mua sắm & Phản hồi (Web Scraping/App)
behavior_data <- data.frame(
CustomerID = c("C001", "C002", "C003", "C004", "C005"),
Total_Spend = c(1500000, 3200000, 500000, 7800000, 120000),
Raw_Feedback = c("Sản phẩm tốt, giao nhanh", "Hàng bị lỗi vỏ hộp", "Bình thường", "Rất hài lòng, sẽ mua lại", "Giao hàng quá chậm")
)
kable(head(crm_data), caption = "Bảng 1: Dữ liệu CRM thô chưa qua xử lý")| CustomerID | FullName | Phone | Raw_Address | |
|---|---|---|---|---|
| C001 | Nguyễn Văn A | 0901234567 | nva@gmail.com | Q1, TPHCM |
| C002 | Trần Thị B | 84987654321 | ttb@yahoo.com | Ba Đình, HN |
| C002 | Trần Thị B | 84987654321 | ttb@yahoo.com | Ba Đình, HN |
| C003 | Lê Văn C | 091-234-5678 | lvc@.com | Đà Nẵng |
| C004 | phạm thị d | sai_so_dien_thoai | ptd@company.vn | quận 1, hồ chí minh |
| C005 | Hoàng E | 0933334444 | hoange@gmail | Cần Thơ |
Vận dụng các biểu thức chính quy (Regex) và logic toán học để loại bỏ các điểm dị biệt (outliers), chuẩn hóa chuỗi và làm sạch dữ liệu tự động.
clean_crm <- crm_data %>%
# Bước 1: Loại bỏ dữ liệu trùng lặp (Deduplication)
distinct() %>%
# Bước 2: Chuẩn hóa Tên (Viết hoa chữ cái đầu)
mutate(FullName = str_to_title(FullName)) %>%
# Bước 3: Làm sạch và định dạng lại Số điện thoại (Chuyển về chuẩn +84)
mutate(
Clean_Phone = str_remove_all(Phone, "[^0-9]"), # Xóa các ký tự không phải số
Clean_Phone = case_when(
str_starts(Clean_Phone, "0") & nchar(Clean_Phone) == 10 ~ str_replace(Clean_Phone, "^0", "+84"),
str_starts(Clean_Phone, "84") & nchar(Clean_Phone) == 11 ~ paste0("+", Clean_Phone),
TRUE ~ "Invalid_Phone" # Gắn cờ các số sai logic
)
) %>%
# Bước 4: Xác thực cấu trúc Email bằng Regex
mutate(
Is_Valid_Email = str_detect(Email, "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"),
Clean_Email = ifelse(Is_Valid_Email, Email, NA) # Chuyển email sai thành Missing Value
) %>%
select(CustomerID, FullName, Clean_Phone, Clean_Email, Raw_Address)
kable(clean_crm, caption = "Bảng 2: Dữ liệu CRM sau khi làm sạch bằng Regex")| CustomerID | FullName | Clean_Phone | Clean_Email | Raw_Address |
|---|---|---|---|---|
| C001 | Nguyễn Văn A | +84901234567 | nva@gmail.com | Q1, TPHCM |
| C002 | Trần Thị B | +84987654321 | ttb@yahoo.com | Ba Đình, HN |
| C003 | Lê Văn C | +84912345678 | NA | Đà Nẵng |
| C004 | Phạm Thị D | Invalid_Phone | ptd@company.vn | quận 1, hồ chí minh |
| C005 | Hoàng E | +84933334444 | NA | Cần Thơ |
Sử dụng left_join để kết nối dữ liệu định danh khách hàng với dữ liệu hành vi mua sắm, tạo ra một góc nhìn toàn diện (360-degree view) trên một bảng dữ liệu duy nhất.
customer_360 <- clean_crm %>%
left_join(behavior_data, by = "CustomerID") %>%
# Xử lý giá trị khuyết thiếu sau khi hợp nhất
replace_na(list(Total_Spend = 0, Raw_Feedback = "No comment"))
kable(customer_360, caption = "Bảng 3: Hồ sơ khách hàng 360 độ (Đã hợp nhất)")| CustomerID | FullName | Clean_Phone | Clean_Email | Raw_Address | Total_Spend | Raw_Feedback |
|---|---|---|---|---|---|---|
| C001 | Nguyễn Văn A | +84901234567 | nva@gmail.com | Q1, TPHCM | 1500000 | Sản phẩm tốt, giao nhanh |
| C002 | Trần Thị B | +84987654321 | ttb@yahoo.com | Ba Đình, HN | 3200000 | Hàng bị lỗi vỏ hộp |
| C003 | Lê Văn C | +84912345678 | NA | Đà Nẵng | 500000 | Bình thường |
| C004 | Phạm Thị D | Invalid_Phone | ptd@company.vn | quận 1, hồ chí minh | 7800000 | Rất hài lòng, sẽ mua lại |
| C005 | Hoàng E | +84933334444 | NA | Cần Thơ | 120000 | Giao hàng quá chậm |
Trong thực tế, các trường văn bản tự do (Free-text) như Địa chỉ hay Phản hồi rất khó phân loại bằng code thuần. Tại đây, tôi mô phỏng tư duy thiết kế luồng tự động hóa bằng cách viết hàm gọi API của LLM (Mô phỏng) để bóc tách và gắn nhãn (Labeling) dữ liệu.
# Hàm mô phỏng LLM xử lý Prompt bóc tách địa chỉ (Categorization)
extract_region_ai <- function(address) {
address_lower <- tolower(address)
if (str_detect(address_lower, "hcm|hồ chí minh")) return("Miền Nam")
if (str_detect(address_lower, "hn|hà nội")) return("Miền Bắc")
if (str_detect(address_lower, "đà nẵng|cần thơ")) return("Miền Trung/Tây Nam Bộ")
return("Khác")
}
# Hàm mô phỏng LLM phân tích sắc thái văn bản (Sentiment Labeling)
analyze_sentiment_ai <- function(feedback) {
feedback_lower <- tolower(feedback)
if (str_detect(feedback_lower, "tốt|hài lòng")) return("Positive (Promoter)")
if (str_detect(feedback_lower, "lỗi|chậm")) return("Negative (Detractor)")
return("Neutral (Passive)")
}
# Áp dụng AI Functions vào tập dữ liệu
enriched_data <- customer_360 %>%
rowwise() %>%
mutate(
Region = extract_region_ai(Raw_Address),
Sentiment_Label = analyze_sentiment_ai(Raw_Feedback),
# Phân hạng khách hàng dựa trên logic toán học cơ bản
Customer_Tier = case_when(
Total_Spend >= 5000000 ~ "VIP",
Total_Spend >= 1000000 ~ "Standard",
TRUE ~ "Basic"
)
) %>%
ungroup()
kable(enriched_data %>% select(CustomerID, Region, Sentiment_Label, Customer_Tier),
caption = "Bảng 4: Kết quả làm giàu và gắn nhãn dữ liệu")| CustomerID | Region | Sentiment_Label | Customer_Tier |
|---|---|---|---|
| C001 | Miền Nam | Positive (Promoter) | Standard |
| C002 | Miền Bắc | Negative (Detractor) | Standard |
| C003 | Miền Trung/Tây Nam Bộ | Neutral (Passive) | Basic |
| C004 | Miền Nam | Positive (Promoter) | VIP |
| C005 | Miền Trung/Tây Nam Bộ | Negative (Detractor) | Basic |
Trực quan hóa kết quả phân loại để đánh giá bức tranh tổng thể về khách hàng trước khi bàn giao dữ liệu cho đội ngũ Phân tích nghiệp vụ (Business Intelligence) hoặc Marketing.
# Vẽ biểu đồ phân bố phân khúc khách hàng theo cảm xúc
ggplot(enriched_data, aes(x = Customer_Tier, fill = Sentiment_Label)) +
geom_bar(position = "dodge", color = "black", alpha = 0.8) +
scale_fill_manual(values = c("Negative (Detractor)" = "#e74c3c",
"Neutral (Passive)" = "#bdc3c7",
"Positive (Promoter)" = "#2ecc71")) +
theme_minimal() +
labs(
title = "Phân bổ Cảm xúc Khách hàng theo Phân khúc Chi tiêu",
subtitle = "Dữ liệu đã được làm sạch và gắn nhãn hoàn thiện",
x = "Phân khúc Khách hàng (Tier)",
y = "Số lượng",
fill = "Nhãn Cảm xúc (AI Labeled)"
) +
theme(text = element_text(size = 12),
legend.position = "bottom")Toàn bộ luồng mã lệnh trên đảm bảo tính toàn vẹn của tập dữ liệu từ đầu vào phân mảnh đến đầu ra chuẩn hóa. Việc kết hợp linh hoạt giữa các thuật toán Regex và tư duy ứng dụng Prompt AI cho phép hệ thống mở rộng (Scale-up) để xử lý hàng triệu dòng dữ liệu một cách tối ưu và tự động.