Vấn Đề
- Đơn hàng đầu tiên KHÔNG phản ánh đủ sự gắn kết.
- Mấu chốt: Người dùng hoàn thành ĐƠN HÀNG THỨ 3 (trong vòng 60 ngày) có Retention & LTV cao vượt trội.
- Thách thức: Tỷ lệ lớn người dùng KHÔNG ĐẠT được mốc này hoặc MẤT RẤT NHIỀU THỜI GIAN.
Định Nghĩa Lại “Activation” Trong Use Case Này
- Kích hoạt = Hoàn thành ĐƠN HÀNG THỨ 3
- Khung thời gian = trong vòng 60 NGÀY (kể từ ngày đăng ký)
Dữ Liệu Cần Có Để Phân Tích
- user_id | sign_up_date (Ngày đăng ký) | acquisition_source (Nguồn thu hút) | order_1_date (Ngày mua đầu tiên) | order_2_date | order_3_date
Các Phân Tích Cần Làm
- Tỷ Lệ Kích Hoạt Tổng Thể
- Mất Bao Lâu Để Kích Hoạt? (Phân Phối TTTA)
- Tiến Trình Kích Hoạt Theo Thời Gian (Survival Analysis)
- So Sánh Đường Cong Kích Hoạt theo Nguồn
- Điểm Nghẽn Ở Đâu? (Bottleneck Analysis)
- Hiệu Suất Theo Phân Khúc
- Xu Hướng Kích Hoạt Theo Tháng Đăng Ký
activated_count = data['Activated'].sum() # Số lượng người dùng đã kích hoạt
not_activated_count = len(data) - activated_count # Số lượng người dùng chưa kích hoạt
overall_activation_rate = (activated_count / len(data)) * 100 # Tỷ lệ kích hoạt tổng thể
fig_kpi = plt.figure(figsize=(8, 5)) # Tạo figure cho KPI Card và Bar Chart
# Tạo tiêu đề cho biểu đồ
fig_kpi.suptitle(f'Tình Hình Kích Hoạt Tổng Thể (Đơn 3 <= {activation_threshold_days} ngày)', fontsize=16)
gs = gridspec.GridSpec(1, 2, width_ratios=[1.5, 1]) # Tạo layout 1 hàng 2 cột - cột 1 rộng hơn
# Tạo KPI Card và Bar Chart
# KPI Card (Cột 1)
ax_kpi = fig_kpi.add_subplot(gs[0]) # Tạo một subplot cho KPI Card
ax_kpi.text(0.5, 0.6, f'{overall_activation_rate:.1f}%', # Tỷ lệ Kích hoạt
fontsize=48, fontweight='bold', color='dodgerblue', ha='center', va='center') # Đặt tỷ lệ Kích hoạt ở giữa
ax_kpi.text(0.5, 0.4, f'Người dùng Đã Kích Hoạt\n({activated_count} / {len(data)} users)', # Số lượng người dùng đã kích hoạt
fontsize=14, ha='center', va='center', color='dimgray') # Đặt tiêu đề cho KPI Card
ax_kpi.axis('off') # Ẩn trục để chỉ hiển thị KPI Card
# Tạo Bar Chart (Cột 2)
ax_bar = fig_kpi.add_subplot(gs[1]) # Tạo một subplot cho Bar Chart
counts = [activated_count, not_activated_count] # Số lượng người dùng đã kích hoạt và chưa kích hoạt
labels = ['Đã Kích Hoạt', 'Chưa Kích Hoạt'] # Nhãn cho các cột
colors = ['dodgerblue', 'lightcoral'] # Màu sắc cho các cột
bars = ax_bar.bar(labels, counts, color=colors, width=0.6, edgecolor='white') # Tạo biểu đồ cột
ax_bar.set_ylabel('Số lượng người dùng') # Đặt nhãn cho trục Y
ax_bar.set_ylim(0, max(counts) * 1.2) # Đặt giới hạn cho trục Y
ax_bar.set_title('Phân bổ User', fontsize=12) # Đặt tiêu đề cho Bar Chart
ax_bar.set_xlabel('Tình trạng kích hoạt') # Đặt nhãn cho trục X
ax_bar.spines['top'].set_visible(False) # Ẩn đường viền trên và phải
ax_bar.spines['right'].set_visible(False) # Ẩn đường viền phải
ax_bar.spines['left'].set_visible(True) # Hiện đường viền trái
ax_bar.grid(axis='y', linestyle='--', alpha=0.7) # Thêm lưới cho trục Y để dễ đọc hơn
for bar in bars: # Thêm nhãn giá trị cho mỗi cột
yval = bar.get_height() # Lấy chiều cao của cột
# Đặt nhãn giá trị ở giữa cột - căn giữa theo chiều ngang và trên cùng theo chiều dọc
ax_bar.text(bar.get_x() + bar.get_width()/2.0, yval + 0.01*max(counts), int(yval), va='bottom', ha='center', fontsize=10)
plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # Đảm bảo không có chồng lấn giữa các phần tử trong layout
plt.subplots_adjust(wspace=0.3) # Điều chỉnh khoảng cách giữa các subplot
plt.show() # Hiển thị biểu đồ KPI Card và Bar Chart
Chỉ 31.2% người dùng đạt kích hoạt thực sự (đơn hàng 3 trong vòng 60 ngày). Điều này cho thấy có một lượng lớn người dùng chưa đạt được kích hoạt, có thể do nhiều nguyên nhân như trải nghiệm sản phẩm không tốt, thiếu thông tin, hoặc không đủ động lực để thực hiện đơn hàng thứ 3. Cần điều tra thêm để tìm hiểu nguyên nhân và cải thiện tỷ lệ kích hoạt.
activated_users_data = data[data['Activated']].copy() # Lọc ra người dùng đã kích hoạt
if not activated_users_data.empty: # Nếu có người dùng đã kích hoạt
# Tính toán TTTA cho người dùng đã kích hoạt
plt.figure(figsize=(10, 6)) # Tạo figure cho Ridgeline Plot
order = activated_users_data.groupby('AcquisitionSource')['TTTA'].median().sort_values().index # Sắp xếp theo median TTTA
g = sns.displot( # Tạo Ridgeline Plot
data=activated_users_data, # Dữ liệu là người dùng đã kích hoạt
x="TTTA", # Trục x là TTTA
row="AcquisitionSource", # Phân nhóm theo nguồn thu hút
kind="kde", # Sử dụng loại biểu đồ là KDE
aspect=4, # Tỷ lệ chiều rộng/cao của mỗi facet
height=1.5, # Chiều cao mỗi facet
row_order=order, # Sắp xếp theo thứ tự median TTTA
palette='magma', # Màu sắc cho các phân phối
fill=True, alpha=0.6, linewidth=1.5 # Tô màu và đường viền cho các phân phối
)
# Thay đổi tiêu đề cho mỗi facet
g.fig.suptitle(f'Phân Phối TTTA theo Nguồn Thu Hút (Chỉ User Đã Kích Hoạt)', y=1.03, fontsize=14, fontweight='bold')
g.set_xlabels('Số Ngày từ Đăng ký đến Đơn hàng 3') # Đặt nhãn cho trục x
g.set_ylabels('Mật độ') # Đặt nhãn cho trục y
g.set_titles("Nguồn: {row_name}") # Đặt tiêu đề cho mỗi facet theo nguồn thu hút
g.set(ylim=(0, None)) # Đặt giới hạn cho trục y
# Thêm đường median cho mỗi nhóm
def add_median_line(data, **kwargs): # Hàm thêm đường median vào mỗi facet
plt.axvline(data.median(), color='red', linestyle='--', linewidth=1.5) # Đường median
plt.text(data.median() + 1, plt.ylim()[1]*0.1, f'Median: {data.median():.1f}', color='red', fontsize=9) # Thêm nhãn cho median
g.map(add_median_line, 'TTTA') # Áp dụng hàm thêm đường median cho mỗi facet
plt.tight_layout(rect=[0, 0, 1, 0.97]) # Đảm bảo không có chồng lấn giữa các phần tử trong layout
plt.show() # Hiển thị Ridgeline Plot
Nguồn ‘Organic’ có phân phối tập trung và lệch trái nhất (kích hoạt nhanh), trong khi nguồn ‘Social Media’ có phân phối trải rộng và lệch phải hơn (nhiều người kích hoạt muộn).
data['duration'] = data['DaysToOrder3'].fillna(activation_threshold_days+1) # Thay thế NaN bằng activation_threshold_days + 1 để không ảnh hưởng đến phân tích
data['duration'] = data['duration'].clip(upper=activation_threshold_days) # Giới hạn thời gian tối đa là activation_threshold_days
#data['duration'] = data['duration'].apply(lambda x: min(x, activation_threshold_days)) # Giới hạn thời gian tối đa là activation_threshold_days
data['observed'] = data['Activated'].astype(int) # Chuyển đổi kích hoạt thành biến nhị phân (0 hoặc 1)
kmf_overall = KaplanMeierFitter() # Tạo đối tượng KaplanMeierFitter
kmf_overall.fit(durations=data['duration'], event_observed=data['observed'], label='Tỷ lệ Kích hoạt Tích lũy') # Tính toán tỷ lệ kích hoạt tích lũy
plt.figure(figsize=(12, 7)) # Tạo figure cho biểu đồ Kaplan-Meier
ax_kmf_overall = plt.subplot(111) # Tạo subplot cho biểu đồ Kaplan-Meier
# Vẽ đường cong Kaplan-Meier cho tỷ lệ kích hoạt tích lũy
(1 - kmf_overall.survival_function_).plot(ax=ax_kmf_overall, linewidth=1, color='dodgerblue', label='Tỷ lệ Kích hoạt Ước tính')
ci_sf = kmf_overall.confidence_interval_survival_function_ # Tính toán khoảng tin cậy cho đường cong Kaplan-Meier
activation_ci_lower = 1 - ci_sf.iloc[:, 1] # Upper bound của survival là lower bound của activation
activation_ci_upper = 1 - ci_sf.iloc[:, 0] # Lower bound của survival là upper bound của activation
# Vẽ khoảng tin cậy cho đường cong Kaplan-Meier
ax_kmf_overall.fill_between(activation_ci_lower.index, activation_ci_lower, activation_ci_upper, color='dodgerblue', alpha=0.2, label='Khoảng tin cậy 95%')
plt.title(f'Đường Cong Kích Hoạt Tích Lũy (Kaplan-Meier) & Khoảng Tin Cậy ({activation_threshold_days} ngày)', fontsize=14, fontweight='bold')
plt.xlabel('Số ngày kể từ khi đăng ký', fontsize=12) # Đặt nhãn cho trục X
plt.ylabel('Tỷ lệ người dùng đã kích hoạt (Đạt đơn 3, %)', fontsize=12) # Đặt nhãn cho trục Y
plt.ylim(0, 1) # Giới hạn trục Y từ 0 đến 1 (tỷ lệ phần trăm)
plt.gca().yaxis.set_major_formatter(mtick.PercentFormatter(xmax=1.0)) # Định dạng trục Y thành phần trăm
plt.grid(True, linestyle='--', alpha=0.6) # Thêm lưới cho trục Y để dễ đọc hơn
plt.axvspan(0, 30, color='gold', alpha=0.15, label='30 Ngày Đầu Quan Trọng') # Vùng vàng cho 30 ngày đầu tiên
times = [7, 15, 30, 60] # Các thời điểm quan trọng để đánh dấu
act_rates_at_times = (1 - kmf_overall.predict(times)) # Tính toán tỷ lệ kích hoạt tại các thời điểm quan trọng
for time, rate in zip(times, act_rates_at_times): # Vẽ các điểm quan trọng trên đường cong
if time <= kmf_overall.timeline[-1]: # Chỉ vẽ nếu thời gian nằm trong khoảng thời gian của dữ liệu
plt.scatter(time, rate, s=60, zorder=5, color='red') # Điểm đỏ tại các thời điểm quan trọng
plt.text(time + 1.5, rate + 0.02, f'{rate*100:.1f}%', fontsize=10, color='red', fontweight='bold') # Nhãn cho các điểm quan trọng
plt.legend(loc='lower right') # Thêm chú thích cho biểu đồ
plt.tight_layout() # Đảm bảo không có chồng lấn giữa các phần tử trong layout
plt.show() # # Hiển thị biểu đồ Kaplan-Meier
Khoảng Tin Cậy 95% (vùng tô màu xanh nhạt) cho thấy độ chắc chắn của ước tính tỷ lệ kích hoạt. Vùng tô màu vàng làm nổi bật 30 ngày đầu tiên là giai đoạn có tốc độ kích hoạt nhanh nhất và quan trọng nhất. Các điểm đỏ đánh dấu tỷ lệ kích hoạt tại các mốc quan trọng, cung cấp con số cụ thể về tiến trình kích hoạt theo thời gian

Leave a Comment