python案例——cd消费用户行为分析

前言:

存在一份用户在一家cd网站上的消费记录,对此进行消费情况及用户行为分析。
数据样式:


image.png

数据来源:
链接:https://pan.baidu.com/s/1l68MIJil0-kZQg5DAxIcdg
提取码:w50i

具体步骤:
1.数据清洗
2.进行用户行为销售分析(按月):每月消费总额、次数、产品购买量、人数
3.用户个体消费分析:重点关注消费金额和消费次数情况
4.用户消费行为分析:首购、最后一次购买、用户分层(新老客、RFM)、用户购买周期、用户生命周期
5.用户回购率和复购率分析

数据清洗:

1.导入常用包及数据集

import pandas as pd
import numpy as np

columns = ['user_id','order_dt','order_products','order_amount']
df = pd.read_table('CDNOW_master.txt',names = columns , sep= "\s+")
image.png

2.检查数据并清洗整理

df.info() #order_dt int64 为整数型 需改成日期型
image.png
df['order_dt']=pd.to_datetime(df.order_dt,format="%Y%m%d")
df['month']=df.order_dt.values.astype("datetime64[M]") #astype 转换格式
image.png

按月分析故新列【month】字段

一、进行用户消费趋势分析(按月)

1.每月的消费总金额

grouped_month = df.groupby('month')
order_month_amount=grouped_month.order_amount.sum()
order_month_amount.head()
image.png

导入可视化包

#加载数据可视化包
import matplotlib.pyplot as plt
#可视化显示在页面,%代表内置命令,inline 显示图标
%matplotlib inline
# 更改设计风格
plt.style.use('ggplot')
order_month_amount.plot() #plot为折线图

image.png

由上图可知,消费金额在前三个月达到最高分,后续消费金额较为平稳,有轻微下降趋势
2.每月的消费次数

grouped_month.user_id.count().plot()
image.png

前三个月消费订单数在10000笔左右,后续月份的平均消费人数则在2500人

3.每月的产品购买量

grouped_month.order_products.sum().plot()

image.png

前三个月产品购买数在20000以上,后续月份的产品购买量在6000~8000左右 。
4.每月的消费人数

df.groupby("month").user_id.apply(lambda x:len(x.drop_duplicates())).plot() 

image.png

每月消费人数低于每月消费次数,但差异不大 前三个月每月的消费人数在8000-10000之间,后续月份平均消费人数在2000人不到
5.直接利用数据透视表分析消费金额、产品购买量、消费次数

pivot_df=df.pivot_table(index ="month",
              values = ["order_products","order_amount","user_id"],
               aggfunc = {"order_products":'sum',
                         "order_amount":"sum",
                          "user_id":"count"}).head()
#数据透视表进行去重操作比较麻烦,不建议
image.png
pivot_df.plot()
image.png

二、用户个体消费分析

1.用户消费金额、消费次数的描述统计

grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

image.png

用户平均购买了5.39张CD,但是中位数只有1,说明小部分用户购买了大量的CD
用户平均消费3.7元,中位值有7.37,判断同上,有极值干扰
2.用户消费金额和消费的散点图

grouped_user.sum().plot.scatter(x = "order_amount",y = "order_products")
image.png
grouped_user.sum().query("order_amount<4000000").plot.scatter(x = "order_amount",y = "order_products")

image.png

3.用户消费金额的分布图

grouped_user.sum().order_amount.plot.hist(bins = 20) #bins是分组,分20个组
image.png

从直方图可知,用户消费金额,绝大部分呈现几种趋势,部分异常值干扰了判断。可以使用过滤操作排除异常

grouped_user.sum().query("order_products < 92").order_amount.plot.hist(bins = 40) 
image.png

使用切比雪夫定理过滤异常值,计算95%的数据的分布情况。 mean ± 5std (order_products 7+17*5=92)

4.用户累计消费金额占比(百分之多少的用户占了百分值多少的消费额)

user_cumsum = grouped_user.sum().sort_values("order_amount").apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot(figsize = (10,2)) #reset_index()去掉索引 方便作图
image.png

按用户消费金额进行升序排列,由图可知 50% 的用户 仅贡献了15%的消费额度。而排名前5000的用户贡献了60%的消费额度。

三、用户消费行为

1.用户第一次消费(首购)

grouped_user.min().order_dt.value_counts().plot() #最小时间,距离用户最遥远,就是第一次购买的时间,value_counts()计数每天的个数
image.png

用户第一次购买分布,集中在前三个月
其中,在2月11日至2月25日有一次剧烈波动

2.用户最后一次消费

grouped_user.max().order_dt.value_counts().plot()
image.png

用户最后一次购买的分布比第一次分布广
大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买

3.新老客消费比

3.1多少用户仅消费了一次?

user_life = grouped_user.order_dt.agg(["min","max"])
user_life.head()
image.png
(user_life["min"] == user_life["max"]).value_counts()
image.png

有一半用户,就消费了一次

3.2每月新客户占比?

grouped_um = df.groupby(['month','user_id']).order_dt.agg(["min","max"])     #按月分组下的userid分组,求每月的最早购买日期和最晚消费日期
grouped_um["new"] = (grouped_um["min"] == grouped_um["max"] )                # 新增列 True 为 新用户

grouped_um.reset_index().groupby("month").new.value_counts()
image.png
grouped_um.reset_index().groupby("month").new.count()
image.png
grouped_um1 = grouped_um.reset_index().groupby("month") #重新按月分组
grouped_um2 = grouped_um1["new"].apply(lambda x : x.value_counts()/x.count()).reset_index() #求新老用户占比
grouped_um2
image.png
grouped_um2[grouped_um2["level_1"]].plot(y = 'new', x ="month")  #利用布尔值筛选True 作图
image.png

97年3至4月新用户数量由90%跌落80%,后几个月新用户量保持在80~82%区间,1998年6月后再无新用户

4.用户分层-RFM

image.png

rfm = df.pivot_table (index = "user_id",
                      values = ["order_products","order_amount","order_dt"],
                      aggfunc = {"order_dt":"max",
                                 "order_amount":"sum",
                                 "order_products":"sum"
                                })
rfm.head()
image.png
 -(rfm.order_dt - rfm.order_dt.max()) # 最早时间与最晚时间差
image.png
rfm["R"] = -(rfm.order_dt - rfm.order_dt.max()) / np.timedelta64(1,"D") #/np.timedelta64(1,"D") 换成浮点数
rfm.rename(columns = {"order_products":"F","order_amount":"M"},inplace = True) #inplace 代表 是否覆盖原始二维表
rfm[["R","F","M"]].apply(lambda x : x-x.mean())
image.png
def rfm_func(x):
    level = x.apply(lambda x:"1" if x>0  else "0")
    label = level.R + level.F +level.M
    d = {
        "111":"重要价值客户",
        "011":"重要保持客户",
        "101":"重要发展客户",
        "001":"重要挽留客户",
        "110":"一般价值客户",
        "010":"一般挽留客户",
        "100":"一般发展客户",
        "000":"一般挽留客户"
    }
    result = d[label]
    return result
rfm['label'] = rfm [["R","F","M"]].apply(lambda x: x-x.mean()).apply(rfm_func,axis = 1) #axis = 1是逐行应用
image.png
rfm.loc[rfm.label == '重要价值客户','color'] = 'g'     #green
rfm.loc[~(rfm.label == '重要价值客户'),'color'] = 'r' #red
rfm.plot.scatter("F","R",c=rfm.color) 
image.png
rfm.groupby('label').sum()
image.png

从RFM分层可知,大部分用户为重要保持客户,但是这是由于极值的影响,所以RFM的划分标准应该以业务为准

尽量用小部分的用户覆盖大部分的额度
不要为了数据好看划分等级

5.用户分层-新、活跃、回流、消失

pivoted_counts=df.pivot_table(index = "user_id",
                              columns = "month",
                              values = "order_dt",
                              aggfunc = "count").fillna(0)
pivoted_counts.head()
image.png
df_purchase = pivoted_counts.applymap(lambda x: 1 if x>0 else 0) #简化模型,只需判断是否存在 即 1与0
df_purchase.tail ()  #判断尾部数据 是从 3月份才开始第一次购买
image.png
def active_status(data):
    status = []
    for i in range(18):  #12+6个月
    
        #若本月没有消费
        if data[i] == 0:
            if len(status) > 0:                 #判断 存在记录的话时
                if status[i-1] == "unreg":      #unreg 未注册
                    status.append("unreg")
                else:
                    status.append("unactive")   # 如果前一个为 unreg 未注册,则后一个应判断为不活跃
            else:
                status.append("unreg")         #不存在记录时,加入 unreg
            
        #若本月消费
        else:
            if len(status) == 0:
                status.append("new")
            else:
                if status[i-1] == "unactive" :
                    status.append("return")     #回流
                elif status[i-1] == "unreg" :
                    status.append("new")
                else:
                     status.append("active")
    return status
image.png

若本月没有消费

  • 若之前是未注册,则依旧为未注册
  • 若之前有消费,则为流失/不活跃
  • 其他情况,为未注册

若本月有消费

  • 若是第一次消费,则为新用户
  • 如果之前有过消费,则上个月为不活跃,则为回流
  • 如果上个月为未注册,则为新用户
  • 除此之外,为活跃
indexs=df['month'].sort_values().astype('str').unique()  #astype 的区别
purchase_stats = df_purchase.apply(lambda x:pd.Series(active_status(x),index=indexs),axis=1)
purchase_stats.head(5)
image.png
purchase_stats.tail(5)
image.png
purchase_stats_ct1 = purchase_stats.apply(lambda x:pd.value_counts(x))  #注意unreg区别
purchase_stats_ct1
image.png

流失用户(unactive)正在增加
新用户暂停
活跃用户越来越少
运营差

purchase_stats_ct.fillna(0).T.head()  #fillna将空值填充为0,  .T转置
image.png
purchase_stats_ct.fillna(0).T.plot.area()   #.plot.area()面积图
image.png
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis =1)
image.png

由上表可知,每月的用户消费状态变化

  • 活跃用户,持续消费的用户,对应的是消费运营的质量
  • 回流用户,之前不消费本月才消费,对应的是唤回运营
  • 不活跃用户,对应的是流失

6.用户购买周期(按订单)

  • 用户消费周期描述
  • 用户消费周期分布
df.head(10)
image.png
order_diff = grouped_user.apply(lambda x : x.order_dt -x.order_dt.shift()) #grouped_user = df.groupby('user_id'),shift() 为 两个日期错行相减
order_diff.head(10)      #目的是求 时间差值 
image.png
order_diff.describe()     #会自动过滤空值,对时间差值进行描述统计分析
image.png
(order_diff / np.timedelta64(1,"D")).hist(bins = 20)
image.png
  • 订单周期呈指数分布
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期都低于100天

7.用户生命周期(按第一次&最后一次消费)

  • 用户生命周期描述
  • 用户生命周期分布


    image.png
(user_life["max"] - user_life["min"]).describe()
image.png
((user_life["max"] - user_life["min"])/np.timedelta64(1,"D")).hist(bins = 40)
image.png

用户的生命周期受只购买一次的用户影响比较厉害(可以排除)
用户均消费134天,中位数仅0天

user_life["差值"]=(user_life["max"] - user_life["min"])
user_life.head()
image.png
user_life["差值"]=(user_life["max"] - user_life["min"])/np.timedelta64(1,"D")
user_life.head(5)
image.png
user_life.query("差值>0")["差值"].hist(bins = 40)
image.png

四、复购率和回购率分析

复购率

  • 自然月内,购买多次的用户占比
    回购率
  • 曾经购买过的用户在某一时期内的的再次购买的占比
pivoted_counts.head()
image.png
purchase_r = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x== 0 else 0 ) 
purchase_r.head()
image.png
(purchase_r.sum()/purchase_r.count()).plot(figsize = (10,4)) #宽为10 高为4 , sum()对于1的求和,count()过滤掉np.Nan
image.png

复购率稳定在20%左右,前三个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低

df_purchase.head()
image.png
def purchase_back(data):
    status = []
    for i in range(17):
        if data[i] == 1:
            if data[i+1] ==1:
                status.append(1)
            if data[i+1] ==0:
                    status.append(0)
        else:
            status.append(np.NaN)
    status.append(np.NaN)  #需注意 语句中的是 第一位与第二位相比 输出结构放第一位, 循环17次,17/18的判断结束后,输出的status仅17个,需补上最后一个
    return status
#1代表 本月购买及下个月购买 ,sum()可计算 ,0代表 本月购买,下个月未购买 count()可计算总人数, 回购率: 本月及下月购买/本月购买人群
indexs=df['month'].sort_values().astype('str').unique()
purchase_b = df_purchase.apply(lambda x :pd.Series(purchase_back(x),index = indexs),axis =1)
purchase_b.head()
image.png
(purchase_b.sum()/purchase_b.count()).plot(figsize = (10,4))
image.png

绝大部分用户购买一次后不再购买,老用户回购率在30%徘徊

总结

1.cd网站的用户在前三个月内涌入绝大多数新用户,其前三个月的平均消费次数为10000笔左右,消费人次达8000~10000左右,后续的消费次数及人次下跌至2000左右。

2.根据二八法则,50%的用户仅贡献了15%的消费额度,而消费金额前5000名用户贡献了60%消费额度,同时根据用户购买次数,平均购买7张CD,而中位数只有3,说明小部分用户购买大量的CD,符合上述二八法则的分析。

3.根据用户首次购买、最后购买及新老客占比分析,绝大部分用户购买在前三个月,且购买一次就不再进行购买,这部分客户占50%(符合2的50%用户仅贡献15%消费额度)。

4.根据每月新用户占比分析,3至4月的新用户占比由90%跌落80%,后几个月新用户量保持在80%到82%区间,1998年6月后再无新用户,前三个月新用户平均8000人左右,后续新用户在1000至1500人波动。

5.根据用户分层-RFM分析,重要价值客户的消费金额高,但近期消费下降,大部分为重要保持客户。

6.按用户购买时间分析,用户平均购买周期68天,绝大部分用户购买周期都低于100天。

7.根据复购率和回购率分析,复购率稳定在20%左右,回购率稳定在30%左右。

你可能感兴趣的:(python案例——cd消费用户行为分析)