开发思路:模拟了5000名用户的数据,包含用户画像(年龄、性别、会员等级、城市级别)、行为特征(订单数、消费额、距上次购买天数、访问频率、购物车放弃率、客服联系、折扣使用)以及一个基于这些特征计算得出的流失标进行深入分析与挖掘
- 配置 (
# --- 配置 ---):
NUM_USERS: 模拟的用户数量。
REPORT_PREFIX: 生成报告和图表文件名的前缀。
RANDOM_SEED: 随机种子,确保结果可复现。
- 数据生成 (
generate_sample_churn_data):
- 模拟了5000名用户的数据,包含用户画像(年龄、性别、会员等级、城市级别)、行为特征(订单数、消费额、距上次购买天数、访问频率、购物车放弃率、客服联系、折扣使用)以及一个基于这些特征计算得出的流失标签 (
is_churned)。
- 数据的生成逻辑考虑了特征之间的关联性(例如,高价值会员通常更活跃,流失风险更低)。
- 生成的数据保存为CSV文件,方便后续查看和使用。
- 数据预处理 (
preprocess_data):
- 对分类变量(性别、会员等级、城市级别)进行标签编码 (
LabelEncoder),将其转换为数值。
- 选择用于建模的特征列。
- 分离特征矩阵
X 和目标标签 y。
- 模型训练与评估 (
train_and_evaluate_models):
- 将数据划分为训练集和测试集。
- 对特征进行标准化 (
StandardScaler),这对逻辑回归模型很重要。
- 训练两个模型:
- 逻辑回归 (Logistic Regression): 线性模型,简单高效,可解释性强。
- 随机森林 (Random Forest): 非线性模型,通常能捕捉更复杂的模式,性能可能更好。
- 在测试集上评估两个模型的性能,使用多种指标:准确率、精确率、召回率、F1分数、AUC-ROC。
- 比较两个模型的性能,并选择表现最好的一个(以AUC-ROC为标准)。
- 为最佳模型生成并保存混淆矩阵图表。
- 特征重要性分析 (
analyze_feature_importance):
- 分析最佳模型的特征重要性。
- 对于随机森林,使用其内置的
feature_importances_。
- 对于逻辑回归,使用系数的绝对值
np.abs(model.coef_[0])。
- 对特征按重要性进行排序,并打印Top 10。
- 使用条形图可视化特征重要性,并保存图表。
- 报告生成 (
generate_churn_prediction_report):
- 汇总整个分析过程,包括项目概述、数据描述、模型性能对比、特征重要性分析。
- 提供基于分析结果的具体业务应用建议,例如如何针对高风险用户进行干预。
- 将所有内容整合到一个
.txt 文本报告中。
- 主函数 (
main):
- 按顺序调用上述所有步骤的函数,完成从数据生成到报告输出的整个流程。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, classification_report
from sklearn.preprocessing import StandardScaler, LabelEncoder
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings("ignore")
# --- 配置 ---
NUM_USERS = 5000
REPORT_PREFIX = '电商用户流失预测报告'
RANDOM_SEED = 42
# --- 数据生成 ---
def generate_sample_churn_data(n_users):
"""生成模拟的用户流失数据"""
print("--- 正在生成模拟用户数据 ---")
np.random.seed(RANDOM_SEED)
data = []
user_ids = [f'user_{i}' for i in range(1, n_users + 1)]
for user_id in user_ids:
# --- 用户基础画像 ---
age = np.random.randint(18, 65)
gender = np.random.choice(['Male', 'Female'], p=[0.5, 0.5])
membership_tier = np.random.choice(['Bronze', 'Silver', 'Gold'], p=[0.6, 0.3, 0.1])
location_city_tier = np.random.choice(['Tier_1', 'Tier_2', 'Tier_3'], p=[0.3, 0.4, 0.3])
# --- 用户行为特征 ---
# 注册日期 (假设在2年前到1年前之间)
signup_date = datetime.now() - timedelta(days=np.random.randint(365, 2*365))
# 总订单数和总消费 (与会员等级相关)
if membership_tier == 'Gold':
total_orders = np.random.poisson(20)
total_spent = np.random.lognormal(10.5, 0.4)
elif membership_tier == 'Silver':
total_orders = np.random.poisson(10)
total_spent = np.random.lognormal(9.5, 0.5)
else: # Bronze
total_orders = np.random.poisson(5)
total_spent = np.random.lognormal(8.5, 0.6)
# 平均订单价值
avg_order_value = total_spent / max(total_orders, 1)
# 最近一次购买距离天数 (关键特征)
# 假设Gold用户更活跃,流失风险低;Bronze用户流失风险高
if membership_tier == 'Gold':
days_since_last_purchase = np.random.exponential(15) # 平均15天
elif membership_tier == 'Silver':
days_since_last_purchase = np.random.exponential(30) # 平均30天
else: # Bronze
days_since_last_purchase = np.random.exponential(60) # 平均60天
last_purchase_date = datetime.now() - timedelta(days=days_since_last_purchase)
# 月均访问次数 (活跃度)
if membership_tier == 'Gold':
monthly_visits = np.random.poisson(15)
elif membership_tier == 'Silver':
monthly_visits = np.random.poisson(10)
else: # Bronze
monthly_visits = np.random.poisson(5)
# 购物车放弃率 (加购但未购买的订单 / 总加购次数)
cart_abandonment_rate = np.random.beta(2, 5) # 大多数用户放弃率较低
# 客服联系次数 (最近一年)
customer_service_contacts = np.random.poisson(2)
# 折扣使用频率
discount_usage_freq = np.random.beta(3, 7) # 大多数用户不常用折扣
# --- 构造流失标签 (基于特征的概率) ---
# 这是一个简化的模拟逻辑,真实场景会更复杂
churn_prob = 0.0
# 长时间未购买是强信号
if days_since_last_purchase > 90:
churn_prob += 0.4
elif days_since_last_purchase > 60:
churn_prob += 0.2
elif days_since_last_purchase > 30:
churn_prob += 0.1
# 购物车放弃率高可能表示犹豫或不满
if cart_abandonment_rate > 0.7:
churn_prob += 0.15
# 客服联系多可能表示有问题
if customer_service_contacts > 5:
churn_prob += 0.1
# 非活跃用户 (月访问次数少)
if monthly_visits < 3:
churn_prob += 0.1
# Bronze会员本身流失率可能稍高
if membership_tier == 'Bronze':
churn_prob += 0.05
# 加入随机性
churn_prob += np.random.normal(0, 0.1)
churn_prob = np.clip(churn_prob, 0, 1) # 限制在0-1之间
is_churned = int(np.random.random() < churn_prob)
data.append({
'user_id': user_id,
'age': age,
'gender': gender,
'membership_tier': membership_tier,
'location_city_tier': location_city_tier,
'signup_date': signup_date,
'total_orders': total_orders,
'total_spent': round(total_spent, 2),
'avg_order_value': round(avg_order_value, 2),
'days_since_last_purchase': round(days_since_last_purchase, 2),
'monthly_visits': monthly_visits,
'cart_abandonment_rate': round(cart_abandonment_rate, 4),
'customer_service_contacts': customer_service_contacts,
'discount_usage_freq': round(discount_usage_freq, 4),
'is_churned': is_churned
})
df = pd.DataFrame(data)
# 计算衍生特征
df['signup_date'] = pd.to_datetime(df['signup_date'])
df['tenure_days'] = (datetime.now() - df['signup_date']).dt.days
df['tenure_days'] = df['tenure_days'].astype(int)
csv_filename = f'{REPORT_PREFIX}_模拟数据.csv'
df.to_csv(csv_filename, index=False, encoding='utf-8-sig')
print(f"模拟数据已生成并保存至: {csv_filename}")
return df
# --- 数据预处理 ---
def preprocess_data(df):
"""数据预处理"""
print("\n--- 正在进行数据预处理 ---")
df_processed = df.copy()
# 1. 处理日期特征 (这里已转换为天数,无需进一步处理)
# 2. 编码分类变量
le_gender = LabelEncoder()
le_membership = LabelEncoder()
le_location = LabelEncoder()
df_processed['gender_encoded'] = le_gender.fit_transform(df_processed['gender'])
df_processed['membership_tier_encoded'] = le_membership.fit_transform(df_processed['membership_tier'])
df_processed['location_city_tier_encoded'] = le_location.fit_transform(df_processed['location_city_tier'])
# 3. 选择用于建模的特征列
feature_columns = [
'age', 'gender_encoded', 'membership_tier_encoded', 'location_city_tier_encoded',
'total_orders', 'total_spent', 'avg_order_value', 'days_since_last_purchase',
'monthly_visits', 'cart_abandonment_rate', 'customer_service_contacts',
'discount_usage_freq', 'tenure_days'
]
X = df_processed[feature_columns]
y = df_processed['is_churned']
print(f"预处理完成。特征矩阵形状: {X.shape}, 标签向量形状: {y.shape}")
return X, y, le_gender, le_membership, le_location, feature_columns
# --- 模型训练与评估 ---
def train_and_evaluate_models(X, y):
"""训练和评估多个模型"""
print("\n--- 正在训练和评估模型 ---")
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=RANDOM_SEED, stratify=y)
# 特征缩放 (对逻辑回归比较重要)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
results = {}
# --- 1. 逻辑回归 ---
print("训练逻辑回归模型...")
model_lr = LogisticRegression(random_state=RANDOM_SEED, max_iter=1000)
model_lr.fit(X_train_scaled, y_train)
y_pred_lr = model_lr.predict(X_test_scaled)
y_pred_proba_lr = model_lr.predict_proba(X_test_scaled)[:, 1]
results['Logistic Regression'] = {
'model': model_lr,
'predictions': y_pred_lr,
'probabilities': y_pred_proba_lr,
'accuracy': accuracy_score(y_test, y_pred_lr),
'precision': precision_score(y_test, y_pred_lr),
'recall': recall_score(y_test, y_pred_lr),
'f1': f1_score(y_test, y_pred_lr),
'auc': roc_auc_score(y_test, y_pred_proba_lr)
}
# --- 2. 随机森林 ---
print("训练随机森林模型...")
model_rf = RandomForestClassifier(n_estimators=100, random_state=RANDOM_SEED, n_jobs=-1)
model_rf.fit(X_train, y_train) # 随机森林不需要特征缩放
y_pred_rf = model_rf.predict(X_test)
y_pred_proba_rf = model_rf.predict_proba(X_test)[:, 1]
results['Random Forest'] = {
'model': model_rf,
'predictions': y_pred_rf,
'probabilities': y_pred_proba_rf,
'accuracy': accuracy_score(y_test, y_pred_rf),
'precision': precision_score(y_test, y_pred_rf),
'recall': recall_score(y_test, y_pred_rf),
'f1': f1_score(y_test, y_pred_rf),
'auc': roc_auc_score(y_test, y_pred_proba_rf)
}
# --- 比较和报告 ---
print("\n--- 模型性能对比 ---")
comparison_df = pd.DataFrame({
'Model': list(results.keys()),
'Accuracy': [results[k]['accuracy'] for k in results.keys()],
'Precision': [results[k]['precision'] for k in results.keys()],
'Recall': [results[k]['recall'] for k in results.keys()],
'F1-Score': [results[k]['f1'] for k in results.keys()],
'AUC-ROC': [results[k]['auc'] for k in results.keys()]
})
print(comparison_df.round(4).to_string(index=False))
# 选择最佳模型 (以AUC为准)
best_model_name = comparison_df.loc[comparison_df['AUC-ROC'].idxmax(), 'Model']
best_model = results[best_model_name]['model']
best_predictions = results[best_model_name]['predictions']
best_probabilities = results[best_model_name]['probabilities']
print(f"\n选择最佳模型: {best_model_name}")
# 绘制最佳模型的混淆矩阵
cm = confusion_matrix(y_test, best_predictions)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['Not Churned', 'Churned'],
yticklabels=['Not Churned', 'Churned'])
plt.title(f'混淆矩阵 - {best_model_name}')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
cm_path = f'{REPORT_PREFIX}_混淆矩阵_{best_model_name.replace(" ", "_")}.png'
plt.savefig(cm_path)
plt.close()
print(f"混淆矩阵图表已保存至: {cm_path}")
# 打印最佳模型的详细分类报告
print(f"\n--- {best_model_name} 详细分类报告 ---")
print(classification_report(y_test, best_predictions, target_names=['Not Churned', 'Churned']))
return results, best_model_name, scaler, X_test, y_test, cm_path
# --- 特征重要性分析 ---
def analyze_feature_importance(model, feature_names, model_name):
"""分析并可视化特征重要性"""
print(f"\n--- 分析 {model_name} 特征重要性 ---")
if hasattr(model, 'feature_importances_'):
# 随机森林等树模型
importances = model.feature_importances_
indices = np.argsort(importances)[::-1]
title = f'{model_name} - 特征重要性 (基于不纯度)'
elif hasattr(model, 'coef_'):
# 线性模型 (逻辑回归系数的绝对值)
importances = np.abs(model.coef_[0])
indices = np.argsort(importances)[::-1]
title = f'{model_name} - 特征重要性 (基于系数绝对值)'
else:
print("模型不支持特征重要性分析。")
return None, None
# 创建特征重要性DataFrame
feature_importance_df = pd.DataFrame({
'feature': [feature_names[i] for i in indices],
'importance': [importances[i] for i in indices]
})
print("特征重要性排序:")
print(feature_importance_df.head(10).to_string(index=False))
# 绘制特征重要性
plt.figure(figsize=(10, 6))
sns.barplot(data=feature_importance_df.head(10), x='importance', y='feature', palette='viridis')
plt.title(title)
plt.xlabel('重要性')
plt.tight_layout()
feat_imp_path = f'{REPORT_PREFIX}_特征重要性_{model_name.replace(" ", "_")}.png'
plt.savefig(feat_imp_path)
plt.close()
print(f"特征重要性图表已保存至: {feat_imp_path}")
return feature_importance_df, feat_imp_path
# --- 报告生成 ---
def generate_churn_prediction_report(best_model_name, results, cm_path, feat_imp_df, feat_imp_path):
"""生成最终的流失预测分析报告"""
print("\n--- 正在生成流失预测分析报告 ---")
from datetime import datetime
report_filename = f"{REPORT_PREFIX}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
best_metrics = results[best_model_name]
with open(report_filename, 'w', encoding='utf-8') as f:
f.write("=" * 50 + "\n")
f.write(" 电商平台用户流失预测分析报告\n")
f.write(f" 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 50 + "\n\n")
f.write("--- 1. 项目概述 ---\n")
f.write("本项目旨在通过分析用户历史行为数据,构建机器学习模型来预测用户流失的可能性。\n")
f.write("目标是识别出高风险用户,以便运营团队可以提前采取干预措施,降低用户流失率。\n\n")
f.write("--- 2. 数据概览 ---\n")
f.write("数据来源: 模拟生成的电商平台用户数据。\n")
f.write("关键字段: 用户ID, 年龄, 性别, 会员等级, 城市级别, 总订单数, 总消费额, 平均订单价值,\n")
f.write(" 距离上次购买天数, 月均访问次数, 购物车放弃率, 客服联系次数, 折扣使用频率, 注册时长, 流失标签。\n")
f.write("原始数据已保存为 CSV 文件。\n\n")
f.write("--- 3. 模型性能评估 ---\n")
f.write("训练了两种模型:逻辑回归 (Logistic Regression) 和 随机森林 (Random Forest)。\n")
f.write("评估指标包括: 准确率 (Accuracy), 精确率 (Precision), 召回率 (Recall), F1分数 (F1-Score), AUC-ROC。\n\n")
f.write("各模型性能对比:\n")
comparison_df = pd.DataFrame({
'Model': list(results.keys()),
'Accuracy': [results[k]['accuracy'] for k in results.keys()],
'Precision': [results[k]['precision'] for k in results.keys()],
'Recall': [results[k]['recall'] for k in results.keys()],
'F1-Score': [results[k]['f1'] for k in results.keys()],
'AUC-ROC': [results[k]['auc'] for k in results.keys()]
})
f.write(comparison_df.round(4).to_string(index=False))
f.write("\n\n")
f.write(f"最佳模型: {best_model_name}\n")
f.write(f"该模型在测试集上的表现:\n")
f.write(f" - 准确率 (Accuracy): {best_metrics['accuracy']:.4f}\n")
f.write(f" - 精确率 (Precision): {best_metrics['precision']:.4f}\n")
f.write(f" - 召回率 (Recall): {best_metrics['recall']:.4f}\n")
f.write(f" - F1分数 (F1-Score): {best_metrics['f1']:.4f}\n")
f.write(f" - AUC-ROC: {best_metrics['auc']:.4f}\n")
f.write("混淆矩阵详细分析了模型的预测结果,图表已生成。\n")
f.write(f"混淆矩阵图表: {cm_path}\n\n")
f.write("--- 4. 关键驱动因素分析 ---\n")
f.write("通过分析最佳模型的特征重要性,识别出影响用户流失的最关键因素。\n")
f.write("特征重要性排序 (Top 10):\n")
f.write(feat_imp_df.head(10).to_string(index=False))
f.write("\n从上表可以看出,哪些用户行为和属性对流失影响最大。\n")
f.write(f"特征重要性图表: {feat_imp_path}\n\n")
f.write("--- 5. 业务应用与建议 ---\n")
f.write("1. 高风险用户识别: 利用最佳模型对所有用户计算流失概率,筛选出高风险用户列表。\n")
f.write("2. 精准干预:\n")
f.write(" - 对于'距离上次购买天数'长的用户,可推送召回优惠券。\n")
f.write(" - 对于'购物车放弃率'高的用户,可分析支付流程或提供客服帮助。\n")
f.write(" - 对于'月均访问次数'少的用户,可通过邮件/SMS推送个性化内容。\n")
f.write("3. 产品与运营优化:\n")
f.write(" - 根据关键特征优化用户引导和留存策略。\n")
f.write(" - 针对不同会员等级制定差异化的忠诚度计划。\n")
f.write("4. 模型迭代: 定期使用最新数据重新训练模型,以适应市场和用户行为的变化。\n\n")
f.write("=" * 50 + "\n")
f.write(" 报告结束\n")
f.write("=" * 50 + "\n")
print(f"流失预测分析报告已生成: {report_filename}")
# --- 主函数 ---
def main():
"""主函数"""
# 1. 生成数据
df_churn = generate_sample_churn_data(NUM_USERS)
# 2. 数据预处理
X, y, le_gender, le_membership, le_location, feature_cols = preprocess_data(df_churn)
# 3. 模型训练与评估
results, best_model_name, scaler, X_test, y_test, cm_path = train_and_evaluate_models(X, y)
# 4. 特征重要性分析 (针对最佳模型)
best_model = results[best_model_name]['model']
feat_imp_df, feat_imp_path = analyze_feature_importance(best_model, feature_cols, best_model_name)
# 5. 生成报告
generate_churn_prediction_report(best_model_name, results, cm_path, feat_imp_df, feat_imp_path)
print("\n用户流失预测分析流程完成。")
if __name__ == "__main__":
main()