欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

程序员文章站 2024-01-21 11:12:28
...


前言

该文章为连载的第一篇:RFM用户分类模型

某家在线教育机构拥有自己开发的教育产品VLE,该教育机构提供了他们四个学期里,开展的七门课的数据,接下来我会根据这些数据,为该教育机构做一系列的数据分析,包括用户的RFM模型、用户分群特征、用户成绩分析等等。

该教育机构部分数据库结构如下

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1


如下这一篇文章为:用户的RFM模型

先简单介绍一下RFM模型:

RFM模型是衡量客户价值和客户创利能力的重要工具和手段。
该机械模型通过一个客户的

  1. 近期购买行为(R:Recency)、
  2. 购买的总体频率(F:Frequency)
  3. 花了多少钱(M:Monetary)

3项指标来描述该客户的价值状况。


该案例是在线教育产品,并且这里只提供用户一学期购买课程的消费注册使用情况,所以RFM会调正为:

  1. 近期产品使用行为(R:Recency)、
  2. 产品使用频率(F:Frequency)
  3. 花了多少钱(M:Monetary)

这个案例中计算用户RFM模型会用到:

  1. studentRegistration 学生注册信息表 (记为regi)
  2. studentVle 学生产品交互行为表 (记为vle)
  3. courses 课程表 (记为courses)

其中:

  1. code_presentation为学期
  2. code_module为课程
  3. 其他字面意思比较好理解

一、导入库

import pandas as pd
import numpy as np
import datetime
import time
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS']
matplotlib.rcParams['axes.unicode_minus']=False
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn.preprocessing import LabelEncoder
np.set_printoptions(suppress=True)
pd.set_option('display.float_format', lambda x: '%.4f' % x)


二、构造价格数据

因为这里没有关于价格的数据,我们需要对每门课构造一个价格

构造价格的依据:

  • 假设AAA是最简单的课程课程难度随着AAA、BBB、CCC递增,价格会越来越贵
  • 假设课程价格的基准是10*时间长度,同时价格根据难度增加,BBB为11乘时间长度,CCC为12,以此类推
  • 同时物价膨胀学校每年也会调整增10%的学费

实现代码如下(示例):

presentation=sorted(courses["code_presentation"].unique().tolist())
#presentation 为['2013B', '2013J', '2014B', '2014J']
module=sorted(courses["code_module"].unique().tolist())
#module 为['AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF', 'GGG']

这里需要注意的是,不是每一学期都会开展这七门课


#基准价格
x=10
module_price={}
for m in module:
    module_price[m]=x+module.index(m)
    
#module_price 为{'AAA': 10, 'BBB': 11, 'CCC': 12, 'DDD': 13, 'EEE': 14, 'FFF': 15, 'GGG': 16}

#增加价格列,并用每门课基准价格乘时间长度
courses["price"]=courses["code_module"].map(module_price)
courses["price"]=courses["module_presentation_length"]*courses["price"]

#从第二学期开始,价格上升10%
for pre in presentation[1:]:
    courses.loc[(courses["code_presentation"]==pre),"price"]*=(1.1)**(presentation.index(pre))

courses.sort_values(by=["code_presentation","code_module"]).head(10)

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

三、分别计算RFM信息

这里我们只选取最新的一个学期2014J计算

1、聚合所需要的信息

代码如下(示例):

#聚合【学生注册信息表】和【学生vle交互信息表】,筛选出这一学期的

#用how="outer"的原因是部分学生注册了但没有使用VLE
regi_vle_this_pre=pd.merge(regi[regi["code_presentation"]=="2014J"],vle[vle["code_presentation"]=="2014J"],on=["code_module","code_presentation","id_student"],how="left")

#并对注册但没有使用过产品的用户,点击数调整成0
regi_vle_this_pre["sum_click"]=regi_vle_this_pre["sum_click"].fillna(0)
regi_vle_this_pre[regi_vle_this_pre["sum_click"].isna()]

#再聚合【课程表】
df=pd.merge(regi_vle_this_pre,courses[courses["code_presentation"]=="2014J"][["code_module","price"]],on="code_module")

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

2、计算每个学生的购买课程总价

这学期有10670学生,大部分学生都只买了一门课,有586人买了两门课,2人买了3门课,具体计算先忽略。

amount=pd.crosstab(df["id_student"],df["code_module"],values=df["price"],aggfunc="mean").fillna(0).reset_index()

amount["total_amount"]=amount["AAA"]+amount["BBB"]+amount["CCC"]+amount["DDD"]+amount["EEE"]+amount["FFF"]+amount["GGG"]

amount.sort_values(by="total_amount",ascending=False)

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

#将总价也聚合到上面的表里
df=pd.merge(df,amount[["id_student","total_amount"]],on="id_student")

3、计算RFM各指标

对各项指标,去查看一个描述统计值,并用箱线图查看异常值是否较多,再定义怎么去界定RFM高低的界限。

3.1 R

df.groupby("id_student").agg({"date":"max"}).describe(percentiles=[.25,.5,.75,.8])


date_list=[x for x in df.groupby("id_student").agg({"date":"max"})["date"].values.tolist() if str(x)!="nan" ]
#去掉nan值

plt.boxplot(x=date_list)
plt.show()

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1
date这里最大是开课后有269天,最小的是开课前25天,这里假设每门课的开课时间一致,现在就是开课后269天,我们拿到的数据都是更新的。

这里去定义7天内使用过的才算近期使用,即269-7=262以上为R近用户

3.2 F

df.groupby(["id_student"]).agg({"date":pd.Series.nunique}).describe(percentiles=[.25,.5,.75,.8])

plt.boxplot(df.groupby(["id_student"]).agg({"date":pd.Series.nunique})["date"].values)
plt.show()

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1
中位数和平均数相差加大,呈左偏态分布
大多数用户使用次数较低,但有少部分用户使用次数异常高,拉高平均值

这里暂时取中位数值作为F的临界点,即42天。

3.3 M

df.groupby(["id_student"]).agg({"total_amount":"mean"}).describe(percentiles=[.25,.5,.75,.8])

plt.boxplot(df.groupby(["id_student"]).agg({"total_amount":"mean"})["total_amount"].values)
plt.show()

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

平均数比中位数略高,有部分偏高异常值,原因是

  • 学生报名情况:这学期有10670学生,大部分学生都只买了一门课,有586人买了两门课,2人买了3门课
  • 课程价格情况:
    [Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

这里定义M高用户为选择课程价格为大于4500的用户

3.5 计算每个用户的RFM

回顾一下定义RFM各指标为高的界限:

  1. 前7天内有在使用产品为R高
  2. 在这学期中,使用产品超过42天为F高
  3. 购买价格课程大于4500为M高
df["cnt_use_day"]=df["date"]
rfm_df=df.groupby("id_student").agg({"date":"max","cnt_use_day":pd.Series.nunique,"total_amount":"mean","date_unregistration":"max"}).reset_index()


rfm_df["r"]=rfm_df["date"].apply(lambda x:1 if x>=262 else 0)
rfm_df["f"]=rfm_df["cnt_use_day"].apply(lambda x:1 if x>=42 else 0)
rfm_df["m"]=rfm_df["total_amount"].apply(lambda x:1 if x>=4500 else 0)

rfm_df["user_label"]=np.nan

RFM的标签分层(按RFM顺序):
(1,1,1)重要价值用户
(0,1,1)重要唤回客户
(1,0,1)重要深耕客户
(0,0,1)重要挽回客户
(1,1,0)潜力客户
(1,0,0)新客户
(0,1,0)一般维持客户
(0,0,0)流失客户

rfm_df.loc[(rfm_df["r"]==1)&(rfm_df["f"]==1)&(rfm_df["m"]==1),"user_label"]="重要价值用户"
rfm_df.loc[(rfm_df["r"]==0)&(rfm_df["f"]==1)&(rfm_df["m"]==1),"user_label"]="重要唤回用户"
rfm_df.loc[(rfm_df["r"]==1)&(rfm_df["f"]==0)&(rfm_df["m"]==1),"user_label"]="重要深耕用户"
rfm_df.loc[(rfm_df["r"]==0)&(rfm_df["f"]==0)&(rfm_df["m"]==1),"user_label"]="重要挽回客户"
rfm_df.loc[(rfm_df["r"]==1)&(rfm_df["f"]==1)&(rfm_df["m"]==0),"user_label"]="潜在用户"
rfm_df.loc[(rfm_df["r"]==1)&(rfm_df["f"]==0)&(rfm_df["m"]==0),"user_label"]="新用户"
rfm_df.loc[(rfm_df["r"]==0)&(rfm_df["f"]==1)&(rfm_df["m"]==0),"user_label"]="一般维持客户"
rfm_df.loc[(rfm_df["r"]==0)&(rfm_df["f"]==0)&(rfm_df["m"]==0),"user_label"]="流失用户"

rfm_df["user_label"].value_counts()

结果如下:
[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

可视化:

plt.figure(figsize=(5,5))
cmap = plt.get_cmap("tab20c")
outer_colors = cmap(np.arange(8)*1)
#print(outer_colors)

data = rfm_df["user_label"].value_counts().sort_index().values.tolist()
label=rfm_df["user_label"].value_counts().sort_index().index.tolist()
 
#突出重要价值用户
plt.pie(data,pctdistance=0.7,autopct='%.1f%%',explode=(0,0,0,0,0.3,0,0,0),labels=label,colors=outer_colors)

plt.axis('equal')
plt.tight_layout()

plt.show()

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1

同时这里我们有用户注册后注销的信息(date_unregistration),我们也可以看注销账号的用户里的用户分类:

rfm_df[rfm_df["date_unregistration"]>0]["user_label"].value_counts()

[Python]RFM用户分类模型, 加matplotlib可视化数据--在线教育行业分析案例连载1


总结

从以上RFM信息看出,流失用户、一般维持用户占比较大,需要结合其他信息,做好精细化运营,促进学生使用产品,提升用户粘性。

同时在注销账号的用户中可以看到,注销用户较多,大约占25%,并且不少为R低F低M高的重要挽回用户,需要重点找到原因。

当我们拥有用户RFM标签,我们可以:

  1. 结合courses【课程表】去看是否有哪一门课的课程对用户吸引较大,能让用户持续使用产品
  2. 结合studentInfo【学生信息表】去重要价值用户有什么样的共性,比如重要价值用户中,多数学生为非贫困地区用户,某个年龄段的高频使用用户较多
  3. 结合studentAssessments【学生平时成绩表】去看流失用户、重要挽回用户有什么样的特征,是否他们的成绩较差而选择放弃使用,是否需要调正课程难度,评分标准