python数据分析
库版本
库版本详情
用户在CD网站上的消费记录。本次分析,通过这份数据分析用户的消费情况以及趋势,个体消费情况,以及回购率和复购率。
导入数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#显示图形(自动生成画布,只在jupyter notebook中有效)
%matplotlib inline
columns=['user_id','order_dt','order_products','order_amount']
df=pd.read_table(r'C:/Users/12585/Desktop/CDNOW_master.txt',names=columns,sep='\s+')
- user_id:用户ID
- order_dt:购买日期
- order_prodects:购买产品数
- order_amount:购买金额
查询df内容
df内容各种统计数据
df.describe()
统计数据
其中,count代表总共有多少数据,mean代表平均值,std代表标准差,min代表最小值,max代表最大值。
观察到,order_dt中日期处理成pandas中可以计算的日期格式,如下:
df['order_dt']=pd.to_datetime(df.order_dt,format='%Y%m%d')
#新增一个字段,将order_dt.values的日期转化成当月的第一天
df['month']=df.order_dt.values.astype('datetime64[M]')
再次查询此时df内容
df内容
进行用户消费趋势的分析(按月)
- 每月的消费总金额
- 每月的消费次数
- 每月的产品购买量
- 每月的消费人数
每月的消费总金额
# 按月归类
grouped_month=df.groupby('month')
#每个月消费金额
order_month_amount=grouped_month.order_amount.sum()
#显示部分数据
order_month_amount.head()
消费总金额
#图形采用折线的方式
plt.style.use('ggplot')
#绘制order_month_amount数据的折线图,横轴代表月份,纵轴代表每个月的总金额
order_month_amount.plot()
月销售额趋势图
说明:1997年年初的两个月作用销量陡增,接着断崖式下降,再趋于平稳。但是在接下来这一年的年初却未曾出现这种情况,推测这种销售情况可能由于某种特殊的原因导致的,且不容易复现。
每月的消费次数
grouped_month.user_id.count().plot()
月消费次数
说明:用户消费次数在前两个月较多,后暴跌,从四月份开始逐渐平稳。
每月的产品购买量
grouped_month.order_products.sum().plot()
产品购买量
说明:产品购买量的趋势同消费额和用户消费次数成正相关。
每月的消费人数(去除重复消费的用户,得到每个月的用户数)
# 这里 x 其实是一个数组,是每月分组的用户id,去重后求数组的 len 长度就代表每月的用户数
df.groupby('month').user_id.apply(lambda x:len(x.drop_duplicates())).plot()
每月消费人数
和每月用户消费次数对比可知,顾客多次消费的人数不多,大多数都是一一个月来购买一次,具体什么原因导致有待后面的分析。
用户个体消费分析
即个体的消费情况分析。
- 用户消费金额,消费次数的统计特征
- 用户消费金额和消费次数的散点图
- 用户消费金额的分布图
- 用户消费次数的分布图
- 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
用户消费金额,消费次数的统计特征
#按照用户分组
grouped_user=df.groupby('user_id')
#计算单个用户消费额
grouped_user.sum().describe()
用户消费统计
说明:用户金额平均数在106左右,方差std较大,说明个体用户消费金额波动比较大。订单量的平均值为7单左右,方差std为17左右,波动相对而言较小。
用户消费金额和消费次数的散点图
grouped_user.sum().plot.scatter(x='order_amount',y='order_products')
消费额和购买量的关系图
说明:从图中可知,消费次数和消费额成正相关。大部分客户消费在4000以下,购买数量在200以下。
用户消费金额的分布图
# 直方图,分为20块
grouped_user.sum().order_amount.plot.hist(bins=100)
顾客消费情况
说明:从图中可知,大部分消费额都是比较集中,除去少部分异常值。大部分的消费额都在一千以内,客户购买力较小。下面除去较少的个别客户,得到更细致的更普遍的客户消费额分布趋势。
# 直方图,分为20块,过滤
grouped_user.sum().query('order_products<100').order_amount.plot.hist(bins=20)
顾客消费情况
用户消费次数的分布图
grouped_user.sum().query('order_amount<2000').order_products.plot.hist(bins=20)
顾客购买量直方图
说明:从消费额和消费次数的离散图中可知,存在少数异常值干扰用户消费次数,所以设置一个过滤条件,从离散图中可知,消费额小于2000的状态较普遍。
用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
# cumsum 是求累加值
user_cumsum=grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
# 这里 reset_index() 是为了得到一个自然数的行标签,表示的就是人数,下面的图就可以看出来多少个少占多少百分比
user_cumsum.reset_index().order_amount.plot()
消费额占比
说明 :从图中可知,百分之五十的用户贡献了百分之十五的消费额,消费额前五千的消费总额占据了消费额的百分之六十。
用户消费行为
- 用户第一次消费(首购)
- 用户最后一次消费
- 新老客户消费比
-- 多少用户仅消费一次 - 用户分层
-- RFM模型
-- 新、老、活跃、回流、流失 - 用户购买周期(按订单)
-- 用户消费周期描述
-- 用户消费周期分布 - 用户生命周期(按第一次和最后一次消费) -用户生命周期描述
-- 用户生命周期分布
用户第一次消费(首购)
# 得到最小的日期,然后统计一下各个日期的个数
grouped_user.order_dt.min().value_counts().plot()
user_id
用户最后一次消费
# 得到最大的日期,然后统计一下各个日期的个数,得到最后一次消费的情况,用户流失
grouped_user.order_dt.max().value_counts().plot()
最后一次消费日期统计
新老客户消费比
首先获得第一次和最后一次消费日期。
#新老客消费比
# 得到第一次和最后yc次消费情况,如果 min、max 日期相同,说明只消费了一次
user_life=grouped_user.order_dt.agg(['min','max'])
user_life.head()
首购以及最后一次消费日期
消费一次的客户数量。
(user_life['min']=user_life['max']).value_counts()
一次消费情况
说明:一半用户只消费了一次。
用户分层
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()
原始数据
# 最后日期减去消费日期,此处最后消费日期针对所有用户。
rfm['R']= -(rfm.order_dt - rfm.order_dt.max())/np.timedelta64(1,'D')
# 重命名,也就是 R:最后一次消费距今天数,F:消费总金额 ,M:消费总产品数
# R :消费时间 F:消费金额 M:消费频次
rfm.rename(columns={'order_products':"M",'order_amount':'F'},inplace=True)
rfm.head()
处理后数据
)
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.loc[rfm.label=='重要价值客户','color']='g'
rfm.loc[~(rfm.label=='重要价值客户'),'color']='r'
rfm.plot.scatter('F','R',c=rfm.color)
消费金额和消费次数散点图
rfm.groupby('label').sum()
RFM模型
用户生命周期 新客,活跃,回流,流失(一段时间不消费,或者不活跃)
# 数据透视, userid为索引,月为列,求每月的消费次数,这里填充了
pivoted_counts=df.pivot_table(index='user_id',
columns='month',
values='order_dt',
aggfunc='count').fillna(0)
pivoted_counts.head()
用户月消费次数统计
# 转变一下消费,有消费为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:活跃
df_purchase.apply(lambda x:pd.Series(active_status(x),index=df_purchase.columns),axis=1)
purchase_stats.head()
每月用户状态
# 这里把未注册的替换为空值,这样 count 计算时不会计算到
# 得到每个月的用户分布
purchase_stats_ct=purchase_stats.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct
统计每月各类用户数量
# 又把null 值填充为0
purchase_stats_ct.fillna(0).T
# 绘制了一个面积图,蓝色是注册用户,一开始有后面没有用户进来
purchase_stats_ct.fillna(0).T.plot.area()
各类用户图形表示
复购率和回购率分析
复购率
- 自然月内,购买多次的用户占比(即,购买了两次以上)
回购率
- 曾经购买过的用户在某一时期的再次购买的占比(可能是在三个月内)
#查看每个用户每月购买次数
pivoted_counts.head(10)
消费情况
# 区分一个,和一个以上的情况,以便于计算复购率,大于1为1,等于0为NaN,其它为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))
复购率
# 需要使用函数来判断是否回购:当月消费过的用户下个月也消费了叫做回购,这个定义可以改变
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
# 一行行的传递过去
purchase_b=df_purchase.apply(lambda x:pd.Series(purchase_back(x),index=df_purchase.columns),axis=1)
purchase_b.head()
回购情况
# 求得回购率,回购的次数处于总购买次数
(purchase_b.sum()/purchase_b.count()).plot(figsize=(10,4))
回购率