300 Data Challenges Thực Chiến

Data Challenge #181125: Mastery Trong Gamification & A/B Testing Python

Với mô hình Challenge-based Learning, bạn sẽ không chỉ học Business Domain, mà được giải các bài toán như trong doanh nghiệp, rèn tư duy phân tích và tạo Portfolio ngay trong các khóa học phân tích dữ liệu thực chiến. Inbox mình TẠI ĐÂY nếu bạn muốn đăng ký nhé!

Đây là câu chuyện về ba người bạn thân: Tèo Đầu ToTý Đít Lép, và Tủn Bụng Bự. Mỗi người có một mục tiêu và một xuất phát điểm khác nhau, nhưng họ đều gặp nhau ở một điểm chung: mong muốn thay đổi bản thân thông qua chạy bộ. Và công cụ họ lựa chọn chính là ứng dụng Nike Run Club (NRC).

Câu chuyện của họ sẽ làm rõ những lý thuyết về động lựcsự gắn kết user qua lăng kính phân tích tăng trưởng.


Phần 1: Tèo Đầu To và Sức Mạnh Của Phản Hồi Tức Thì

Tèo Đầu To là một nhân viên văn phòng, cả ngày ngồi một chỗ và chưa bao giờ chạy bộ quá 1km. Cậu quyết định chạy để cải thiện sức khỏe.

Cú Hích Từ Những Con Số Biết Nói

Tèo xỏ giày chạy buổi đầu tiên. Cậu chạy được một đoạn thì thở không ra hơi và đi bộ về. Cậu mở ứng dụng NRC và định tắt đi. Nhưng màn hình tổng kết hiện ra đã giữ cậu lại.

Đó chính là phản hồi tức thì (Instant Feedback), và nó cực kỳ chi tiết:

  • Phản hồi định lượng (Quantitative Feedback): Màn hình không chỉ báo “Bạn đã chạy xong”. Nó vẽ lại chính xác bản đồ GPS quãng đường 1.2km mà Tèo vừa đi qua. Bên dưới là các con số chi tiết:
    • Tốc độ trung bình (Pace): 9:30/km.
    • Thời gian hoạt động: 11 phút 24 giây.
    • Calo tiêu thụ: 85 kcal.
    • Và quan trọng nhất, một banner nhỏ hiện lên: “Chúc mừng chuyến đường chạy dài nhất của bạn!”. Dù chỉ là 1.2km, nhưng đó là một kỷ lục. Lần đầu tiên, Tèo thấy nỗ lực của mình được “đo đếm” và ghi nhận một cách khách quan. Cậu đang tiến bộ.
  • Phản hồi định tính (Qualitative Feedback): Đây mới là yếu tố thay đổi cuộc chơi. Trong lúc chạy, Tèo không hề đơn độc.
    • Cậu bật bài chạy có hướng dẫn “First Run” và một giọng nói ấm áp vang lên trong tai nghe: “Chào mừng đến với Nike Run Club! Hôm nay chúng ta sẽ bắt đầu thật nhẹ nhàng.”.
    • Cứ vài phút, huấn luyện viên ảo lại lên tiếng: “Làm tốt lắm, hãy đi bộ trong một phút để lấy lại sức nào… Bây giờ hãy chạy chậm lại nhé.”.
    • Những lời động viên đúng lúc này giống như có một người bạn đồng hành thực sự, giúp Tèo quên đi mệt mỏi và cảm giác cô đơn.

Chính sự kết hợp giữa những con số và lời động viên ấm áp đã cho Tèo động lực để không bỏ cuộc.

Tìm Thấy “Dòng Chảy” Trong Từng Bước Chạy

Tèo nhanh chóng nhận ra giá trị của các bài “Chạy có hướng dẫn” (Guided Run). Chúng được thiết kế để giúp cậu đạt được trạng thái dòng chảy (Flow State).

Một bài chạy cho người mới bắt đầu không yêu cầu cậu phải chạy liên tục 20 phút. Thay vào đó, nó được chia nhỏ: 5 phút đi bộ khởi động, 1 phút chạy – 2 phút đi bộ (lặp lại 5 lần), 3 phút đi bộ thả lỏng.

  • Cấu trúc này ngăn Tèo không bị căng thẳng (stressed) vì mục tiêu quá khó. Cậu biết mình chỉ cần cố gắng trong 1 phút chạy sắp tới.
  • Nó cũng giúp cậu không bị nhàm chán (bored), vì luôn có sự thay đổi giữa đi bộ và chạy, cùng với sự dẫn dắt của huấn luyện viên.

Bài tập được thiết kế vừa vặn trên ngưỡng kỹ năng của Tèo. Cậu phải tập trung để hoàn thành, nhưng không quá sức. Cảm giác chinh phục từng khoảng “chạy 1 phút” ngắn ngủi mang lại sự thỏa mãn tức thì, khiến cậu muốn tiếp tục. Cậu chỉ cần tập trung vào giọng nói trong tai nghe, và việc chạy bộ bỗng trở nên dễ dàng hơn rất nhiều.

Bài Học Rút Ra

Đối với những user mới bắt đầu như Tèo Đầu To, họ cần những phản hồi nhanh và những chiến thắng nhỏ.

  • Đừng chỉ nói, hãy cho thấy: 
    • Thay vì thông báo chung chung, hãy trực quan hóa sự tiến bộ của user (bản đồ, biểu đồ, con số cụ thể).
    • Biến dữ liệu thành thành tích (VD: “Chuyến chạy dài nhất”, “Lần chạy nhanh nhất”).
  • Phản hồi trong thời gian thực: 
    • Sử dụng các tín hiệu âm thanh (audio cues) hoặc rung (haptic feedback) để hướng dẫn và động viên họ ngay trong lúc đang thực hiện hành động. Điều này hiệu quả hơn nhiều so với việc chỉ tổng kết ở cuối.
  • Thiết kế trải nghiệm cho người mới bắt đầu: 
    • Tạo ra một lộ trình nhập môn (onboarding journey) có cấu trúc, chia nhỏ các nhiệm vụ phức tạp thành những bước đơn giản để giúp user đạt được trạng thái dòng chảy và có được những chiến thắng nhỏ đầu tiên một cách dễ dàng.

Phần 2: Tý Đít Lép và Hành Trình Trở Thành Bậc Thầy

Tý Đít Lép đã có kinh nghiệm chạy bộ. Cậu có thể chạy 5km khá thoải mái, nhưng lại bị kẹt ở mức này trong nhiều tháng. Cậu muốn chạy nhanh hơn, xa hơn, nhưng không biết bắt đầu từ đâu. Các buổi chạy của cậu trở nên vô định và nhàm chán.

Tạo Sân Chơi Có Luật Lệ: Từ Chạy Bộ Vô Định Đến Chinh Phục Mục Tiêu

Tý khám phá ra mục “Thử thách” (Challenges) trong NRC. Cậu quyết định tham gia “Thử thách 50km tháng 11”. Ngay lập tức, việc chạy bộ của cậu có một ý nghĩa hoàn toàn mới. Đây không còn là những buổi chạy vô định, mà là một trò chơi có luật chơi (rules) và mục tiêu (goal) rõ ràng:

  • Luật chơi: Chạy đủ 50km trong tháng 11. Mọi cuộc chạy được ghi nhận bằng NRC đều được tính.
  • Mục tiêu: Lấp đầy thanh tiến trình 50km và nhận huy hiệu kỹ thuật số độc quyền của thử thách.

Cái hay của “luật chơi” này là nó tạo ra một “sân chơi” có khuôn khổ nhưng linh hoạt.

  • Tý không bị ép phải chạy 1.6km mỗi ngày.
  • Cậu có thể chạy 5km vào thứ Hai, nghỉ thứ Ba, rồi chạy 10km vào thứ Tư.
  • Khuôn khổ này cho cậu sự tự do để lên kế hoạch, để sáng tạo trong lịch trình của mình, biến việc chạy bộ từ một nghĩa vụ thành một chiến lược game.
“Trận Đấu Boss” và Cảm Giác Làm Chủ Thực Sự

Tuần cuối cùng của tháng 11, thanh tiến trình của Tý báo 45/50km. Cậu chỉ cần 5km nữa để hoàn thành. Buổi chạy 5km cuối cùng này không còn là một buổi chạy bình thường, nó đã trở thành “trận đấu boss” của Tý trong tháng.

Để tăng thêm kịch tính, Tý quyết định sẽ biến “trận đấu boss” này thành một nỗ lực phá kỷ lục cá nhân cho cự ly 5km. Và NRC chính là trợ thủ đắc lực:

  • Trước trận đấu: Tý xem lại kỷ lục 5km cũ của mình ngay trong hồ sơ: 28 phút 30 giây.
  • Trong trận đấu: Cậu bật chế độ chạy và huấn luyện viên ảo trong tai nghe liên tục cập nhật: “Bạn đang nhanh hơn 15 giây so với tốc độ kỷ lục của mình. Giữ vững nhé!”. Màn hình điện thoại cũng hiển thị song song tốc độ hiện tại và tốc độ cần có để phá PB. Cuộc đua với “bóng ma” của chính mình trở nên vô cùng gay cấn.
  • Chiến thắng: Tý về đích. Ngay lập tức, hai phần thưởng xuất hiện gần như cùng lúc:
    • Một thông báo lớn hiện ra: “KỶ LỤC CÁ NHÂN MỚI! 5km trong 27:50”.
    • Ngay sau đó là: “THỬ THÁCH HOÀN TẤT! Bạn đã nhận được huy hiệu 50km tháng 11”.

Đây là một khoảnh khắc bùng nổ. Tý không chỉ hoàn thành mục tiêu, cậu còn vượt qua phiên bản tốt nhất của chính mình. Cảm giác làm chủ (mastery) đến từ đây. Nó không phải là một cảm giác mơ hồ, mà được chứng thực bằng những bằng chứng không thể chối cãi: một con số phá kỷ lục mới được khắc ghi trong hồ sơ và một chiếc huy hiệu lấp lánh trong bộ sưu tập. Cậu đã thực sự lên một level mới.

Bài Học Rút Ra

Đối với những user đã từng trải nghiệm như Tý Đít Lép thì họ cần một con đường để chứng tỏ sự tiến bộ.

  • Biến dữ liệu thành cột mốc: 
    • Đừng chỉ hiển thị dữ liệu (tốc độ, quãng đường).
    • Hãy biến chúng thành những cột mốc có ý nghĩa mà user có thể chinh phục (Kỷ lục cá nhân, Cấp độ, Chuỗi hoạt động).
  • Tạo ra cuộc chơi game: 
    • Các tính năng như Thử thách, Bảng xếp hạng tạo ra một cuộc chơi để user thể hiện kỹ năng của họ.
  • Thiết kế những “trận đấu boss”: 
    • Tạo cơ hội để user có những khoảnh khắc đỉnh cao bằng cách cho phép họ theo đuổi nhiều mục tiêu cùng lúc.
    • Việc hoàn thành một thử thách và đồng thời phá kỷ lục cá nhân tạo ra một phần thưởng cảm xúc lớn hơn nhiều so với việc đạt được chúng một cách riêng lẻ.

Phần 3: Tủn Bụng Bự và Cỗ Máy Gắn Kết Từ Các Vòng Lặp Phần Thưởng

Tủn Bụng Bự là một lão làng trong giới chạy bộ, đã hoàn thành nhiều cuộc thi marathon. Với Tủn, động lực không còn đến từ việc chạy xa hơn một chút hay nhanh hơn vài giây. Cậu cần một hệ thống đủ sâu sắc để giữ cho đam mê của mình luôn cháy.

Sức Hút Khó Cưỡng Của Sự Hoàn Thiện (Completion)

Mục tiêu lớn của Tủn là chinh phục cự ly ultra marathon 70km. Để làm điều này, cậu tham gia một kế hoạch tập luyện (Training Plan) trên NRC. Kế hoạch này không chỉ đưa ra một mục tiêu cuối cùng, nó chia nhỏ hành trình thành các mục tiêu nhỏ (small goals) hàng tuần.

Đây chính là vòng lặp phần thưởng (Reward Loop) đầu tiên.

  • Mỗi khi Tủn hoàn thành một bài chạy trong kế hoạch, ứng dụng sẽ đánh một dấu tick vào bài tập đó.
  • Việc nhìn thấy các dấu tick được lấp đầy trên lịch tập hàng tuần mang lại một cảm giác thỏa mãn của sự hoàn thiện (Completion).
  • Giống như thanh tiến trình trên LinkedIn, một kế hoạch tập luyện còn dang dở sẽ tạo ra một sự “khó chịu” nhẹ, thôi thúc Tủn phải hoàn thành nó.
Ma Trận “Vòng Lặp Phần Thưởng Đa Lớp” (Multiple Reward Loops)

Điều thực sự giữ chân Tủn là cách NRC giăng ra một ma trận các vòng lặp phần thưởng đa lớp. Hãy xem một buổi chạy cuối tuần của Tủn để thấy cỗ máy này hoạt động như thế nào.

Kế hoạch của Tủn hôm nay là một bài chạy dài 21km. Cậu hoàn thành nó và cảm thấy hài lòng. Nhưng khi cậu nhìn vào màn hình tổng kết, một loạt hiệu ứng xảy ra:

  • Vòng lặp 1 (Kế hoạch tập luyện): “Bài tập hoàn thành: Chạy dài 21km”. Một dấu tick xanh hiện lên. Phần thưởng: Sự thỏa mãn vì đã tuân thủ kế hoạch.
  • Vòng lặp 2 (Thử thách tháng): Ngay sau đó, một thông báo khác xuất hiện: “Thử thách tháng 11: 150/200km”. Thanh tiến trình tiến một đoạn dài. Cậu chỉ còn 50km nữa.
  • Vòng lặp 3 (Cấp độ & Vinh danh): Tủn vào xem hồ sơ cá nhân. Cậu thấy tổng quãng đường của mình đã đạt 1,995km. Một màu sắc mới (mức “Volt”) và danh hiệu “Nhà Vô Địch” đang chờ ở mốc 2,000km. Cậu chỉ còn cách 5km nữa! Cái mốc tròn trịa và phần thưởng danh giá này có một sức hút mãnh liệt.
  • Vòng lặp 4 (Xã hội): Tủn lướt qua bảng xếp hạng bạn bè. Nhờ 21km vừa chạy, cậu đã vượt qua Tý Đít Lép để chiếm lấy vị trí số 1 tuần này.

Tại thời điểm này, Tủn đã hoàn thành mục tiêu ban đầu của mình. Cậu có thể dừng lại. Nhưng hệ thống đã khéo léo đặt cậu vào một tình thế khó xử: Cậu chỉ còn cách một cột mốc cực kỳ ý nghĩa (2,000km) đúng 5km nữa. Việc dừng lại bây giờ có cảm giác thật “nuối tiếc”.

Và Tủn quyết định xỏ giày chạy thêm 5km nữa. Cậu không hề lên kế hoạch cho việc này, nhưng sức hút của việc  hoàn thiện vòng lặp cấp độ là quá lớn.

Khi cậu kết thúc 5km bổ sung, ứng dụng bùng nổ với phần thưởng:

  • “CHÚC MỪNG! BẠN ĐÃ ĐẠT MỐC 2,000KM!”
  • “CẤP ĐỘ MỚI: VOLT!”

Trong một buổi sáng, Tủn đã nhận được phần thưởng từ 3 vòng lặp khác nhau (kế hoạch, thử thách, cấp độ) và một chiến thắng xã hội. Cỗ máy này đảm bảo rằng Tủn luôn ở gần một mục tiêu nào đó, khiến cậu luôn có lý do để quay trở lại và chạy “thêm một chút nữa”.

Bài Học Rút Ra

Để xây dựng lòng trung thành cho những user lão làng, hãy tạo ra một hệ sinh thái các mục tiêu chồng chéo.

  • Đừng chỉ có một con đường: 
    • Hãy xây dựng nhiều hệ thống tiến trình song song (VD: cấp độ tài khoản, thứ hạng mùa giải, thành tích trọn đời, thử thách tạm thời).
    • Mỗi hệ thống là một vòng lặp phần thưởng riêng.
  • Thiết kế sự giao thoa: 
    • Hãy tính toán các mốc phần thưởng để chúng có thể đạt được gần nhau.
    • Việc hoàn thành mục tiêu A phải cho user thấy họ đang ở rất gần mục tiêu B. Đây là cách tạo ra hành vi “chỉ một chút nữa thôi”.
  • Phần thưởng phải đa dạng: 
    • Kết hợp nhiều loại phần thưởng khác nhau: sự hoàn thiện (dấu tick, thanh tiến trình), phần thưởng ảo (huy hiệu, cấp độ, màu sắc), và phần thưởng xã hội (thứ hạng, sự công nhận).
    • Điều này đảm bảo luôn có một thứ gì đó phù hợp với động lực của mỗi user.

Bài Toán A/B Testing

Nike Run Club (NRC) là một ứng dụng chạy bộ phổ biến, nhưng đội ngũ sản phẩm nhận thấy tỷ lệ user mới rời bỏ ứng dụng sau lần chạy đầu tiên còn khá cao. Trải nghiệm sau khi kết thúc một buổi chạy chỉ đơn thuần là một màn hình tóm tắt các số liệu khô khan.

Giả thuyết: Nếu bạn thay thế màn hình tóm tắt tiêu chuẩn bằng một màn hình chúc mừng mang tính động viên và ăn mừng thành tích của user (dù nhỏ nhất), bạn có thể tăng cường cảm xúc tích cực, tạo ra một “khoảnh khắc thành công” đáng nhớ, từ đó cải thiện tỷ lệ giữ chân (retention) user mới.

Bài toán: Thiết kế và thực thi một thử nghiệm A/B để kiểm chứng giả thuyết trên.

  • Nhóm A (Control): Trải nghiệm hiện tại – màn hình tóm tắt thông thường.
  • Nhóm B (Variant): Trải nghiệm mới – màn hình chúc mừng với hiệu ứng và thông điệp động viên.
A/B Testing là gì?

Hãy tưởng tượng bạn là một đầu bếp muốn cải tiến công thức món gà rán của mình. Bạn có công thức cũ (gọi là A) và một công thức mới với gia vị đặc biệt (gọi là B).

Để biết công thức nào ngon hơn, bạn không thể chỉ dựa vào cảm tính. Thay vào đó, bạn làm hai việc:

  • Bạn chia 100 khách hàng thành hai nhóm ngẫu nhiên: 50 người ăn gà công thức A, 50 người ăn gà công thức B.
  • Bạn đo lường xem nhóm nào gọi thêm hoặc cho điểm cao hơn.

Đây chính là bản chất của A/B testing: So sánh hai phiên bản (A và B) của một thứ gì đó để xem phiên bản nào hoạt động hiệu quả hơn.

  • Phiên bản A (Control): Là phiên bản gốc, không thay đổi. Trong case study này, đó là màn hình tóm tắt thông thường.
  • Phiên bản B (Variant): Là phiên bản mới mà bạn muốn thử nghiệm. Ở đây, đó là màn hình chúc mừng.
Tại sao phải làm A/B Testing?
  • Ra quyết định dựa trên dữ liệu, không phải cảm tính: Thay vì tranh cãi “tôi nghĩ màn hình mới đẹp hơn”, bạn để cho hành vi của user trả lời câu hỏi: “Màn hình nào giúp giữ chân user tốt hơn?”. Dữ liệu sẽ cho bạn câu trả lời khách quan.
  • Giảm thiểu rủi ro: Thay vì ra mắt một tính năng mới cho toàn bộ 100% user, bạn chỉ thử nghiệm trên một nhóm nhỏ. Nếu phiên bản B tệ hơn dự kiến hoặc gây ra lỗi, chỉ một phần user bị ảnh hưởng và bạn có thể dừng thử nghiệm ngay lập tức.
  • Tối ưu hóa liên tục: A/B testing là công cụ mạnh mẽ để cải tiến sản phẩm một cách từ từ và chắc chắn. Mỗi thử nghiệm thành công là một bước tiến nhỏ giúp sản phẩm tốt hơn.
Thiết kế thử nghiệm: Suy luận đằng sau các lựa chọn
Chỉ số chính

Chỉ số chính là thước đo trực tiếp cho giả thuyết. Nếu các chỉ số này tăng lên, thử nghiệm được coi là thành công.

  • Day-1 Retention Rate: Tỷ lệ user quay lại vào ngày hôm sau.
    • Đây là chỉ số nhạy nhất, phản ánh tác động cảm xúc tức thì.
    • Một trải nghiệm tích cực (màn hình chúc mừng) có khả năng cao sẽ thôi thúc user quay lại ngay ngày hôm sau để “lặp lại” cảm giác đó.
  • Day-7 Retention Rate: Tỷ lệ user quay lại trong vòng 7 ngày.
    • Chỉ số này đo lường sự gắn bó ở mức độ sâu hơn.
    • Nó cho biết liệu “khoảnh khắc thành công” ban đầu có đủ mạnh để hình thành một thói quen sơ khai hay không.
    • Một sự cải thiện ở D7 Retention có giá trị lớn hơn nhiều so với D1.
Chỉ số phụ

Một tính năng mới có thể cải thiện một chỉ số này nhưng lại vô tình làm ảnh hưởng một chỉ số khác. Chỉ số phụ giúp bạn phát hiện những “tác dụng phụ” không mong muốn đó.

  • Share Rate: Tỷ lệ user chia sẻ kết quả buổi chạy.
    • Màn hình tóm tắt cũ có thể có nút “Chia sẻ” ở vị trí dễ thấy.
    • Màn hình chúc mừng mới, dù tạo cảm xúc tốt, có thể làm user bị phân tâm hoặc che khuất nút chia sẻ, dẫn đến sụt giảm hành vi lan truyền tự nhiên của sản phẩm.
    • Bạn cần đảm bảo rằng mình không đánh đổi sự tăng trưởng tự nhiên để lấy retention.
Phân khúc Phân tích: Trả lời câu hỏi “Hiệu quả với ai?”

Việc chỉ nhìn vào kết quả tổng thể có thể che giấu những sự thật quan trọng. Một tính năng có thể rất hiệu quả với nhóm user này nhưng lại vô dụng cho nhóm khác. Phân tích theo phân khúc giúp bạn hiểu sâu hơn về tác động của sản phẩm.

  • Người mới bắt đầu (Beginners): User có quãng đường chạy đầu tiên < 3km.
    • Đây là nhóm user dễ có nguy cơ rời bỏ cao nhất.
    • Giả thuyết là màn hình chúc mừng sẽ có tác động lớn nhất đến nhóm này.
  • Người chạy tiềm năng (Potential Runners): User có quãng đường chạy đầu tiên >= 3km.
    • Nhóm này đã có một mức độ cam kết ban đầu cao hơn.
    • Họ có thể ít bị ảnh hưởng bởi các yếu tố cảm xúc hơn và quan tâm nhiều hơn đến các chỉ số hiệu suất.
    • So sánh tác động giữa hai nhóm sẽ cho bạn biết liệu tính năng này có sức hấp dẫn rộng hơn hay chỉ dành cho người mới.
Phân tích bằng Hồi quy Logistic (Logistic Regression)

Phép thử Z-test cho bạn biết phiên bản B có tốt hơn phiên bản A hay không. Tuy nhiên, nó chưa trả lời được các câu hỏi sâu hơn:

  • Tác động của phiên bản B lên nhóm “Người mới bắt đầu” và nhóm “Người chạy tiềm năng” có khác nhau nhiều không?
  • Liệu có phải sự tăng trưởng retention chủ yếu đến từ một nhóm nào đó không?

Hồi quy Logistic cho phép bạn phân tích tác động đồng thời của nhiều yếu tố (nhóm A/B, phân khúc user) lên một kết quả duy nhất (có quay lại sau 7 ngày hay không).

Bạn xây dựng một mô hình để dự đoán khả năng retention_day_7 dựa trên 3 yếu tố:

  1. group_B: User có thuộc nhóm B (thấy màn hình mới) không?
  2. is_potential: User có phải là “Người chạy tiềm năng” không?
  3. interaction: Tác động kết hợp của việc vừa thuộc nhóm B, vừa là “Người chạy tiềm năng”.

Kết quả chính từ mô hình:

Yếu tốHệ số (Odds Ratio)P-valueÝ nghĩa trong thực tế
group_B~1.350.011Đối với một “Người mới bắt đầu”, việc thấy màn hình mới (thuộc nhóm B) làm tăng “tỷ lệ cược” (odds) họ sẽ quay lại sau 7 ngày lên 35%. P-value < 0.05 cho thấy tác động này là thật, không phải do may mắn.
is_potential~1.150.151Trong nhóm A (nhóm cũ), không có sự khác biệt đáng kể về retention giữa “Người chạy tiềm năng” và “Người mới bắt đầu”.
interaction~0.920.515Tác động của màn hình mới lên nhóm “Người chạy tiềm năng” không khác biệt một cách có ý nghĩa so với tác động lên nhóm “Người mới bắt đầu”. P-value lớn cho thấy điều này.

Insight rút ra từ Hồi quy Logistic

  • Xác nhận lại Z-test một cách mạnh mẽ hơn: Mô hình khẳng định rằng việc thuộc Nhóm B có tác động tích cực và đáng kể đến việc giữ chân user (tăng 35% odds).
  • Tính năng hiệu quả cho tất cả mọi người: Quan trọng hơn, mô hình cho thấy không có sự khác biệt lớn về mức độ hiệu quả của tính năng mới giữa hai phân khúc user (vì hệ số interaction không có ý nghĩa thống kê). Điều này có nghĩa là màn hình chúc mừng mới hoạt động tốt cho cả người mới chạy lẫn người đã có tiềm năng.
import pandas as pd
import numpy as np
from statsmodels.stats.proportion import proportions_ztest
import statsmodels.api as sm
import matplotlib.pyplot as plt
import seaborn as sns
import os

print("--- BƯỚC 1: ĐỌC VÀ CHUẨN BỊ DỮ LIỆU ---")

try:
    df = pd.read_excel("nrc_ab_test_data.xlsx")
    print("- Đọc thành công file nrc_ab_test_data.xlsx.")
except FileNotFoundError:
    print("Lỗi: Không tìm thấy file nrc_ab_test_data.xlsx.")
    exit()

if not os.path.exists("charts"):
    os.makedirs("charts")
    print("- Đã tạo thư mục 'charts' để lưu biểu đồ.")

print("\n--- BƯỚC 2: PHÂN TÍCH TỔNG THỂ (Z-TEST) ---")

group_a = df[df['group'] == 'A']
group_b = df[df['group'] == 'B']

nobs_a, nobs_b = len(group_a), len(group_b)

def perform_z_test(metric_name, successes_a, successes_b, nobs_a, nobs_b):
    stat, pval = proportions_ztest([successes_a, successes_b], [nobs_a, nobs_b])
    
    rate_a = successes_a / nobs_a
    rate_b = successes_b / nobs_b
    
    print(f"\n*   Phân tích chỉ số: {metric_name}")
    print(f"    - Tỷ lệ Nhóm A: {rate_a:.2%} (với {successes_a}/{nobs_a} user)")
    print(f"    - Tỷ lệ Nhóm B: {rate_b:.2%} (với {successes_b}/{nobs_b} user)")
    print(f"    - P-value: {pval:.4f}")
    
    if pval < 0.05:
        print(f"    -> KẾT LUẬN: Có ý nghĩa thống kê. Phiên bản B {'tốt hơn' if rate_b > rate_a else 'kém hơn'} phiên bản A.")
    else:
        print("    -> KẾT LUẬN: Không có ý nghĩa thống kê. Sự khác biệt không đủ lớn để kết luận.")
        
    return rate_a, rate_b

d1_success_a, d1_success_b = group_a['retention_day_1'].sum(), group_b['retention_day_1'].sum()
d7_success_a, d7_success_b = group_a['retention_day_7'].sum(), group_b['retention_day_7'].sum()
shared_success_a, shared_success_b = group_a['shared_run'].sum(), group_b['shared_run'].sum()

rate_a_d1, rate_b_d1 = perform_z_test("Day-1 Retention (Chỉ số chính)", d1_success_a, d1_success_b, nobs_a, nobs_b)
rate_a_d7, rate_b_d7 = perform_z_test("Day-7 Retention (Chỉ số chính)", d7_success_a, d7_success_b, nobs_a, nobs_b)
rate_a_shared, rate_b_shared = perform_z_test("Tỷ lệ Chia sẻ (Chỉ số bảo vệ)", shared_success_a, shared_success_b, nobs_a, nobs_b)

print("\n--- BƯỚC 3: PHÂN TÍCH THEO PHÂN KHÚC ---")

df['segment'] = np.where(df['first_run_distance'] < 3, 'Beginners', 'Potential Runners')
print("- Đã phân user thành 2 phân khúc: 'Beginners' và 'Potential Runners'.")

def analyze_segment(df_segment, segment_name):
    print(f"\n*   Phân tích phân khúc: {segment_name} (Tổng số {len(df_segment)} user)")
    group_a_seg = df_segment[df_segment['group'] == 'A']
    group_b_seg = df_segment[df_segment['group'] == 'B']
    nobs_a_seg, nobs_b_seg = len(group_a_seg), len(group_b_seg)

    if nobs_a_seg == 0 or nobs_b_seg == 0:
        print("    -> Không đủ dữ liệu cho cả hai nhóm trong phân khúc này.")
        return

    d7_success_a_seg, d7_success_b_seg = group_a_seg['retention_day_7'].sum(), group_b_seg['retention_day_7'].sum()
    perform_z_test("Day-7 Retention", d7_success_a_seg, d7_success_b_seg, nobs_a_seg, nobs_b_seg)

analyze_segment(df[df['segment'] == 'Beginners'], 'Người mới bắt đầu (< 3km)')
analyze_segment(df[df['segment'] == 'Potential Runners'], 'Người chạy tiềm năng (>= 3km)')

print("\n--- BƯỚC 4: PHÂN TÍCH SÂU BẰNG HỒI QUY LOGISTIC ---")

df_logit = df.copy()

df_logit['group_B'] = (df_logit['group'] == 'B').astype(int)
df_logit['is_potential'] = (df_logit['segment'] == 'Potential Runners').astype(int)

df_logit['interaction'] = df_logit['group_B'] * df_logit['is_potential']

Y = df_logit['retention_day_7']
X = df_logit[['group_B', 'is_potential', 'interaction']]
X = sm.add_constant(X)

logit_model = sm.Logit(Y, X).fit(disp=0)

print("\n*   Kết quả mô hình Hồi quy Logistic cho Day-7 Retention:")
print(logit_model.summary())

print("\n*   Diễn giải các hệ số (Odds Ratios):")
odds_ratios = pd.DataFrame(np.exp(logit_model.params), columns=['Odds Ratio'])
print(odds_ratios)

print("\n--- BƯỚC 5: TẠO BIỂU ĐỒ TRỰC QUAN HÓA ---")

def plot_bar_chart(metric, rate_a, rate_b, title, filename):
    plt.figure(figsize=(8, 6))
    sns.set_style("whitegrid")
    
    data = {'Group': ['Group A', 'Group B'], 'Rate': [rate_a, rate_b]}
    
    sns.barplot(x='Group', y='Rate', data=data, palette=['#4c72b0', '#dd8452'], hue='Group', legend=False)
    
    for index, value in enumerate(data['Rate']):
        plt.text(index, value + 0.005, f'{value:.2%}', ha='center', va='bottom', fontsize=12)
        
    plt.title(title, fontsize=16, pad=20)
    plt.ylabel('Tỷ lệ', fontsize=12)
    plt.xlabel('Nhóm', fontsize=12)
    plt.ylim(0, max(rate_a, rate_b) * 1.2)
    
    filepath = os.path.join("charts", filename)
    plt.savefig(filepath)
    plt.close()
    print(f"- Đã lưu biểu đồ '{title}' tại: {filepath}")

plot_bar_chart('Day-1 Retention', rate_a_d1, rate_b_d1, 'So sánh Tỷ lệ Day-1 Retention', 'd1_retention_comparison.png')
plot_bar_chart('Day-7 Retention', rate_a_d7, rate_b_d7, 'So sánh Tỷ lệ Day-7 Retention', 'd7_retention_comparison.png')
plot_bar_chart('Shared Run', rate_a_shared, rate_b_shared, 'So sánh Tỷ lệ Chia sẻ (Guardrail)', 'shared_run_comparison.png')

print("\nPhân tích hoàn tất!")