美文网首页
数据分析:CD网站用户消费行为分析

数据分析:CD网站用户消费行为分析

作者: 楚岸 | 来源:发表于2019-08-13 17:12 被阅读0次

    流程

    1.销量和金额的月份趋势:折线图
    2.每笔订单以及按用户分组:散点图
    3.用户消费水平:直方图
    4.用户首月和最后一次消费的月份统计
    5.复购率:数据透视表
    6.回购率:函数定义
    7.用户分层:面积图;各层用户占比:折线图
    8.用户消费贡献:折线图
    9.用户生命周期:直方图
    10.留存率:函数定义、分组、柱状图
    11.用户平均购买周期:直方图

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt 
    from datetime import datetime
    %matplotlib inline
    #是用于在jupyter下将那些用matplotlib绘制的图显示在页面里而不是弹出一个窗口
    plt.style.use('ggplot')
    
    columns = ['id','dt','products','amount']
    df = pd.read_csv('CDNOW_master.txt',names = columns,sep = '\s+')#sep表示匹配分隔符,\s+表示一个或多个空白字符(这这里指空格)
    
    df.info()
    df.describe()
    

    平均用户购买7张,最多的购买了1033张;平均消费金额为106,标准差为240;平均值和75分位数接近,再加上最大值判断 存在小部分高额消费用户

    df['date'] = pd.to_datetime(df.dt,format = '%Y%m%d')
    df['month'] = df.date.values.astype('datetime64[M]')
    
    df.info()
    
    user_group = df.groupby('id').sum()
    user_group.describe()
    
    df.groupby('month').products.sum().plot()
    df.groupby('month').amount.sum().plot()
    df.plot.scatter(x = 'amount',y = 'products')
    df.groupby('id').sum().plot.scatter(x = 'amount',y = 'products')
    

    前几个月销量非常高,相比于后期数据显得异常,后期则较为平稳



    与销量趋势一样,考虑可能是有季度促销活动的原因,也不能排除用户数据异常值



    看到订单极值并不多,应该不是他们导致的异常波动

    用户购买数量也没有太多极值
    plt.figure(figsize = (12,4))
    plt.subplot(121)
    df.amount.hist(bins = 30)
    plt.subplot(122)
    df.products.hist(bins = 30)
    

    从直方图看出也看出大部分用户的消费水平并不高



    分组用户,求月份的最小值:用户消费行为中第一次的消费时间,集中在前三个月
    用户最后一次消费时间,依然集中在前三个月,后续依然存在用户消费但缓慢减少

    #复购率
    pi_counts = df.pivot_table(index = 'id',columns = 'month',
                              values = 'dt',aggfunc = 'count').fillna(0)
    pi_counts.columns = df.month.sort_values().astype('str').unique()
    
    pi_counts_t = pi_counts.applymap(lambda x:1 if x>1 else (np.NaN if x==0 else 0))
    
    (pi_counts_t.sum() / pi_counts_t.count()).plot()
    

    在pandas中,数据透视有专门的函数pivot_table,功能非常强大。pivot_table参数中, index是设置数据透视后的索引,column是设置数据透视后的列,简而言之,index是你想要 的行,column是想要的列。案例中,我希望统计每个用户在每月的订单量,所以user_id是 index,month是column。

    values是将哪个值进行计算,aggfunc是用哪种方法。于是这里用values=order_dt和 aggfunc=count,统计里order_dt出现的次数,即多少笔订单。

    applymap针对Dataframe里的所有数据;lambda没有elif的用法所以要两个if else



    求出复购率,sum 和 count 都会忽略NaN(图中横坐标不知道为什么没有标注,是月份)

    可以看出早期的复购率较低,后期则拥有较稳定的复购率20%左右

    #回购率
    pi_amount = df.pivot_table(index = 'id',columns = 'month',
                              values = 'amount',aggfunc = 'mean').fillna(0)
    pi_amount.columns = df.month.sort_values().astype('str').unique()
    
    pi_amount_t = pi_amount.applymap(lambda x:1 if x>0 else 0)
    
    def pur_r(data):
        s = []
        for i in range(17):
            if data[i] == 1:
                if data[i+1] == 1:
                    s.append(1)
                if data[i+1] == 0:
                    s.append(0)
            else:
                s.append(np.NaN)
        s.append(np.NaN)
        return pd.Series(s,index = pi_amount.columns)
    
    pi_amount_r = pi_amount_t.apply(pur_r,axis = 1)
    
    (pi_amount_r.sum() / pi_amount_r.count()).plot()
    

    新建一个判断函数。data是输入的数据,即用户在18个月内是否消费的记录,status是空列表,后续用来保存用户是否回购的字段。

    因为有18个月,所以每个月都要进行一次判断,需要用到循环。if的主要逻辑是,如果用户本月进行过消费,且下月消费过,记为1,下月没有消费过是0。本月若没有进行过消费,为NaN,后续的统计中进行排除。

    用apply函数应用在所有行上,获得想要的结果。


    image.png

    用户回购大于复购,约为30%,波动性较强

    新客户回购率约在15%左右,差异不是那么明显

    (新客户为第一次消费,老客户为更多消费次数)

    综合分析复购率和回购率,新客质量整体低于老客,老客忠诚度表现较好,消费频次稍次

    进行用户分层:

    新用户为第一次消费,活跃用户为在某个时间窗口内有过消费,不活跃用户为在时间窗口内没有消费过的老客,回流用户为在上一个窗口中没有消费,而在当前时间窗口内有过消费

    #用户分层
    def user_type(data):
        s = []
        for i in range(18):
            #本月未消费
            if data[i] == 0:
                if len(s) == 0:
                    s.append('未消费')
                else:
                    if s[i-1] == '未消费':
                        s.append('未消费')
                    else:
                        s.append('unactive')
            #本月消费
            else:
                if len(s) == 0:
                    s.append('new')
                else:
                    if s[i-1] == '未消费':
                        s.append('new')
                    elif s[i-1] == 'unactive':
                        s.append('return')
                    else:
                        s.append('active')
        return pd.Series(s,index = pi_amount.columns)
    
    pi_amount_s = pi_amount_t.apply(user_type,axis = 1)
    
    amount_counts = pi_amount_s.replace('未消费',np.NaN).apply(pd.value_counts)
    amount_counts
    
    amount_counts.fillna(0).T.plot.area(figsize = (12,6))
    

    #回流和活跃用户占比
    return_rata = amount_counts.apply(lambda x:x / x.sum(),axis = 0)
    plt.figure(figsize = (12,4))
    plt.subplot(121)
    return_rata.loc['return'].plot()
    plt.subplot(122)
    return_rata.loc['active'].plot()
    

    用户回流占比在5%-8%左右,存在下降趋势;活跃用户下降趋势更加明显,3%-5%左右,但在实际情况上其用户质量高于回流用户

    #高消费人群贡献
    u_amount = df.groupby('id').amount.sum().sort_values().reset_index()
    u_amount['_cumsum_'] = u_amount.amount.cumsum()
    total = u_amount._cumsum_.max()
    u_amount['prop'] = u_amount.apply(lambda x:x._cumsum_ / total,axis = 1)
    u_amount.tail()
    
    u_amount.prop.plot()
    

    order_amount为用户消费金额,cumsum是累加函数,最后的2500315.63就是总计消费金额

    .tail方法求的是最末n项值



    可以看出前20000名用户贡献了40%的消费,而后4000名用户则有60%,呈现28倾向

    u_counts = df.groupby('id').dt.count().sort_values().reset_index()
    u_counts['_counts_'] = u_counts.dt.cumsum()
    total_ = u_counts._counts_.max()
    u_counts['prop'] = u_counts.apply(lambda x:x._counts_ / total_,axis = 1)
    u_counts.tail()
    
    u_counts.prop.plot()
    


    前两万用户贡献了50%的销量,高消费用户群体贡献了50%销量

    #用户生命周期
    
    od_min = df.groupby('id').date.min()
    od_max = df.groupby('id').date.max()
    
    (od_max - od_min).describe()
    ((od_max - od_min) / np.timedelta64(1,'D')).hist()
    


    因为这里的数据类型是timedelta时间,它无法直接作出直方图,所以先换算成数值。换算的方式直接除timedelta函数即可,这里的np.timedelta64(1, 'D'),D表示天,1表示1天,作为单位使用的。因为max-min已经表示为天了,两者相除就是周期的天数。

    大部分用户都只消费了1次,不妨排除这个群体,计算消费两次以上的老客生命周期

    #一次以上用户生命周期
    life_time = (od_max - od_min).reset_index()
    life_time['lifetime'] = life_time.date / np.timedelta64(1,'D')
    life_time[life_time.lifetime > 0].lifetime.hist(bins = 100,figsize = (12,6))
    

    去除了生命周期为0的客户后,可以看出依然有许多用户的生命周期向0靠拢,呈现双峰趋势;普通型的用户生命周期集中在50—300天,高质量的用户生命周期在400天后,基本属于忠诚用户了


    #留存率
    u_retention = pd.merge(left = df,right = od_min.reset_index(),
                          how = 'inner',on = 'id',suffixes = ('','_min'))
    u_retention['od_diff'] = u_retention.date - u_retention.date_min
    u_retention['_diff_'] = u_retention.od_diff.apply(lambda x:x / np.timedelta64(1,'D'))
    
    bin = [0,3,7,15,30,60,90,180,360]
    u_retention['diff_bin'] = pd.cut(u_retention._diff_,bins = bin).astype('str')
    pi_retention = u_retention.pivot_table(index = 'id',columns = 'diff_bin',
                                          values = 'amount',aggfunc = sum).drop(['nan'],axis = 1)
    pi_retention.mean()
    

    merge相当于SQL的join,在how参数里指定类型为inner join,以user_id作为主键连接,suffixes参数使合并内容中重名项添加后缀


    pi_retention_t = pi_retention.fillna(0).applymap(lambda x:1 if x>0 else 0)
    (pi_retention_t.sum() / pi_retention_t.count()).plot.bar()
    

    “只有2.5%的用户在第一次消费的次日至3天内有过消费,3%的用户在3~7天内有过消费。数字并不好看,CD购买确实不是高频消费行为。时间范围放宽后数字好看了不少,有20%的用户在第一次消费后的三个月到半年之间有过购买,27%的用户在半年后至1年内有过购买。从运营角度看,CD机营销在教育新用户的同时,应该注重用户忠诚度的培养,放长线掉大鱼,在一定时间内召回用户购买。”

    (可能是版本问题,不把date_diff_bin转换为str格式就无法显示(0,3]而且整个数据统计也是错误的,寻找解决方法未果;转换为str之后它只会按字符思维的顺序排列)

    (始终不能重排列顺序心态崩了不想自己看就直接复制了)

    #平均购买周期
    def diff(group):
        d = group._diff_.shift(-1) - group._diff_.shift(1)
        return d
    last_diff = u_retention.groupby('id').apply(diff)
    last_diff.mean()
    

    shift为pandas的偏移函数,其括号内的值控制向前或者是向后偏移;

    若加入参数axis=1则是左右偏移;

    在这里diff中实现的是用户两次消费的时间间隔

    平均消费间隔为68天

    想要召回用户,在60天左右效果较好

    last_diff.hist(bins = 100)
    

    大部分用户的消费间隔还是较短的,所以想要留住用户,可以在用户每次消费之后赠送20-30天内使用的优惠券,消费后一周内询问用户消费体验,15天左右提醒优惠券到期,30天短信提醒推送

    总结

    问题及优化

    1. 打开文件的路径,可以直接放在jupyter的打开目录下
    2. 如果在函数定义中返回结果是列表,然而要用于DataFrame:pd.Series( ,index= )
    3. 作图后图中有时候无横坐标显示
    4. 用户生命周期中:大部分用户都只消费一次,可以排除这个群体查看老客用户生命周期
    5. 分区时间差之后,数据透视表始终缺少(0,3]、多出 NaN 列以及数据不正常:
      将所有分组类型改为字符串类型、再去除 NaN 列,再作图(问题在于修改类型之后各组排序混乱,始终无解决方法)

    相关文章

      网友评论

          本文标题:数据分析:CD网站用户消费行为分析

          本文链接:https://www.haomeiwen.com/subject/xkebjctx.html