1.TỔNG QUAN DỰ ÁN

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.

2. KHỞI TẠO TẬP DỮ LIỆU THÔ (MESSY DATA)

Để 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ý")
Bảng 1: Dữ liệu CRM thô chưa qua xử lý
CustomerID FullName Phone Email Raw_Address
C001 Nguyễn Văn A 0901234567 Q1, TPHCM
C002 Trần Thị B 84987654321 Ba Đình, HN
C002 Trần Thị B 84987654321 Ba Đình, HN
C003 Lê Văn C 091-234-5678 lvc@.com Đà Nẵng
C004 phạm thị d sai_so_dien_thoai quận 1, hồ chí minh
C005 Hoàng E 0933334444 Cần Thơ

3. LÀM SẠCH VÀ XÁC THỰC DỮ LIỆU (DATA CLEANING)

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")
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 Q1, TPHCM
C002 Trần Thị B +84987654321 Ba Đình, HN
C003 Lê Văn C +84912345678 NA Đà Nẵng
C004 Phạm Thị D Invalid_Phone quận 1, hồ chí minh
C005 Hoàng E +84933334444 NA Cần Thơ

4. HỢP NHẤT DỮ LIỆU (DATA MERGING)

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)")
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 Q1, TPHCM 1500000 Sản phẩm tốt, giao nhanh
C002 Trần Thị B +84987654321 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 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

5. LÀM GIÀU DỮ LIỆU BẰNG AI (DATA ENRICHMENT & LABELING)

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")
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

6. TRỰC QUAN HÓA & KIỂM ĐỊNH CUỐI (VERIFICATION REPORT)

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")

TỔNG KẾT

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.