ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用Python做早教机构学员留存预测从特征工程到XGBoost模型的完整实操

用Python做早教机构学员留存预测从特征工程到XGBoost模型的完整实操 背景早教托管机构的学员留存有特殊性决策者是家长使用者是2到6岁的孩子续费周期通常为3到6个月。跟K12不同早教学员没有成绩作为续费依据留存更多取决于家长的感受和体验。帮一个早教客户做留存分析时发现他们的整体续费率只有54%低于行业平均的65%。通过建立留存预测模型提前识别高流失风险学员并干预三个月后续费率提升到了67%。今天分享完整的建模过程。一、数据准备与特征工程import pandas as pdimport numpy as npfrom datetime import datetime, timedelta# 加载数据enrollment pd.read_csv(enrollment.csv) # 报名记录attendance pd.read_csv(attendance.csv) # 考勤记录communication pd.read_csv(communication.csv) # 家校沟通记录payment pd.read_csv(payment.csv) # 缴费记录# 定义标签报名后180天内是否续费enrollment[renewed] (enrollment[next_enroll_date].notna() (pd.to_datetime(enrollment[next_enroll_date]) -pd.to_datetime(enrollment[enroll_date])).dt.days 180).astype(int)特征工程是早教留存预测最关键的一步。经过多轮筛选最终使用了四大类共23个特征。第一类学员基础特征features enrollment[[student_id, age_at_enrollment, gender, course_type]].copy()# 年龄分组早教阶段年龄影响大features[age_group] pd.cut(features[age_at_enrollment],bins[0, 2, 3, 4, 5, 6],labels[0-2, 2-3, 3-4, 4-5, 5-6])第二类出勤行为特征这是预测力最强的一类特征。早教学员的出勤模式直接反映家长的参与度和满意度。# 计算每个学员的出勤统计attendance[date] pd.to_datetime(attendance[date])attendance_stats attendance.groupby(student_id).agg(total_sessions(date, count),attended(status, lambda x: (x present).sum()),absent(status, lambda x: (x absent).sum()),leave(status, lambda x: (x leave).sum()),first_date(date, min),last_date(date, max)).reset_index()attendance_stats[attendance_rate] (attendance_stats[attended] / attendance_stats[total_sessions])# 出勤率变化趋势最近4周vs之前4周def calculate_trend(group, student_id, current_date):recent group[(group[student_id] student_id) (group[date] current_date - timedelta(weeks4)) (group[date] current_date)]previous group[(group[student_id] student_id) (group[date] current_date - timedelta(weeks8)) (group[date] current_date - timedelta(weeks4))]recent_rate (recent[status] present).mean() if len(recent) 0 else 0previous_rate (previous[status] present).mean() if len(previous) 0 else 0return recent_rate - previous_rateattendance_stats[attendance_trend] attendance_stats.apply(lambda row: calculate_trend(attendance, row[student_id], row[last_date]), axis1)# 连续缺课天数强流失信号def max_consecutive_absent(group, student_id):student_records group[group[student_id] student_id].sort_values(date)max_streak 0current_streak 0for status in student_records[status]:if status absent:current_streak 1max_streak max(max_streak, current_streak)else:current_streak 0return max_streakattendance_stats[max_absent_streak] attendance_stats[student_id].apply(lambda sid: max_consecutive_absent(attendance, sid))第三类家校沟通特征# 家长主动沟通次数反映关注度comm_stats communication.groupby(student_id).agg(parent_initiated(initiator, lambda x: (x parent).sum()),teacher_initiated(initiator, lambda x: (x teacher).sum()),total_comm(initiator, count),avg_response_time(response_minutes, mean),last_comm_days_ago(date, lambda x: (datetime.now() - pd.to_datetime(x).max()).days)).reset_index()# 沟通频率变化comm_stats[comm_frequency] comm_stats[total_comm] / ((pd.to_datetime(now) - enrollment.set_index(student_id)[enroll_date]).dt.days).reindex(comm_stats[student_id]).values第四类付费行为特征payment_stats payment.groupby(student_id).agg(total_paid(amount, sum),payment_count(amount, count),avg_payment(amount, mean),last_payment_days_ago(date, lambda x: (datetime.now() - pd.to_datetime(x).max()).days)).reset_index()# 课时消耗速度payment_stats[burn_rate] payment_stats[total_paid] / (attendance_stats.set_index(student_id)[attended].reindex(payment_stats[student_id]).values 1)二、模型训练from xgboost import XGBClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.metrics import classification_report, roc_auc_score# 合并所有特征model_data features.merge(attendance_stats, onstudent_id) \.merge(comm_stats, onstudent_id, howleft) \.merge(payment_stats, onstudent_id, howleft)model_data model_data.fillna(0)# 划分训练集和测试集X model_data.drop([student_id, renewed], axis1, errorsignore)y enrollment.set_index(student_id).reindex(model_data[student_id])[renewed]# 处理类别变量X pd.get_dummies(X, columns[age_group, course_type, gender])X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)model XGBClassifier(n_estimators200,max_depth5,learning_rate0.1,subsample0.8,colsample_bytree0.8,scale_pos_weightlen(y_train[y_train0]) / len(y_train[y_train1]))model.fit(X_train, y_train)y_pred model.predict(X_test)y_prob model.predict_proba(X_test)[:, 1]print(classification_report(y_test, y_pred))print(fAUC: {roc_auc_score(y_test, y_prob):.4f})模型AUC达到0.82在早教留存预测领域算是不错的水平。三、特征重要性分析from xgboost import plot_importanceimport matplotlibmatplotlib.use(Agg)importance model.feature_importances_feat_df pd.DataFrame({feature: X.columns, importance: importance})feat_df feat_df.sort_values(importance, ascendingFalse).head(10)最重要的5个特征分别是连续缺课天数、出勤率变化趋势、最近沟通天数、出勤率、课时消耗速度。这个结果跟行业经验高度一致早教学员流失不是突然发生的而是有一个渐进过程。先是出勤率下降然后家长沟通减少最后直接不来了。模型的价值在于能在出勤率刚开始下降的时候就发出预警。四、业务落地模型不是用来预测完就结束的关键是把预测结果转化为运营动作。# 按流失风险分级risk_scores model.predict_proba(X)[:, 1]model_data[risk_score] risk_scoresmodel_data[risk_level] pd.cut(risk_scores,bins[0, 0.3, 0.6, 1.0],labels[低风险, 中风险, 高风险])# 高风险学员自动触发干预high_risk model_data[model_data[risk_level] 高风险]for _, row in high_risk.iterrows():create_intervention_task(student_idrow[student_id],risk_scorerow[risk_score],suggested_actionsgenerate_intervention_suggestions(row))干预建议根据风险因素自动生成如果是出勤率下降导致的建议老师主动联系家长了解情况如果是沟通减少导致的建议安排一次课堂观摩让家长看到孩子的进步如果是课时消耗过快导致的建议提前沟通续费方案。五、总结早教学员留存预测的核心不在模型算法有多复杂而在特征工程是否贴合业务实际。出勤率、沟通频率、课时消耗速度这些特征看似简单但组合起来对流失风险的识别力非常强。模型上线后客户机构的续费率从54%提升到67%核心价值是让老师能在家长还没决定不续费的时候就主动介入而不是等家长来退费了才发现问题。
返回列表