疫情以来,越来越多的家长和学生开始主动或者被动的接受在线这种教学方式。在线教育行业迎来了一波流量增长,积累的数据也越来越多。与此同时,越来越的创业者开始进入这个行业,行业内的竞争也越来越激烈。能否高效的利用这些数据,成为一个公司能否装上涡轮发动机迎风起飞的关键因素。
在线教育的成交转化流程一般如下:用户报名 -> 上体验课 -> 付费购课 。体验课一般包含 3 - 5 节,每天 1 节。每节课程有若干个模块,模块中会包含各种形式的互动问题。问题是封闭式的,都会有正确答案,用户的每次答题结果会保存下来。
数据维度划分
用户维度
用户课程维度
时序维度
设计矩形数据的字段
为了方便统计,采用最常见的矩形数据来作为分析的基础。具体来说,每个用户在某个体验课下会有一行记录,每一列就是一个特征数据。针对体验课可能有 N 天,N 并不相同的情况,在需要的字段上通过添加数字编号来解决。例如:d1s ( day one state )表示第 1 天的课程完成情况,当完整上完时字段值为 2,未开始时值为 0 ,已开始未完成时是 1。依次类推。数据表共计 40 列。
采集数据
通常数据采集有推流和拉流两种方式,各自的优缺点这里不再赘叙。感兴趣的同学可以画一个损益矩阵选择自己适用的方式。这里我们采用了拉流的方式实现数据采集,每次在业务流量低峰时跑脚本。脚本会先拉取目标用户群,然后循环抽取每个用户的具体数据。在抽取具体数据时并没有大量使用数据库表 join ,也没有过多的使用 where 条件,把计算和逻辑放到本地进行离线计算。得益于 Golang 强大的 GMP 调度器,纯 CPU 计算并不会成为瓶颈。
数据清洗
df.dropna(subset=[column_a, column_b])
clean_df = df.loc[df[column_a] > column_a_bound]
其中 column_a_bound 可以通过 4 分位数、方差、均值等得出。选择合适的值即可;
可视化探索
通常性别是一个单维度的数据,适合用饼图来表示:
对应的代码:
plt.figure(figsize=(5,5)) vc = df[‘gender'].value_counts() y = [vc.values[0], vc.values[1]] patches,l_text,p_text = plt.pie(y,labels=['女', '男'], explode=(0.1,0), colors=['pink','yellowgreen'], labeldistance = 1.1, autopct = '%3.1f%%', shadow = True, startangle = 90, pctdistance = 0.6) for t in l_text: t.set_size=(30) for t in p_text: t.set_size=(20) # 设置x,y轴刻度一致,这样饼图才能是圆的 plt.axis('equal') plt.legend() plt.show()
类似可以探索一下不同性别在样本用户中的比例、在成交用户中的比例。体现多个比例数据适合矩状图。
代码如下:
df_gender_deal = df.loc[df['deal'] == 1] gender_rate = df['gender'].value_counts() gender_deal_rate = df_gender_deal['gender'].value_counts() gender_dist = pd.DataFrame({'all': gender_rate, 'deal': gender_deal_rate}) gender_dist['all_rate'] = gender_dist['all'].apply(lambda x: x / gender_dist.shape[0] * 100) gender_dist['deal_rate'] = gender_dist['deal'].apply(lambda x: x / df_gender_deal.shape[0] * 100) x = gender_dist.index bar_width = 0.35 plt.figure(figsize=(10,5)) plt.bar(x, gender_dist['all_rate'], bar_width, align="center", color="c", label="样本占比", alpha=0.5) plt.bar(x+bar_width, gender_dist['deal_rate'], bar_width, color="b", align="center", label="成交占比", alpha=0.5) plt.xlabel("Gender") plt.ylabel("Percentage") plt.xticks(x+bar_width/2, x) plt.legend() plt.show()
从对比中可以提出假设,男性用户在成交中的比例上升了。上升的比例并不明显,所以我们可以认为性别对成交没有明显的影响。
单一维度提供的信息有限,有些数据需要跟时间维度一起观察变化趋势。例如:为了衡量第二天的课程有多少人能够坚持到课,我们需要对比第一天的到课用户数占比和第二天的到课用户占比。
其中完课比是到课用户中完成所有课程内容的用户占比,这个数字可以衡量有课程内容和教学环节是否存在问题。上图中可以得出结论:第一天课程的完课比偏低,需要排查原因。
折线图很适合做这种趋势变化的体现,多条折线叠加到同一个图上能够更直观的表现数据。
def course_brief(df): active_rate = [] finish_rate = [] finish_at_active = [] for i in range(4): vc = df['d{}'.format(i+1)].value_counts() n = sum(vc.values) active_rate.append((vc[1]+vc[2])/n * 100) finish_rate.append((vc[2])/n * 100) finish_at_active.append(vc[2]/(vc[1]+vc[2]) * 100) plt.figure(figsize=(10, 5)) dayx = ['Day_1', 'Day_2', 'Day_3', 'Day_4'] plt.plot(dayx, active_rate, color="r", label="到课率", linestyle='solid', alpha=0.5) plt.plot(dayx, finish_rate, color="b", label="完课率", alpha=0.5, linestyle='dotted') plt.plot(dayx, finish_at_active, color="g", label="完课比", alpha=0.5, linestyle='dashed') plt.legend() course_brief(df)
还有一些信息必须用多维数据表现,可视化效果并不好,我们需要构造特征进行降维。例如:我们想验证封闭式问题的答对率的变化趋势是否和成交有关联,也就是说我们想知道课堂表现越来越好的人更愿意成交还是越来越差的人更愿意成交。
基于原始数据抽象出“首次答对率” 的特征,统计每节课的所有首次答题记录中答对的题目数,该数目在回答过的题目中的比例即视为 “首次答对率”。计算出每节课的首次答对率,得到下列数据:
为了衡量这个数字的变化趋势,我们可以构造两列新的数据,startFR 和 endFR。其中 startFR = (d1fr + d2fr + d3fr) / 3,endFR = (d3fr + d4fr + d5fr) / 3。通过 pandas 可以很容易计算出两列新特征:
df['startFR'] = (df['d1fr'] + df['d2fr'] + df['d3fr']) / 3 df['endFR'] = (df['d3fr'] + df['d4fr'] + df['d5fr']) / 3
成功的将 5 维数据降到 2 维,但是还不够,还可以继续。
df['upRate'] = (df['startFR'] - df['endFR']) * 100 / df['startFR']
这里构造了一个新特征 upRate 来表示 “首次答对率” 的变化趋势,为正时表示 “首次答对率”处于上升趋势,为负时表示下降。数值表示变化的斜率,数字越大表示变化越明显。结合其他数据列,可以画出下列图来探索数据:
上图中灰色的 x 点表示未成交用户,蓝色圆点表示成交用户。X 轴为 upRate,Y 轴为第一节课的首次答对率。虚线为中位数所在的位置。散点图适合探索数据分布,观察数据的关联性。
df_deal = df.loc[df['deal']==1] df_none = df.loc[df['deal']==0] plt.scatter(df_deal['upRate'], df_deal['av1'], marker = 'o',color = 'blue', label = 'Deal') plt.scatter(df_none['upRate'], df_none['av1'], marker = 'x',color = 'gray', label = 'Deal') plt.axhline(y=df['av1'].median(),ls=":",c="black")#添加水平直线 plt.axvline(x=df['upRate'].median(),ls=":",c="green")#添加垂直直线 plt.show()
可以从一下几个方向深入的挖掘和使用数据,让数据的价值发挥出来。