数据分析案例——用户消费行为分析

4 篇文章 1 订阅
2 篇文章 0 订阅

数据分析之——用户消费行为分析

内容摘要

  • 项目背景
  • 项目目的
  • 分析思路
  • python数据分析实现
  • PowerBI可视化看板设置

一、项目背景

  • 这是一份用户消费行为的分析报告;
    数据来源于网上,是用户在一家单车网站上的消费记录,其时间跨度为1997年1月至1998年4月。
  • 数据格式:txt文档,具体信息见图
    在这里插入图片描述
  • 第一列——user_id:用户ID
    第二列——order_dt:购买日期
    第三列——order_prodects:购买产品数
    第四列——order_amount:购买金额

二、项目目的

通过数据分析:
1、了解本网站的基本营业情况;
2、对用户行为进行分析,掌握用户的分布情况,便于之后的策略制定;

三、分析思路

1、基础数据整理清洗
2、针对月份进行用户消费趋势的分析——便于掌握公司的营收发展趋势
3、针对用户个体消费进行分析
4、针对用户消费行为进行分析
5、针对用户购买周期进行分析
6、针对用户复购回购进行分析

四、python数据分析实现

  • 一、基础数据整理清洗
    数据清洗的目的在于对数据进行审查和校验,其主要操作有:
    (1)解析原始数据列名,理解其意义并做通俗化替换;
    (2)查看数据源每一条信息的数据类型是否正确;
    (3)查看数据有无缺失值情况;
    (4)查看数据有无异常值情况;
    (5)数据一致化处理(多用于针对时间数据)

    • 首先导入数据,解析原始数据信息,做通俗化替换。
#导入必要的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

#导入数据并设置列名
#解析原始数据列名,理解其意义并做通俗化替换
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table(r':\bicycle_master.txt',sep='\s+',names=columns)
#查看数据
df.head(2)

在这里插入图片描述

  • 查看数据源每一列信息的数据类型是否正确,尤其需要注意可能会计算的数据和时间格式。
#查数据源每一条信息的数据类型是否正确
df.info()
#发现其中order_dt 购买日期应该为时间格式类型,并非整型数据,需要进行数据处理

在这里插入图片描述

-数据源中时间数据的类型为整型,需要修改成datetime格式,并且为接下来时间序列分析做准备需要将年和月提取出来。

#pd.to_datetime函数进行转换
df['order_dt']=pd.to_datetime(df['order_dt'],format='%Y%m%d')
#同时考虑到后续对数据分分析,将其按照年、月将时间提取出来
#生成年、月
df['Year']=df['order_dt'].astype('datetime64[Y]')
df['Month']=df['order_dt'].astype('datetime64[M]')
#查看数据
df.head(2)

在这里插入图片描述

#查看数据描述
df.describe()

在这里插入图片描述

  • 发现大部分订单只消费了少量商品,75%用户的产品购买量仅3个;
    用户的消费金额比较稳定,平均消费35元,中位数在25元,存在一定极值的干扰。

  • 基础数据整理结束

  • 二、针对月份进行用户消费趋势的分析

    如果以天为尺度进行分析,具有较大的波动性和随机性,并且数据量较大;以月为尺度进行分析,能够避免一定的随机性,并且还可以看出一定的分布趋势,故选择以月为尺度进行数据分析。
    以月为尺度进行分析,较为重要的是消费金额,消费频率,购买产品量和消费人数
    消费人数的变化可以看出该平台对用户粘性的变化程度;消费水平一方面与消费人数有关,另一方面可能与当月的产品及活动有关。

    • 每月的消费总金额
    • 每月的消费次数
    • 每月的产品购买量
    • 每月的消费人数
#需要对Month进行一个groupby,并对购买金额和购买产品数量进行求和,对购买次数(用户id)进行计数
grouped=df.groupby(df['Month']).agg({"order_amount":'sum','user_id':'count','order_products':'sum'})
grouped.head()

在这里插入图片描述

#统计每月的消费人数,即统计用户id唯一值的个数
order_month_person=grouped_month.user_id.nunique()
order_m_p=order_month_person.to_frame()
order_m_p.head()

在这里插入图片描述

  • 对用户人数统计可以看出,4月后出现明显的下跌趋势,将数据进行保存,用于后期powerbi绘图

  • 三、针对用户个体消费进行分析

    • 用户消费金额,消费个数描述性统计
    • 用户消费金额和消费次数的散点图
    • 用户消费金额的分布图
    • 用户消费次数的分布图
    • 用户累计消费金额占比
#新建一个按照用户分类的数据
user_info=df.groupby('user_id')
#用户消费金额,消费个数描述性统计
user_info.sum().describe()

(1)用户消费金额,消费个数描述性统计
在这里插入图片描述

  • 用户平均购买为7,但是中位值只有3,说明购买量大的用户占少数
  • 用户平均消费106元,中位值有43,判断同上,有极值干扰
    (2)用户消费金额和产品个数的散点图
#用户消费金额和消费次数的散点图
user_info.sum().plot.scatter(x='order_amount',y='order_products')

在这里插入图片描述

#绘制消费金额和消费个数的散点图
user_cost_number=df.groupby('user_id').agg({'order_amount':'sum','order_products':'count'})
user_cost_number.plot.scatter(x='number',y='cost')

在这里插入图片描述
(3)用户消费金额的分布图

user_info.sum().order_amount.plot.hist(bins=20)
#从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常

在这里插入图片描述

#使用切比雪夫定理过滤掉异常值,因为切比雪夫定理说明,95%的数据都分布在5个标准差之内,剩下5%的极值就不要了
#order_amount (mean = 106 ,std = 241) mean+5std = 1311
#过滤操作 pd.query(条件)
user_info.sum().query('order_amount<1311').order_amount.plot.hist(bins=20)

在这里插入图片描述

(4)用户消费次数的分布图

#过滤操作
#先筛选再取数
user_cost_number.query('number<50').number.plot.hist(bins=20)

在这里插入图片描述

(5)用户累计消费金额占比

# cumsum 是求累加值
user_cumsum=user_info.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
# 这里 reset_index() 是为了得到一个自然数的行标签,表示的就是人数,下面的图就可以看出来多少个少占多少百分比
user_cumsum.reset_index().order_amount.plot()

在这里插入图片描述

  • 四、针对用户消费行为进行分析
    • 用户第一次消费(首购)
    • 用户最后一次消费
    • 新老客户消费比
      • 多少用户仅消费一次
      • 每月新客占比
    • 用户分层
      • RFM模型
      • 新、老、活跃、回流、流失

(1)用户第一次消费及最后一次消费

grouped_user=df.groupby(df['user_id'])
#第一次消费=消费时间最小=min()
grouped_user.min().order_dt.value_counts().plot()	

在这里插入图片描述

#最后一次消费=消费时间最大=max()
grouped_user.max().order_dt.value_counts().plot()

在这里插入图片描述

  • 发现新用户仅在前三个月,随后便没有新用户的产生;最后一次消费呈现断崖式的下降

(2)新老客户消费比

  • 多少用户仅消费一次
  • 每月一次消费与多次消费比例
#仅消费一次说明第一次第一次消费时间=最后一次消费时间
user_life=grouped_user.order_dt.agg([max,min])
#选择第一次==最后一次的
#统计数量
(user_life['max']==user_life['min']).value_counts()

"""
结果如下
True     12054
False    11516
dtype: int64
"""
  • 有一半的用户,只消费了一次
#每月新客占比
#df.groupby(['Month','user_id']) 会有多个日期,因此pivot_table不显示order_dt
grouped_um = df.groupby(['Month','user_id']).order_dt.agg(["min","max"])      #按月分组下的userid分组,求每月的最早购买日期和最晚消费日期
grouped_um["new"] = (grouped_um["min"] == grouped_um["max"] )       # 新增列 True 为一次消费的用户

#统计true和false的个数
grouped_um.groupby(['Month']).new.value_counts()

在这里插入图片描述
(3)用户分层

  • RFM模型
  • 新、老、活跃、回流、流失
# 用户分层
# 画 RFM,先对原始数据进行透视

#消费时间-最近;消费频率-求和;消费金额-求和
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()

在这里插入图片描述

# 得到最近一次消费,一般是计算 today 距离最近一次消费,这里因为时间太久远,就随便用的max值
# 数值越大就越久远,分子得到的是一些天数类似 545 days(因为是时间格式相减),处以一个单位,就不会有单位了只留下数值
#这里取负数是为了接下与mean比较是标定0、1而定的,因为越近越好,其时间越小
rfm['R']= -(rfm.order_dt - rfm.order_dt.max())/np.timedelta64(1,'D')
# 重命名,也就是 R:最后一次消费距今天数,F:消费总金额 ,M:消费总产品数
# R :消费时间  F:消费金额  M:消费频次
rfm.rename(columns={'order_products':"F",'order_amount':'M'},inplace=True)

在这里插入图片描述

#这里需要定义一个函数,针对RFM的表现进行8种类型的区分
#这个RFM的标准都选择为其平均值
def rfm_func(x):
    level=x.apply(lambda x:'1' if x>=0 else '0')
    # level 的类型是 series,index 是 R、F、M
    #print(type(level))
    #print(level.index)
    label=level.R + level.F + level.M
    d={
        # R 为1 表示比均值大,离最早时间近,F为1 表示 消费金额比较多,M 为1 表示消费频次比较多,所以是重要价值客户
        '111':'重要价值客户',
        '011':'重要保持客户',
        '101':'重要发展客户',
        '001':'重要挽留客户',
        '110':'一般价值客户',
        '010':'一般保持客户',
        '100':'一般发展客户',
        '000':'一般挽留客户',
    }
    result=d[label]
    return result

# 注意这里是要一行行的传递进来,所以 axis=1,传递一行得到一个 111,然后匹配返回一个值
rfm['label']=rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)

在这里插入图片描述

  • 从RFM 分层可知,大部分用户是重要保持客户,但是这是由于极值的影响,所以 RFM 的划分标准应该以业务为准,也可以通过切比雪夫去除极值后求均值,并且 RFM 的各个划分标准可以都不一样
  • 尽量用小部分的用户覆盖大部分的额度,这样对之后计划的指定可以更具有针对性
#新、老、活跃、回流、流失
# 数据透视, userid为索引,月为列,求每月的消费次数,这里填充了
pivoted_counts=df.pivot_table(index='user_id',
                             columns='Month',
                             values='order_dt',
                             aggfunc='count').fillna(0)
# 转变一下消费,有消费为1,没有消费为0
df_purchase=pivoted_counts.applymap(lambda x:1 if x>0 else 0)
df_purchase.tail()

在这里插入图片描述

# 这里由于进行数据透视,填充了一些 null 值为0,而实际可能用户在当月根本就没有注册,
#这样会误导第一次消费数据的统计,所以写一个函数来处理
def active_status(data):
    status=[]
    # 数据一共有18个月份,每次输入一行数据,这样进行逐月判断
    for i in range(18):
        # 若本月没有消费,上面处理过的结果
        if data[i]==0:
            if len(status)>0:
                if status[i-1]=='unreg':
                    status.append('unreg')
                else:
                    status.append('unactive')
            else:
                # 之前一个数据都没有,就认为是未注册
                status.append('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
"""
若本月没有消费,这里只是和上个月判断是否注册,有缺陷,可以判断是否存在就可以了

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

若是第一次消费,则为新用户
如果之前有过消费,则上个月为不活跃,则为回流
如果上个月为未注册,则为新用户
初次之外,为活跃
return:回流

new:新客

unreg:未注册

active:活跃
"""
#传入数据
purchase_stats=df_purchase.apply(lambda x: pd.Series(active_status(x),index=df_purchase.columns),axis=1)

在这里插入图片描述

# 这里把未注册的替换为空值,这样 count 计算时不会计算到
# 得到每个月的用户分布
purchase_stats_ct=purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct

在这里插入图片描述

#计算各种占比
returnratee=purchase_stats_ct.apply(lambda x:x/x.sum(),axis=0)
#将NAN填充成0并转至
purchase_stats_ct_info = purchase_stats_ct.fillna(0).T
#绘制面积图
purchase_stats_ct_info .plot.area()

在这里插入图片描述

  • 五、针对用户购买周期进行分析
    • 用户购买周期(按订单)
      • 用户消费周期描述
      • 用户消费周期分布
    • 用户生命周期(按第一次和最后一次消费) -
      • 用户生命周期描述
      • 用户生命周期分布

(1)用户购买周期(按订单)

  • 用户消费周期描述
  • 用户消费周期分布
# 用户购买周期(按订单)
#注意shift函数的使用
# 计算相邻两个订单的时间间隔,shift 函数是对数据进行错位,所有数据会往下平移一下,所以可以
order_diff=grouped_user.apply(lambda x:x.order_dt-x.order_dt.shift())
order_diff.head(10)

在这里插入图片描述

#用户消费周期描述
order_diff.describe()

在这里插入图片描述

#用户消费周期分布
# 去除单元值np.timedelta64(1,'D'))
(order_diff/np.timedelta64(1,'D')).hist(bins=20)

在这里插入图片描述

  • 订单周期呈指数分布
  • 用户的平均购买周期是68天
  • 绝大部分用户的购买周期都低于100天

(2)用户生命周期(按第一次和最后一次消费)

  • 用户生命周期描述
  • 用户生命周期分布
# 用户生命周期(按第一次和最后一次消费)描述
(user_life['max']-user_life['min']).describe()

在这里插入图片描述

#用户生命周期分布
((user_life['max']-user_life['min'])/np.timedelta64(1,"D")).hist(bins=40)

在这里插入图片描述

  • 用户的生命周期受只购买一次的用户影响比较厉害(可以排除)
  • 用户均消费134天,中位数仅0天
# 提取大于0的数据
u_1=(user_life['max']-user_life['min']).reset_index()
u_1

在这里插入图片描述

# 提取大于0的数据
u_1=(user_life['max']-user_life['min']).reset_index()[0]/np.timedelta64(1,"D")
u_1[u_1>0].hist(bins=40)

在这里插入图片描述

  • 六、针对用户复购回购进行分析
    • 复购率
      • 自然月内,购买多次的用户占比(即,购买了两次以上)
    • 回购率
      • 曾经购买过的用户在某一时期的再次购买的占比(可能是在三个月内)
# 复购率
pivoted_counts.head(10)

在这里插入图片描述

# 区分一个,和一个以上的情况,以便于计算复购率,大于1为1,等于0 为0,等于1为0
purchase_r=pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
purchase_r.head()

在这里插入图片描述

# 复购人数/总消费人数(不会计算nan值)
#绘图
(purchase_r.sum()/purchase_r.count()).plot(figsize=(10,4))

在这里插入图片描述

  • 复购率稳定在20%所有,前一个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低
#回购率使用的数据集
df_purchase.head()

在这里插入图片描述

#回购率
# 需要使用函数来判断是否回购:当月消费过的用户下个月也消费了叫做回购,这个定义可以改变
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)
    # 第18个月补充NaN
    status.append(np.NaN)
    return status
#遍历每一个用户每一行
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()

在这里插入图片描述

#计算比例
purchase_b_backshop = purchase_b.sum()/purchase_b.count()
#绘图
purchase_b_backshop.plot()

在这里插入图片描述

五、PowerBI可视化看板设置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 9
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值