一.分析背景与目的
1.1背景与数据限制
本案例分析的对象为一款视听类app,模式类似爱奇艺、优酷等应用。核心内容为少儿动画/动漫、幼教类长视频内容,核心使用对象为幼儿、儿童和青少年。
由于数据量庞大,本次使用的数据集将抽取中国某一省份的用户进行分析。由于该省份2020年寒假开始时间为1月14日,故本数据集时间跨度为2020年1月1日-1月26日。
1.2核心业务描述
1.2.1业务总体描述
1)核心功能:案例app核心功能为用户提供少儿动画类的长视频(小部分免费、大部分收费、收费的节目均有试播功能)
2)主要商业模式:通过单点、包月、包季、包年等方式对用户进行收费
次要商业模式:通过贴片广告,实现流量变现(本次分析只针对主要商业模式讨论)
3)核心转化方式:
①产品内容页订购按钮——>付费页
②内容试播后自主跳转——>付费页
③其他途径进入(营销活动、产品首页订购通道)——>付费页
4)常见行为轨迹逻辑:
打开apk——>进入产品首页——>进入内容页——>点播
1.2.2用户侧核心指标
1)流量类:
应用访问数:pv,每日/每周apk打开次数
日访问用户数:uv,每日访问用户(去重后数量)
活跃用户数:dau,当日在应用中有超过5次行为的用户
当日订购用户数:buyer,当日有付费行为的用户数
2)转化类:
付费转化率:buyer/uv
3)比例类:
活跃用户每日行为数:活跃用户日行为总数/dau
活跃用户比例:dau/uv
4)用户使用情况类:
用户点播时长
用户点播次数
完播率
1.3 分析用户行为的目的及意义
1.3.1用户行为的定义
用户在使用app时的行为主线为“付费-点播”,故行为包含但不限于:在banner上的点击行为、进入内容页、播放节目、付费、收藏节目、点赞等
1.3.2目的和意义
1)探究用户在寒假后的使用行为变化,作为基准指标,在寒假中对业务数据进行监控,当业务出现异常时,及时归因及处理;
2)下钻分析,为运营工作调整业务方向,挖掘寒假业务增长点;
3)分析内容最新发展变化,为运营内容推荐提供参考点。
1.4主要分析思路
以自然周为单位,分别在寒假前和寒假中抽取一周的数据,计算出各指标用于对比用户行为变化:
1.4.1 基本指标
1)日访问uv,dau,buyer
2)日活跃用户人均行为数,活跃用户占比,付费用户占比(付费转化率)
3)用户周访问次数分布
1.4.2 用户时间
1)每小时访问的用户的周访问属性分布
2)访问-付费、访问-播放时间间隔分布
将寒假中的节目播放信息抽取,根据用户和时间点交叉匹配,筛选出关联度高的节目:
1.4.3 内容消费
1)节目播放频率占比
2)TOP10节目播放关联
3)节目类型播放关联
二.数据集来源及理解
2.1各表数据特征及理解
数据时间范围:2020-1-1至2020-1-26
文件类型:csv
文件数量:6
数据表名list:
1)用户访问记录
2)用户产品内打点记录
3)用户点播行为记录
4)用户订购行为记录
5)用户内容页进入记录
6)节目id与详细信息对照表
2.2 详细字段理解(根据分析需求,提取了表中的部分需要用到的字段,时间列均为timestamp格式,全部字段读取后均为object类型)
1)用户访问记录
user_id:用户ID
visit_time:用户访问时间
2)用户产品内打点记录
user_id:用户ID
kid_seat:点击位置
kid_log_time:点击时间
3)用户点播行为记录
user_id:用户ID
video_id:节目ID
time:播放时间点
4)用户订购行为记录
user_id:用户ID
time:订购时间
5)用户内容页进入记录
user_id:用户ID
visit_time:用户进入内容页的时间
6)节目id与详细信息对照表
video_id:节目ID
video_name:节目名称
category_id:节目分类ID
category_name:节目分类名称
总结:主要的分析维度为用户时间和用户行为,各表以用户ID和行为的发生时间点作关联。
三.数据预处理
数据在提取过程中已经进行过清洗,所以在此不描述清洗过程。目前数据集尚未成为能直接分析的数据集,需要进行列转换及其他处理。根据1.4的分析思路,需要将原数据表进行以下处理(以下内容与1.4相对应):
1.4.1 基本指标
用户访问记录、用户产品内打点记录、用户订购行为记录:将visit_time转化为datetime格式,过滤出被分析日期,visit_time过滤出日期日期部分
1.4.2 用户时间
用户访问记录:将visit_time转化为datetime格式,过滤出被分析日期
1.4.3 内容消费
用户点播行为记录:将visit_time转化为datetime格式,过滤出被分析日期,关联节目id与详细信息对照表,为每一条点播记录添加video_name、category_id、category_name
四.数据分析
4.1关键指标分析
4.1.1日均用户指标
4.1.2日均用户比例指表
4.1.3活跃用户周访问次数分布
4.1.4分析小结
1)寒假前日均uv 2.4w,dau 1.7W,日新增付费用户320个,日活跃用户人均行为数13.74,活跃用户占比71%,付费转化率1.33%;
2)寒假中日均uv 2.9W,dau 2W,日新增付费用户446个,日活跃用户人均行为数13.35,活跃用户占比68%,付费转化率1.56%;
3)非寒假用户访问主要集中在周末,工作日流量持平,到了周末流量较工作日上升70%,寒假用户访问在一周中分布较为均匀,寒假中的活跃用户数访问较总访问用户数稳定;
4)非寒假时期的活跃占比和活跃用户人均行为数相比寒假时期更高且更稳定,总体分布为方差较小的正态分布;
5)非寒假转化率标准差为0.32%,寒假转化率标准差为0.25%,寒假付费转化率更高且更稳定;
6)周访问次数总体符合泊松分布,非寒假期94%的用户周访问次数在5次以内,非寒假期96%的用户周访问次数在5次以内,非寒假时周访问5次以上的用户多于寒假时期;
4.2用户行为时间分析
4.2.1每小时访问的用户的周访问属性
(纵坐标为24小时,横坐标为用户的周访问次数,气泡大小为用户人数)
4.2.2访问-付费、访问-播放时间分布(上图为非寒假,下图为寒假)
4.2.3小结
1)从时间分布上看,非寒假时期的16:00-20:00是访问高峰期,符合儿童/青少年日常上线作息,寒假时期无明显高峰期;周访问次数越高的用户,访问时间越往8:00-20:00集中;
2)有付费意愿的用户中有75%会在访问产品7.5分钟以内付费,用户访问产品1分钟或6分钟时为付费高峰期;有观看意愿的用户中有75%会在访问产品1分钟内播放节目;进入寒假后用户访问-付费时间明显缩短,用户更集中在第1个付费高峰期,第2个付费高峰期提前且更为平缓;播放方面寒假更多用户时间缩短,但总体和非寒假时期持平。
4.3内容消费分析
4.3.1 节目播放频率占比(头腰部内容)
热门点播节目(字体较大的)均为优爱腾少儿点播榜头部内容,其中奥特曼品牌久远,创新迭代速率高,热度高;总体上看,头部内容日韩美占大头,腰部内容中国产动画占大头。
4.4.2 TOP10节目播放关联
分析同一个用户一天中,有多少节目能同一段时间中播放;由此可以大致分析出节目之间的关联性。
4.4.3 节目类型播放关联
提取点播行为数据集中各节目类型在一段时间中同时出现的次数,次数越大(颜色越浅),两种节目类型关联性越高。
4.4.4分析小结:
1)播放节目中,TOP10节目的播放次数占TOP150节目的总点播次数的77%;
2)关联性最强的节目组合TOP10的中只有1部节目不属于播放TOP10节目,说明播放TOP10节目的一部分节目播放次数可能是由另外一部分节目所带起;
3)由4.4.3图可看出 ①益智-搞笑 ②搞笑-机战 ③益智-机战,三者之间关联性最强,机战与大部分节目类型的关联度均不低,因此机战类节目与我们的用户群体普遍需求最为贴合。
五.总结
5.1分析总结
1)寒假对产品的uv,dau,付费转化率均有提高,但用户活跃率略有降低,寒假只是为产品增加了非刚需用户/非优质用户的量,并没有提升存量用户对产品的使用粘性,反而进入寒假后优质用户对产品的粘性略有降低,推测为进入寒假后受竞品开展运营手段及非本领域产品抢占用户更多的使用时间;
2)时间方面,寒假对用户使用时间影响较大,周高峰期由周末平摊到一周;使用高峰期由16:00-20:00平摊到全天;
3)寒假提升的增量用户大多的周访问次数集中在1-2次,后续需要继续分析这批用户的7日以上回访率;
4)有付费意愿的用户中有75%会在访问产品7.5分钟以内付费,用户访问产品1分钟(由产品首页的付费通道或内容介绍页直接付费)或6分钟(内容试播结束自动跳转付费)时为付费高峰期,寒假中用户更集中在第一个高峰期付费;有观看意愿的用户中有75%会在访问产品1分钟内容播放节目,访问-播放方面寒假中更多用户时间缩短了;
5)节目播放行为中,优爱腾少儿点播榜头部内容/日韩美动画内容占距头部位置,且头部内容的点播次数占据TOP150节目的总点播次数的77%;
6)点播次数TOP10的一部分节目的点播次数高有一部分的因素是由TOP10榜中的其他节目带起来的;节目类型方面机战类节目与用户群体的需求最为贴合,益智、搞笑、机战类节目之间的关联度最高。
5.2业务增长建议
5.2.1提升用户粘性
1)存量用户活性有下降趋势,需针对这部分用户开展营销活动或其他运营手段促活;
2)后续需密切关注寒假带来增量用户回访率,并针对这部分用户以提高周访问次数为目的开展运营手段,争取从1、2次提升到3、4次;
3)用户访问时间从集中变分散,一天多次访问的可能性比寒假前提升,建议对全用户做出能促进一天多次访问的运营手段;
4)对于1分钟内未有播放行为的用户,可弹窗主动推送点播次数高的节目给用户,促动用户点播;
5)对于关联性高的大类节目,可优先向相关用户展示;
6)可用连带能力强的播放TOP10节目以热带类型关联性强冷门节目,促进腰尾部节目的消费。
5.2.2促进用户交易
1)基于用户访问-付费时长,6分钟左右的时间点实施一定的付费优惠信息/提醒等运营措施,促进用户交易;
2)由于用户更集中于第一个付费高峰期付费,可以产品首页的付费通道或内容介绍页为选点展开营销活动,提升购买转化率。
# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
#将方法封装,输入开始和结束日期即可运行方法
def analyse(start_time,end_time):
#数据清洗以及预处理
#1.1 date-user_id-url
#读取用户访问记录,过滤出相应日期的数据,将时间戳转化为日期格式,输出为csv
data11=pd.read_csv(r'D:\用户清洗后访问记录.csv')
data11=data11.assign(time=pd.to_datetime(data11['visit_time']))
data11=data11[(data11.time>start_time)&(data11.time<end_time)]
data11=data11.assign(date=data11.time.map(lambda x:x.strftime('%Y-%m-%d')))
data11=data11[['date','user_id','url']]
data11.to_csv(r'D:\1-1_'+start_time+'_'+end_time+'.csv',index=False)
#1.2 date-user_id-kid_seat
#读取用户行为记录,过滤出相应日期的数据,将时间戳转化为日期格式,输出为csv
data12=pd.read_csv(r'D:\清洗后用户打点.csv')
data12=data12.assign(time=pd.to_datetime(data12['kid_log_time']))
data12=data12[(data12.time>start_time)&(data12.time<end_time)]
data12=data12.assign(date=data12.time.map(lambda x:x.strftime('%Y-%m-%d')))
data12=data12[['date','user_id','kid_seat']]
data12.to_csv(r'D:\1-2_'+start_time+'_'+end_time+'.csv',index=False)
#1.3 date-user_id
#读取用户订购记录,过滤出相应的数据,将时间戳转化为日期,输出为csv
data13=pd.read_csv(r'D:\用户订购表.csv')
data13=data13.assign(time=pd.to_datetime(data13['time']))
data13=data13[(data13.time>start_time)&(data13.time<end_time)]
data13=data13.assign(date=data13.time.map(lambda x:x.strftime('%Y-%m-%d')))
data13=data13[['date','user_id']]
data13.to_csv(r'D:\1-3_'+start_time+'_'+end_time+'.csv',index=False)
#3 time-user_id-url
#读取用户访问表,过滤出相应日期的数据
data3=pd.read_csv('D:\用户清洗后访问记录.csv')
data3=data3.assign(time=pd.to_datetime(data3['visit_time']))
data3=data3[(data3.time>start_time)&(data3.time<end_time)]
data3=data3[['time','user_id','url']]
data3.to_csv(r'D:\3_'+start_time+'_'+end_time+'.csv',index=False)
#数据分析
#1 用户基本指标
#1.1.分析寒假前一周每日的UV、DAU、订购用户数
#1.1.1 UV
df111=pd.read_csv(r'D:\1-1_'+start_time+'_'+end_time+'.csv')
#为1.3做准备
df13=df111
df111=df111.groupby('date')['user_id'].nunique()
df111.name='uv'
df111=df111.reset_index()
#1.1.2 DAU
df112=pd.read_csv(r'D:\1-2_'+start_time+'_'+end_time+'.csv')
df112=df112.groupby(['date','user_id']).size()
df112=df112[df112>2]
#为1.2.1做准备
df121=df112
df112=df112.groupby(level=0).size()
df112.name='dau'
df112=df112.reset_index()
#1.1.3 订购用户数
df113=pd.read_csv(r'D:\1-3_'+start_time+'_'+end_time+'.csv')
df113=df113.groupby('date').size()
df113.name='buyer'
df113=df113.reset_index()
#1.1 组合3张数量表
df11=pd.merge(pd.merge(df111,df112,on='date'),df113,on='date')
df11.to_excel('D:\\'+start_time+'_'+end_time+'_11.xlsx')
#1.2.算出活跃用户人均行为数、活跃用户占比、订购用户占比
#1.2.1 活跃用户人均行为数
#从1.1.2处获取活跃用户及当日行为数
df121=df121.groupby(level=0).sum()
df121.name='pv'
df12=pd.merge(df11,df121,left_on='date',right_index=True)
df12=df12.assign(pv=df12.pv/df12.dau)
#1.2.2 活跃用户占比
df12=df12.assign(active_mix=df12.dau/df12.uv)
#1.2.3 订购用户占比
df12=df12.assign(buy_mix=df12.buyer/df12.uv)
#1.2 去掉无用列
df12=df12[['date','pv','active_mix','buy_mix']]
df12.to_excel('D:\\'+start_time+'_'+end_time+'_12.xlsx')
#1.3 用户周访问次数分布
#数据从1.1.1那里获取
df13=df13.groupby('user_id').size().value_counts()
df13.to_excel('D:\\'+start_time+'_'+end_time+'_13.xlsx')
#3 用户时间
#3.1.每小时访问的用户属性(周访问次数属性)
#计算出每条访问记录的小时
df31=pd.read_csv(r'D:\3_'+start_time+'_'+end_time+'.csv')
df31=df31.assign(time=pd.to_datetime(df31.time))
df31=df31.assign(hour=df31.time.dt.hour)
#计算出每个用户的周次数
week_freq=pd.read_csv(r'D:\1-1_'+start_time+'_'+end_time+'.csv').groupby('user_id').size()
week_freq.name='freq'
#为每条访问记录中的用户添加周次数
df31=pd.merge(df31,week_freq,left_on='user_id',right_index=True,how='left')
#计算出小时-周访问次数-用户数
df31=df31.groupby(['hour','freq']).size()
df31.name='count'
df31=df31.reset_index()
df31=df31[df31['freq']<15]
df31.sort_values(by=['hour','freq'],inplace=True)
df31.to_excel('D:\\'+start_time+'_'+end_time+'_31.xlsx')
#3.2.计算用户访问-订购之间的时间间隔
#提取每个订购记录的天数和每个访问记录的天数
buyday_32=pd.read_csv(r'D:\用户订购表.csv')
buyday_32=buyday_32.assign(user_id=buyday_32['user_id'].map(lambda x:str(int(x))))
buyday_32=buyday_32.assign(time=pd.to_datetime(buyday_32['time']))
buyday_32=buyday_32[(buyday_32.time>start_time)&(buyday_32.time<end_time)]
buyday_32=buyday_32.assign(day=buyday_32.time.dt.day)
buyday_32.rename(columns={'time':'buy_time'},inplace=True)
visitday_32=pd.read_csv(r'D:\3_'+start_time+'_'+end_time+'.csv')
visitday_32=visitday_32.assign(time=pd.to_datetime(visitday_32.time))
visitday_32=visitday_32.assign(day=visitday_32.time.dt.day)
#将有访问记录的用户的每天第1条访问记录抽出
visitday_32=visitday_32.sort_values(by=['user_id','time'])
visitday_32.drop_duplicates(['user_id','day'],keep='first',inplace=True)
visitday_32.rename(columns={'time':'visit_time'},inplace=True)
#连接订购表和访问表
df32=pd.merge(buyday_32,visitday_32,on=['user_id','day'])
#计算访问-订购时间差
df32=df32.assign(delta=(df32.buy_time-df32.visit_time).map(lambda x:x.seconds/60))
df32=df32[df32['delta']>0]
#3.3.计算用户访问-观看之间的时间间隔
#提取每个有点播行为的用户每天的第1次进入播控页的时间
playday_33=pd.read_csv(r'D:\清洗后用户打点.csv')
playday_33=playday_33.assign(time=pd.to_datetime(playday_33['kid_log_time']))
playday_33=playday_33[(playday_33.time>start_time)&(playday_33.time<end_time)]
playday_33=playday_33[playday_33['kid_seat']=='进入播控页']
playday_33=playday_33.assign(day=playday_33.time.dt.day)
playday_33.rename(columns={'time':'play_time'},inplace=True)
#将有播放记录的用户的每天第1条播放记录抽出
playday_33=playday_33.sort_values(by=['user_id','play_time'])
playday_33.drop_duplicates(['user_id','day'],keep='first',inplace=True)
#连接播放表和访问表
df33=pd.merge(playday_33,visitday_32,on=['user_id','day'])
#计算访问-播放时间差
df33=df33.assign(delta=(df33.play_time-df33.visit_time).map(lambda x:x.seconds/60))
df33=df33[df33['delta']>0]
# 3.将3.2和3.3拼接成1个数据集
df32=df32.assign(item='buy')
df33=df33.assign(item='play')
df3=pd.concat([df32[['item','delta']],df33[['item','delta']]])
df3=df3[df3['delta']<15]
sns.violinplot(x='item',y='delta',data=df3)
plt.savefig('C:\\Users\\liminghao\\Desktop\\'+start_time+'_'+end_time+'_3.png',dpi=400)
analyse('2020-1-6','2020-1-13')
analyse('2020-1-20','2020-1-27')
#4 内容消费
#数据清洗
#4.1 time-user_id-video_id
#读取用户点播表,过滤出0,1,5列,过滤出1-6到1-12的数据,关联添加video名称
data41=pd.read_csv(r'D:\点播表.csv',header=None)
data41=data41[[0,1,5]]
data41=data41.rename(columns={0:'user_id',1:'video_id',5:'time'})
data41=data41.assign(time=pd.to_datetime(data41.time))
data41=data41[(data41.time>'2020-1-14')&(data41.time<'2020-1-25')]
video_name=pd.read_excel(r'C:\节目id.xlsx','product_table')
video_name=video_name[['剧头标识','标题']]
data41=pd.merge(data41,video_name,left_on='video_id',right_on='剧头标识',how='left')
data41=data41[['user_id','video_id','标题','time']]
data41=data41.rename(columns={'标题':'title'})
data41.to_csv(r'D:\4-1.csv',index=False)
#4.2 date-user_id-video_id-title
#读取用户点播表,将时间戳转化成日期,去重
data42=pd.read_csv(r'D:\4-1.csv')
data42=data42.assign(time=pd.to_datetime(data42.time))
data42=data42.assign(date=data42.time.map(lambda x:x.strftime('%Y-%m-%d')))
data42=data42[['date','user_id','video_id','title']]
data42.drop_duplicates(inplace=True)
data42.to_csv(r'D:\4-2.csv',index=False)
#4.3 date-user_id-category_id-c_title
#读取用户点播小类表,读取剧集信息表,2表关联,为点播小类表添加大类id和名称
data42=pd.read_csv(r'D:\4-2.csv')
category=pd.read_excel(r'C:\节目大类.xlsx','短带长信息')
category=category[['片名','题材','category_id']]
data43=pd.merge(data42,category,left_on='title',right_on='片名',how='inner')
data43=data43.rename(columns={'题材':'c_title'})[['date','user_id','category_id','c_title']]
data43.to_csv(r'D:\4-3.csv',index=False)
数据分析
#4.1.节目播放频率
df41=pd.read_csv(r'D:\4-1.csv')
df41=df41['title'].value_counts()
#4.2.节目播放相关性
df42=pd.read_csv(r'D:\4-2.csv')
df42=pd.merge(df42,df42,on=['user_id','date'],how='inner')
df42=df42[df42.video_id_x<df42.video_id_y]
df42=df42.groupby(['title_x','title_y']).size().sort_values(ascending=False)
df42.name='count'
df42=df42.reset_index()
df42.to_excel('D:\\'+'2020-1-14'+'_'+'2020-1-25'+'_42.xlsx')
#4.3.大类播放相关性
df43=pd.read_csv(r'D:\4-3.csv')
df43=pd.merge(df43,df43,on=['user_id','date'],how='inner')
df43=df43[df43.category_id_x<df43.category_id_y]
df43=df43.groupby(['c_title_x','c_title_y']).size().sort_values(ascending=False)
df43.name='count'
df43=df43.reset_index()