数据分析项目四:KLOOK旅游数据分析

导入数据

        观察数据,是csv格式

        导入数据

# 导入数据
df = pd.read_csv('kelu.csv')
df.head()

        分析数据:发现time列数据类型需要修改为datetime

df.info()
# 可以看出共有8k+条数据,time列数据类型需要转换为datetime,没有空数据

df.describe()
# 可以看出平均评分在92,大多数的用户给出了100分
# 数据是16年到19年之间的
# 门票价格都是110
# frequency代表消费次数,全为1,后续计数用

        根据分析结果修改time列数据类型

# 分析数据
# 先转换time列数据类型
df['time'] = pd.to_datetime(df['time'],format='%Y/%m/%d')
df.info()

每日/每月/每个用户数据分析

a1.每日销量分析

# 1.每日销量分析
# time列本来就是精确到天的,所以直接按time分组就行,然后因为也没有空数据,所以随便取一列计数就行
df.groupby('time')['frequency'].count().plot(figsize=(12,4))
# 整体看来每日销量呈现上升趋势,但是18年5月份前后(2、3、4)出现一次较大波动,销量急剧下滑,猜测:台风、疫情,运营推广不利
# 16年9月-17年1月,销量非常低,平均每天2-3张,猜测:101观景台门票刚上线发售,观景台刚对游客开放

a2.每月销量分析

# 2.每月销量分析
# time列精确到天数,而我们只需要月份就行,跟上一个项目一样,新增一列精度为月份
df['month'] = df['time'].values.astype('datetime64[M]') # 保留精度为月份的日期
df.groupby('month')['rating'].count().plot(figsize=(12,4))
# 月份销量整体呈现上升趋势,但是18年2、3、4月销量下滑,跟每天销量有关

a3.每个用户的购买量和消费金额

        先学习新函数merge,比较好理解,将两个表按照相同字段连接

# 新函数merge法,相当于SQL中的join
df1 = pd.DataFrame({
    'name':['zhangsan','lisi'],
    'group':['A','B']
})

df2 = pd.DataFrame({
    'name':['lisi','wangwu'],
    'group':['B','C'],
    'score':[88,90]
})
pd.merge(left=df1,right=df2,on='name',how='inner',suffixes=['_1','_2'])
# left:左表名称  right:右表名称  on:关联字段  how:inner(默认,交集)/outer(并集)/left(只保留左侧)/right(只保留右侧)
# suffixes:如果两个表中有相同列,用suffixes给的值进行区分(默认值x、y)

        这里可以不用这么麻烦,只是为了讲解这个函数

        画散点图的时候,可以指定x、y

# 按照游客分组,统计每个游客购买次数
group_count_author = df.groupby('author')['frequency'].count().reset_index() # 这里的重置索引是为了加上frequency的列名
# 按照游客分组,统计每个游客的消费金额
group_sum_amount = df.groupby('author')['amount'].sum().reset_index()
user_purchase_retention = pd.merge(left=group_count_author,
                                  right=group_sum_amount,
                                  on='author',
                                  how='inner')
user_purchase_retention.plot.scatter(x='frequency',y='amount')
plt.title('用户购买次数和消费金额关系')
plt.xlabel('购物次数')
plt.ylabel('消费金额')
# 结论:斜率是门票价格110,两者呈线性关系 

用户购买数量和次数行为分析

b1.用户购买门票数量分析

        这里我做的跟视频有点出入,因为我觉得frequency只是次数而已,不代表数量,展示的是我自己的做法

# 1.用户购买门票数量分析
# 这里视频教学是直接对frequency进行计数,但是我觉得frequency全为1表示消费次数,但是一次消费可能不只一张门票
df['quantity'] = df['amount']/110
df['totle_quantity'] = df['quantity']*df['frequency']
df.groupby('author')['totle_quantity'].count().plot(kind='hist',bins=50)
plt.xlim(1,17)
plt.xlabel('购买数量')
plt.ylabel('人数')
plt.title('用户购买门票数量直方图')
# 绝大多数用户买过1张门票,用户在7000人左右
# 少数人买过2-6张门票,推测:可能是台北周边用户

b2.用户购买门票2次及以上情况分析(由于b1可视化图中部分数据趋势不明显)

        感觉b1应该是分析的门票购买次数,这样上下才能关联起来,问题不是很大,这里是在上图中看不出门票购买2次及以上,单独提出来分析

        这里为了画直方图需要再次分个组

# 2.用户购买门票2次以上情况分析
# 这里是购买门票两次以上,跟数量无关了,用回frequency
df_frequency2 = df.groupby('author').count().reset_index()
# 求df_frequency2时已经分过组了,但是为了画图这里要再分一次,对df_frequency2进行sum求和
df_frequency2[df_frequency2['frequency']>=2].groupby('author')['frequency'].sum().plot(kind='hist',bins=50)
plt.xlabel('购买数量')
plt.ylabel('人数')
plt.title('购买门票在2次及以上的用户数量')
# 消费两次的用户在整体上占比较大,大于2次的用户是小部分,用户购买次数最多8次

b3.查看购买两次以及上的具体人数

        这时就要按照次数分组,然后统计人数了

# 3.查看购买两次以及上的具体人数
# 这里要按照frequency分组了,看每个frequency值对应几个人
df_frequency2[df_frequency2['frequency']>=2].reset_index().groupby('frequency')['author'].count()
# 大多数据倾向2-5次

b4.购买次数在1-5次之间的用户占比分析

        先同样的求出每个用户消费频率,接下来两个逻辑判断

        注意这里画饼图,输入的数据需要list格式,我们可以使用list()强制转化,把series对象转换为list

# 4.购买次数在1-5次之间的用户占比分析
# 对author进行分组,取出消费频率,进行逻辑判断(1-5),数据可视化(pie)
df_frequency_gte1 = df.groupby('author')['frequency'].count().reset_index()
# 购物次数>=1 (这里可以不写,因为过滤结果跟上面一样)
df_frequency_gte1 = df_frequency_gte1[df_frequency_gte1['frequency']>=1]
# 购票次数<=5
value = df_frequency_gte1[df_frequency_gte1['frequency']<=5]
# 饼图也是统计每个购买次数有多少人,value是series对象,而饼图的数据需要list,这里进行强制转换
value = list(value.groupby('frequency')['author'].count())
# 绘制饼图
labels = ['1次','2次','3次','4次','5次']
plt.pie(value,labels=labels,autopct='%1.1f%%')
plt.title('购买次数在1-5之间用户占比')
plt.legend()

b5.购买次数在2-5次之间的用户占比分析

        同上操作

# 5.购买次数在2-5次之间的用户占比分析
# 对author进行分组,取出消费频率,进行逻辑判断(2-5),数据可视化(pie)
df_frequency_gte2 = df.groupby('author')['frequency'].count().reset_index()
# 购物次数>=2
df_frequency_gte2 = df_frequency_gte2[df_frequency_gte1['frequency']>=2]
# 购票次数<=5
value = df_frequency_gte2[df_frequency_gte2['frequency']<=5]
# 饼图也是统计每个购买次数有多少人,value是series对象,而饼图的数据需要list,这里进行强制转换
value = list(value.groupby('frequency')['author'].count())
# 绘制饼图
labels = ['2次','3次','4次','5次']
plt.pie(value,labels=labels,autopct='%1.1f%%')
plt.title('购买次数在2-5之间用户占比')
plt.legend()
# 2-3次用户很重要
# 消费次数4-5次人数较少,需要针对性对2-3次用户引导
# 消费在1次的用户可以尝试转化到消费2-3次

复购、回购分析/用户分层分析

c1.复购率分析

        分析复购率、回购率,用数据透视表是很方便的

        这里的数据透视表将月份列的值展开变成每一列,然后每月统计消费次数

# 1.复购率分析
# 复习一下复购率:一个时间窗口内(一个月)复购人数/总消费人数
# 这里用数据透视表
pivot_count = df.pivot_table(index='author',
                             columns='month',
                             values='frequency',
                             aggfunc='count'
).fillna(0)
pivot_count.head()

        复购率计算的是一个时间周期内,所以直接用applymap对每一个数据进行判断,看是否复购,同时没有消费的变成NAN,便于后面操作

# 复购为1,消费一次为0,未消费为nan
pivot_count = pivot_count.applymap(lambda x:1 if x>1 else 0 if x==1 else np.NaN)
pivot_count.head()

        复购率计算

(pivot_count.sum()/pivot_count.count()).plot()
plt.xlabel('日期')
plt.ylabel('百分比')
plt.title('16-19年每月用户复购率')
# 16年9月复购率最高达到7.5%,后开始下降,趋于平稳在1.2%

c2.复购用户人数

# 复购用户人数
pivot_count.sum().plot()
plt.xlabel('日期')
plt.ylabel('百分比')
plt.title('16-19年每月复购人数')
# 整体看来复购人数呈现上升趋势
# 但是18年2、3、4、10和19年2月份人数下降较为明显,出现异常,需要和业务部门具体分析

c3.回购率分析

        这个数据表跟计算复购率的一样,再建一个表清楚一点

# 回购率分析
# 复习回购率:在一个时间窗口内消费过的同时在下个月也消费过的人数/总消费人数
pivot_purchase = df.pivot_table(index='author',
                             columns='month',
                             values='frequency',
                             aggfunc='count'
).fillna(0)
pivot_purchase.head()

# 不需要知道消费几次,只需要知道有没有消费即可,所以只要消费都记为1
pivot_purchase = pivot_purchase.applymap(lambda x:1 if x>=1 else 0)
# 判断用户回购状态:下个月回购了为1,下个月未回购为0,本月未消费为NaN
def purchase_return(data):
    status = [] # 储存回购用户状态
    for i in range(30):
        if data[i] == 1: # 本月消费
            if data[i+1] == 1: # 下个月消费了
                status.append(1)
            else: # 下个月没消费
                status.append(0)
        else: # 本月没消费
            status.append(np.NaN)
    status.append(np.NaN)  # 我自己在写的时候忘了这一列,最后一列没遍历上需要自己补充数据
    return pd.Series(status,pivot_purchase.columns)
    
pivot_purchase_return = pivot_purchase.apply(purchase_return,axis = 1)

        跟上一个项目求回购率一样,直接复制粘贴了

# 求回购率
(pivot_purchase_return.sum()/pivot_purchase_return.count()).plot()
plt.xlabel('日期')
plt.ylabel('回购率')
plt.title('16-19年每月回购率')
# 回购率最高在18年6月,达到4%
# 整体来看,回购率呈现微弱上升趋势
# 在17年6月、18年1月8月、19年1月出现较大下滑

c4.回购人数分析

# 回购人数分析
pivot_purchase_return.sum().plot()
plt.xlabel('日期')
plt.ylabel('回购人数')
plt.title('16-19年每月回购人数')
# 整体呈现上升趋势,回购人数最多在18年11月
# 有几次下降明显

c5.每个月分层用户占比情况

        这里我们借用求回购率建立的数据透视表pivot_purchase

        从上个项目复制粘贴,理解了就能直接用了

# 每个月分层用户占比情况
# 活跃用户/不活跃用户/新用户/回流用户
# 判断是否是新用户、活跃用户、不活跃用户、回流用户
def active_status(data): # data是整行数据
    status = [] # 负责储存31个月的状态:unreg/new/active/unactive/return
    for i in range(31):
        if data[i] == 0:# 本月没有消费
            if len(status) == 0: # 前面没有任何记录
                status.append('unreg')
            else: # 判断上一个状态
                if status[i-1] == 'unreg': # 一直未消费
                    status.append('unreg')
                else: # new/active/unactive/return
                    status.append('unactive') # 这四种情况不论哪种这个月都是不活跃用户
        else: # 本月有消费
            if len(status) == 0: # 前面没有任何记录
                status.append('new')
            else: # 判断上一个状态
                if status[i-1] == 'unreg': 
                    status.append('new') # 第一次消费
                elif status[i-1] == 'unactive':
                    status.append('return')
                else: # new/active/return
                    status.append('active')
    return pd.Series(status,pivot_purchase.columns) # 值status,填入df_purchase的列名中,这里传入的是一行数据也就是series对象,返回也是一样的
                
pivot_purchase_status = pivot_purchase.apply(active_status,axis=1)
pivot_purchase_status.head()

        先将unreg换成NAN,不影响后续计算占比,然后对每一列用value_count计数,最后转置画面积图

# 对每一列进行值的统计,输入进去的每一列是一个series对象
pivot_purchase_count = pivot_purchase_status.replace('unreg',np.NaN).apply(pd.value_counts)
# 转置画面积图
pivot_purchase_count.T.plot.area()
# 红色占主体
# 橙色有上升趋势,某个月又下降
#绿色蓝色很稳定,占比都不多

c6.每月不同用户占比

        我们用到pivot_purchase_count

        这里直接用每一列中的各个数据除以这一列求和,然后转置、可视化

pivot_purchase_count.apply(lambda x:x/x.sum()).T.plot()
# 回流用户和活跃用户一直稳定,占比很低
# 17年三月前网站主体是新用户,后主体为不活跃用户,都趋于稳定

        在上面这幅图中,只能看出新用户和不活跃用户的趋势,需要把回流用户和活跃用户单独提出来看

c7.每月活跃用户占比

        我们看到pivot_purchase_count表中希望取出active那一行的数据,所以我们将表转置一下就能取出active列进行可视化

# 每月活跃用户占比
# 要取出活跃用户需要转置表,然后取出active列
return_rate = pivot_purchase_count.apply(lambda x:x/x.sum()).T
return_rate['active'].plot(figsize=(12,4))
plt.xlabel('时间')
plt.ylabel('百分比')
plt.title('每月活跃用户占比分析')
# 17年1月份活跃用户占比较高在0.5%,但是1-2月份急剧下降,猜测:春节的影响或者温度
# 结合历年1-2月份销量来看,都会出现一定比例下降,再次印证猜想:春节
# 18年2、5月出现异常,门票销量下降,猜测:雨水或台风影响

c8.每月回流用户占比

# 每月回流用户占比
# 要取出活跃用户需要转置表,然后取出return列
return_rate = pivot_purchase_count.apply(lambda x:x/x.sum()).T
return_rate['return'].plot(figsize=(12,4))
plt.xlabel('时间')
plt.ylabel('百分比')
plt.title('每月回流用户占比分析')
# 整体来看,回流用户呈现上升趋势,但是波动较大
# 在17年1、6月,18年4月,19年2月,回流用户比例都出现较大幅度下降,异常信号
# 不论是回流用户还是活跃用户,在以上几个月份中都呈现下降趋势

        这里求了一个平均数,用的是np中的mean函数,处理大量数据时效率更高

# 这里可以用return_rate['return'].mean(),但是建议用np,因为数据量大的时候np处理更快
np.mean(return_rate['return'])
# 在17年9月份以后只有两个异常点在平均值以下
# 在17年9月份以前,所有数据都显示出回流用户比例低于平均值,猜测:景点开放不久,很多游客未发现该景点/未上线该平台

生命周期/留存率/购买周期分析

d1.用户的生命周期

        生命周期就是用户最近一次购买时间-最早一次购买时间,这里可以用agg函数

# 1.用户的生命周期
# 计算方式:每个用户最近一次购买时间与最早一次购买时间的差值,转换成天数,即为生命周期
time_max = df.groupby('author')['time'].max()
time_min = df.groupby('author')['time'].min()
life_time = (time_max - time_min).reset_index()
print(life_time)
# 由总数据的8757条数据和生命周期的7721条数据可知,存在一名用户消费多次的情况
life_time.describe()
# 从描述性分析看平均生命周期是23天,但是25%、50%、75%分位数都为0说明剩余的25%用户生命周期远远大于23天,但是绝大多数用户都是0天
life_time['time'].max()
# 最大生命周期是864天

d2.用户生命周期直方图

# 2.用户生命周期直方图
#这里直接取time列绘图会报错,要转化类型
life_time['lifetime'] = life_time['time']/np.timedelta64(1,'D')
life_time['lifetime'].plot.hist(bins=100)
plt.xlabel('生命周期天数')
plt.ylabel('用户数量')
plt.title('用户生命周期直方图')
life_time[life_time['lifetime'] == 0]
# 生命周期为0的用户有7130位,剩余的为优质忠诚客户

d3.生命周期大于0的用户直方图

        由上图可以看出生命周期为0的用户数量太多了,所以我们去除这批用户,看剩下的趋势

# 3.生命周期大于0的用户直方图
life_time[life_time['lifetime'] > 0]['lifetime'].plot.hist(bins=100)
plt.xlabel('生命周期天数')
plt.ylabel('用户数量')
plt.title('用户生命周期大于0的直方图')
life_time[life_time['lifetime'] > 0]['lifetime'].mean()
# 去掉0生命周期的用户,发现用户平均生命周期为300天,生命周期在100天的用户达到了17人
# 生命周期在100-350天来看,用户数量呈现缓慢下降趋势
# 350-800天左右来看,用户数量下降速度明显,存在一定用户流失,忠诚用户越来越少

d4.用户留存天数及留存率

        这里学习新函数cut(),可以自动判断数据属于哪个区间

# 学习新函数pd.cut()
np.random.seed(666) #保证每次生成的随机数是一样的
score_list = np.random.randint(25,100,size=3)
bins = [1,59,70,80,100] # 指定多个区间
score_cut = pd.cut(score_list,bins)
score_cut
# 可以自动对数组里的数分组

        留存率计算:先要判断用户每条记录属于哪个天数区间,然后根据区间求在此区间内有多少人,由于time_min可以直接用,我们重置一下索引,这样可以取出time列

# 留存率:1-90天有多少留存用户,求出用户的留存天数,比如留存天数==89,属于1-90天内的留存用户
#留存天数计算方式:用户每一条消费数据的消费时间减去其最早消费时间
time_min.reset_index() # 这里用户最早消费时间我们在计算生命周期时用过了,但是这里要重置索引

        这里看一下df表,里面存储最原始的每个用户消费时间记录

        

        这里用merge函数将用户每次消费时间与其最早消费时间连接起来,由于两张表中time列重名了,用suffixes重命名

# 使用merge函数将df(里面有每个用户每次消费时间)和time_min.reset_index()(最小消费时间)结合到一张表上
user_purchase_retention = pd.merge(left=df,right=time_min.reset_index(),on='author',suffixes=('','_min'))
user_purchase_retention.head()

        新增一列time_diff用来计算留存天数

# 计算留存天数
user_purchase_retention['time_diff'] = user_purchase_retention['time'] - user_purchase_retention['time_min']
user_purchase_retention.head()

        同样要进行可视化之前得转换数据类型

# 转换数据类型
user_purchase_retention['time_diff'] = user_purchase_retention['time_diff']/np.timedelta64(1,'D')
user_purchase_retention.head()

        由之前的分析可知生命周期最大为800+,所以我们可以生成0-900,分割为90的时间跨度列表

# 生成时间跨度(3个月,即90天),判断属于哪个区间
bin = [ i*90 for i in range(10)]
user_purchase_retention['time_diff_bin'] = pd.cut(user_purchase_retention['time_diff'],bin)
user_purchase_retention.head()

        这里要按照用户和时间跨度分组,然后再对frequency求和,判断每个区间有多少人数

# 统计每个用户,在不同时间段内的消费频率和值(便于稍后判断该用户在某个区间是不是留存用户)
pivot_retention = user_purchase_retention.groupby(['author','time_diff_bin'])['frequency'].sum().unstack()
pivot_retention

        对每个数据判断是否为留存用户

# 判断是否为留存用户(>0为留存用户,用1表示,=0为非留存用户,用0表示)
pivot_retention.applymap(lambda x: 1 if x>0 else 0)
pivot_retention

# 计算留存率
(pivot_retention.sum()/pivot_retention.count()).plot.bar()
plt.xlabel('时间跨度')
plt.ylabel('留存率')
plt.title('每个跨度留存率')

由上图得出结论:

# 如图,每个周期是3个月,第一个周期留存率在2.2%,前三个周期递减速度在0.3%左右
# 在第四五个周期时趋于平稳,稳定在1.5%左右
# 从第五个周期开始,留存率明显下降,下降到几乎为0,在第四五周期的时候(1年),需要采取方法留住用户进行再次消费
# 如果在时间跨度为1年时不召回用户,可能面临大量用户流失的风险

d5.用户平均购买周期直方图

        这里视频没有讲解,但是按照上一个项目可以求购买周期,用的是shift函数

# 购买周期有点忘了,参考上一个项目,使用的是shift函数
order_diff = df.groupby('author').apply(lambda x:x['time']-x['time'].shift())
order_diff.head()

order_diff = order_diff/np.timedelta64(1,'D')
order_diff

order_diff.hist(bins=20)

        感觉画出来有点问题,但是思路应该是没有错的

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值