用户消费行为分析项目(python)

数据来源:CDNOW网站用户消费购买记录
通过对消费数据的分析,了解网站总体运营状况,分析用户消费行为,并建立用户分层模型
首先导入工具包和数据

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
columns = ['user_id','order_dt','order_products','order_amount']
# 用\s+匹配任意空白符
df=pd.read_csv('CDNOW_master.txt',names=columns,sep='\s+')
df.head()

image.png

数据字段:

  • user_id 用户ID
  • order_dt: 购买日期
  • order_products: 购买产品数
  • order_amount: 购买金额

数据探索

#看下数据类型
df.info()
image.png

时间类型需进行更改

df['order_dt'] = pd.to_datetime(df.order_dt,format='%Y%m%d')
from datetime import datetime
#df['year']=df['order_dt'].dt.year
#df['month']=df['order_dt'].dt.month
df['week']=df['order_dt'].dt.week
df['weekday']=df['order_dt'].dt.weekday  #0-6表示周一到周日
df['ym']=(100*df['order_dt'].dt.year+df['order_dt'].dt.month).astype('str')
df.describe()
image.png
len(df['order_dt'].unique())
len(df['user_id'].unique())

购物总天数有546
消费用户ID合计23570
数据集记录了从1997年1月至1998年6月546天的69659条用户购买CD行为的消费数据

每月消费趋势

# 中文字符和正负号正常显示的设置
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

fig = plt.figure(figsize=(13,15))
ax1=fig.add_subplot(4,1,1)
ax2=fig.add_subplot(4,1,2)
ax3=fig.add_subplot(4,1,3)
ax4=fig.add_subplot(4,1,4)
sns.barplot(df.groupby(['ym'])['order_amount'].sum().index,df.groupby(['ym'])['order_amount'].sum().values,ax=ax1)
ax1.set_title('每月订单总金额变化趋势')
sns.barplot(df.groupby(['ym'])['order_products'].count().index,df.groupby(['ym'])['order_products'].count().values,ax=ax2)
ax2.set_title('每月总订单数变化趋势')
sns.barplot(df.groupby(['ym'])['order_products'].sum().index,df.groupby(['ym'])['order_products'].sum().values,ax=ax3)
ax3.set_title('每月总订购产品数变化趋势')
sns.barplot(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())).index,
            df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())).values,ax=ax4)
ax4.set_title('每月总购买用户数变化趋势')
下载 (1).png

四张图分别是金额,订单数,产品数,用户数by月份变化趋势,可以看到基本趋势是一样的,前三个数据呈增长趋势,并且数据很高,应该是产品导入期。之后进入平稳期,增长趋势较为平稳没有很大变动。

均消费数据

#每月平均消费金额
s=(df.groupby(['ym'])['order_amount'].sum())/
(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())))
fig = plt.figure(figsize=(12,4))
sns.lineplot(s.index,s.values)
#用户每月平均消费次数
s=(df.groupby(['ym'])['user_id'].count())/
(df.groupby(['ym'])['user_id'].apply(lambda x:len(x.drop_duplicates())))
fig = plt.figure(figsize=(12,4))
sns.lineplot(s.index,s.values)
image.png

image.png

这是用户人均消费金额和人均消费次数by月份趋势
前三个月均消费和均次数较低,应属于导入增长期
之后人均消费在50元上下波动,人均次数在1.33次上下波动

星期是否对消费有影响

fig = plt.figure(figsize=(10,6))
sns.barplot(df.groupby(['weekday'])['order_amount'].sum().index,
df.groupby(['weekday'])['order_amount'].sum().values)
image.png

很多网站在周末会有消费高峰,可推出针对性促销活动,从图上看,CDNOW网站周一、周日消费金额稍微高点,但并没有特别突出。因此不用再追加分析。

用户个体消费分析

df['user_id'].value_counts().max()

最高的一名用户有217次购物记录

#用户消费金额,消费次数统计
df.groupby(['user_id'])['order_products','order_amount'].sum().describe()
image.png

用户均购买产品7件,消费金额106元。

#每笔消费记录产品和金额散点图
fig = plt.figure(figsize=(10,6))
sns.scatterplot(df['order_products'],df['order_amount'])
image.png
#每个用户消费记录产品和金额散点图
fig = plt.figure(figsize=(10,6))
sns.scatterplot('order_products','order_amount',data=df.groupby(['user_id']).sum())
image.png

消费产品和消费金额呈线性相关

s=df.groupby(['user_id']).sum()
#每个用户购买金额分布
fig = plt.figure(figsize=(10,6))
sns.distplot(s['order_amount'],kde=False)
image.png
#去除极值
fig = plt.figure(figsize=(10,6))
sns.distplot(s.query('order_amount<500')['order_amount'],kde=False)
image.png

可以看到消费10-20元的用户最对,然后依次是20-30元,30-40元

用户消费行为分析
首次购买时间

s=df.groupby(['user_id'])['order_dt'].min()
fig=plt.figure(figsize=(10,5))
sns.lineplot(s.value_counts().index,s.value_counts().values)
image.png

最后一次购买时间

s=df.groupby(['user_id'])['order_dt'].max()
fig=plt.figure(figsize=(10,5))
sns.lineplot(s.value_counts().index,s.value_counts().values)
image.png

新用户集中在前三个月,同时流失的用户也很多,说明很多用户只消费了一次,之后用户呈现缓慢流失的趋势。

oneshoptime=df.groupby(['user_id']).count().query('order_dt==1')['order_dt'].count()
oneshoptime/len(df['user_id'].unique())

结果:约50%用户只购买一次。

新用户占比
新用户/老用户 判断是否为新用户:用户第一次购买日期记录是否是用当月用户第一次购买日期记录

grouped_month = df.groupby(['ym','user_id']) 
grouped_user = df.groupby(['user_id'])
judge_df = grouped_month['order_dt'].agg(['min']).join(grouped_user['order_dt'].min()) #联结
judge_df['judge']=(judge_df['min']==judge_df['order_dt']) #判断当月第一次是否为总第一次购买
judge_df.head()
image.png
#计算新用户占比
new_old_rate =judge_df.reset_index().groupby('ym')['judge'].apply(lambda x:x.sum()/x.count()) 
fig=plt.figure(figsize=(12,5))
sns.lineplot(new_old_rate.index,new_old_rate.values)
image.png

可以看出前三个月之后,新用户占比降为0

用户分层
RFM模型

#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()
image.png

最近一次消费 (Recency)
消费频率 (Frequency)
消费金额 (Monetary)

# 计算每位用户最后一次消费时间与全部用户最后一次消费时间的差值
rfm['R'] = (rfm['order_dt'].max()-rfm['order_dt']).astype('timedelta64[D]')
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm.head()
image.png
rfm[['R','F','M']].apply(lambda x:x-x.mean()).head()
image.png
# 客户层次的定义,RFM得分可根据业务定义打分,也可以通过K-means聚类模型,得出不同相似程度的数据集,并且根据每一个数据集的特点进行客户定义
def rfm_func(x):
    level = x.apply(lambda x:'1' if x>= 0 else '0')
    '''
    字符串拼接
    111,R>0,是距离平均消费时间要久,R越大,说明没有消费时间越久,
    F>0 M>0,消费次数和金额也是较高的,重要价值客户,依次类推
    '''
    label=level['R']+level['F']+level['M']
    d={
        '111':'重要价值用户',
        '011':'重要保持客户',
        '101':'重要挽留客户',
        '001':'重要发展客户',
        '110':'一般价值客户',
        '010':'一般保持客户',
        '100':'一般挽留客户',
        '000':'一般发展客户'
        
    }
    result=d[label]
    return result
# x - x.mean() (具体真实情况可以修改,不一定需要用均值)   切比雪夫也可以 > 200 极值人工处理掉
rfm['label'] = rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(rfm_func,axis=1)
rfm.head()
image.png
# 计算每层客户R、F、M的和
rfm.groupby('label').sum()
image.png

可以看出,重要保持客户对于消费总金额的占比远大于其他客户的占比,这说明绝大部分收益是由重要保持客户贡献的,只要能保证这部分客户不流失和增加,那么公司收益将得到有力保障

s=rfm.groupby('label')['M'].count()
fig =plt.figure(figsize=(10,10))
plt.pie(s.values,labels=s.index,autopct='%1.1f%%')
image.png

一般挽留用户: 59.7%的用户占比 最近有购买,但消费频率和消费金额不高
措施:采取刺激消费措施,如优惠券和大促
重要保持客户: 19.3%用户占比同时很高的总消费金额 最近无购买,但消费频率和消费金额高
分析:可以回访需求,存在极值影响

用户状态分析

pivot_counts=df.pivot_table(index = 'user_id',
                            columns='ym',
                            values='order_dt',
                            aggfunc='count').fillna(0)
pivot_counts.head()
image.png
#有消费记录1,无消费记录改为0
data_purchase = pivot_counts.applymap(lambda x:1 if x>0 else 0)
data_purchase.head()
image.png

将用户状态分为unreg(未注册)、new(新客)、active(活跃用户)return(回流用户)和unactive(不活跃用户)
思路:

  1. 若本月没有消费
  • 若之前是未注册,则依旧为未注册
  • 若之前有消费,则为流失/不活跃
  • 其他情况,为未注册
  1. 若本月有消费
  • 若是第一次消费,则为新用户
  • 若之前有过消费,则上个月为不活跃,则为回流
  • 若上个月为未注册,则为新用户
  1. 除此之外,为活跃
def active_status(data):
    status = []
    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:
                if status[i-1]=='unreg':
                    status.append('new')
                elif status[i-1]=='unactive':
                    status.append('return')
                else:
                    status.append('active') #其他情况是活跃
            else:
                status.append('new')
    return pd.Series(status, index = pivot_counts.columns) #将列表转为Series
purchase_status = data_purchase.apply(active_status,axis=1)
purchase_status.head()
image.png
purchase_status_sta_T =purchase_status_sta.fillna(0).T
#作用户状态分层面积图
purchase_status_sta_T.plot.area(figsize=(10,6))
image.png

可以直观看到每个月的用户状态变化,以采取拉新,促活,召回等方案。

用户购买周期

order_diff = grouped_user.apply(lambda x:x['order_dt']-x['order_dt'].shift())
order_diff.head(10)
image.png
order_diff.describe()
image.png
#作用户消费周期  跟上次购物时间差距比
fig = plt.figure(figsize=(10,6))
sns.distplot(order_diff.astype('timedelta64[D]').dropna(),bins=20,kde=False)
plt.title('用户消费周期')
image.png

同上次购买日期时间差,说明随着时间拉长,越来越不可能再次来回购

#usertime_diff代表用户消费首次订单与最后一次订单间隔时间
usertime_diff=df['order_dt'].groupby(df['user_id']).max()-df['order_dt'].groupby(df['user_id']).min()
usertime_diff=usertime_diff.reset_index()
usertime_diff=usertime_diff['order_dt'].astype('timedelta64[D]')

#周期分布   同首次消费日期对比
fig = plt.figure(figsize=(10,6))
sns.distplot(usertime_diff[usertime_diff>0],bins=100,kde=False)
image.png

同首次消费时间差比对,出现两个高峰,一个是第一次消费后会容易第二次消费,但时间推移在下降。第二次高峰在一年的时间段,从业务上解释,应该是每次一次新专售卖。

复购率和回购率分析

复购率:自然月内,购买多次的用户占比
回购率:曾今购买过的用户在某一时期内的再次购买占比

#透视表,每月购买次数统计
pivot_counts = df.pivot_table(index = 'user_id',
                             columns='ym',
                             values='order_dt',
                             aggfunc='count').fillna(0)
pivot_counts.head()
image.png
#购买大于1次的赋值1,等于1次的赋值0,小于1次的赋值nan
rebuy_counts = pivot_counts.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
rebuy_counts.head()
image.png
#计算复购率
rebuy_rate=rebuy_counts.sum()/rebuy_counts.count()
fig = plt.figure(figsize=(12,4))
sns.lineplot(rebuy_rate.index,rebuy_rate.values)
plt.title('复购率')
image.png

前三个月大量新用户,复购率较低,hi后复购率稳定在20%至22%之间。

#data_purchsey消费过为1,未消费过为0
data_purchase.head()
def purchase_back(data):
    status = []
    for i in range(17):
        if data[i] == 1: # 本月进行过消费
            if data[i+1] == 1: # 下一月是否进行消费
                status.append(1) #消费为1 回购了
            if data[i+1] == 0:
                status.append(0) # 未消费则为0 没有回购
        else:
            status.append(np.NaN) # 之前没消费则不计
    status.append(np.NaN) # 最后一个月没有判断需要补上
    return pd.Series(status,data_purchase.columns)
#对透视表应用函数purchase_back:
return_counts = data_purchase.apply(purchase_back, axis =1)
return_counts.head()
image.png
#计算回购率

return_rate=return_counts.sum()/return_counts.count()
fig = plt.figure(figsize=(12,4))
sns.lineplot(return_rate.index,return_rate.values)
plt.title('回购率')
image.png

前三个月因为有大量的新用户涌入,但是超过一半的人只消费了一次,所以前三个月回购率比较低,后续月份用户人数比较稳定,回购率也比较稳定,稳定在30%左右,即当月消费人数中有30%左右的用户会在下一个月再次消费

本文探索了用户在cdnow电商网站的消费记录,包括

  1. 总体消费情况(周/月),如金额,次数,平均金额,平均次数
  2. 用户消费情况,用户均消费金额,均消费次数,及其分布
  3. 消费行为分析,首次购买时间,最后一次购买时间,新老用户占比
  4. 用户分层RFM模型的建立
  5. 用户分层状态分析如活跃会员占比
  6. 用户购买周期分析
  7. 复购率和回购率分析
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容