在线音乐零售平台用户消费行为分析

2020-03-17  本文已影响0人  91160e77b9d6

来源:Ustinian - kesci.com
原文链接:CDNow网站用户消费行为分析
点击以上链接👆 不用配置环境,直接在线运行

一、项目背景

CDNow曾经是一家在线音乐零售平台,后被德国波泰尔斯曼娱乐集团公司出资收购,其资产总价值在最辉煌时曾超过10亿美元。本文主要通过分析CDNow网站的用户购买明细来分析该网站的用户消费行为,使运营部门在营销时更加具有针对性,从而节省成本,提升效率。

二、提出问题

对用户进行消费特征分析。分析框架如下:

三.数据处理

3.1 导入数据

#导入常用的库
import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
plt.style.use('ggplot')  #更改设计风格,使用自带的形式进行美化,这是一个r语言的风格
Matplotlib is building the font cache using fc-list. This may take a moment.
#导入源数据
columns = ['用户ID','购买日期','订单数','订单金额']
df = pd.read_table("/home/kesci/input/cdnow5658/CDNOW.txt",names = columns,sep = '\s+')
#默认输出前五行
df.head()

3.2 描述性统计

#数值列的汇总统计信息
df.describe()

describe是描述统计,对用户数据特征进行整体性判断:

3.3 数据处理

#索引,数据类型和内存信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 69659 entries, 0 to 69658
Data columns (total 4 columns):
用户ID    69659 non-null int64
购买日期    69659 non-null int64
订单数     69659 non-null int64
订单金额    69659 non-null float64
dtypes: float64(1), int64(3)
memory usage: 2.1 MB

查看数据类型、数据是否存在空值;原数据没有空值,很干净的数据。接下来我们要将时间的数据类型转化。
当利用pandas进行数据处理的时候,经常会遇见数据类型的问题,当拿到数据的时候,首先要确定拿到的是正确的数据类型,如果数据类型不正确需要进行数据类型的转化,再进行数据处理。

# 将购买日期列进行数据类型转换
df['购买日期'] = pd.to_datetime(df.购买日期,format = '%Y%m%d') #Y四位数的日期部分,y表示两位数的日期部分
df['月份'] = df.购买日期.values.astype('datetime64[M]') 
df.head()

四.用户总体消费趋势分析

# 设置图的大小,添加子图
plt.figure(figsize=(20,15))
# 每月的总销售额
plt.subplot(221)
df.groupby('月份')['订单金额'].sum().plot(fontsize=24) 
plt.title('总销售额',fontsize=24) 
 
#每月的消费次数
plt.subplot(222)
df.groupby('月份')['购买日期'].count().plot(fontsize=24)
plt.title('消费次数',fontsize=24) 

#每月的销量
plt.subplot(223)
df.groupby('月份')['订单数'].sum().plot(fontsize=24)
plt.title('总销量',fontsize=24)

#每月的消费人数
plt.subplot(224)
df.groupby('月份')['用户ID'].apply(lambda x:len(x.unique())).plot(fontsize=24)
plt.title('消费人数',fontsize=24)
plt.tight_layout() # 设置子图的间距
plt.show()

五. 用户个体消费数据分析

5.1 用户消费金额,消费次数的描述统计

# 根据用户id进行分组
group_user = df.groupby('用户ID').sum()
group_user.describe()

从用户角度看,每位用户平均购买7张CD,最多的用户购买了1033张。用户的平均消费金额(客单价)100元,标准差是240,结合分位数和最大值看,平均值才和75分位接近,肯定存在小部分的高额消费用户,这也符合二八法则。

5.2 用户消费金额和消费次数的散点图

#查询条件:订单金额 < 4000
group_user.query('订单金额 < 4000').plot.scatter(x='订单金额',y='订单数')

5.3 用户消费金额的分布图(二八法则)

group_user.订单金额. plot.hist(bins = 20)
#bins = 20,就是分成20块,最高金额是14000,每个项就是700
group_user.query("订单金额< 800")['订单金额'].plot.hist(bins=20)

筛选出了消费金额小于800的用户,我们可以看到:

5.4 用户消费次数的分布图(二八法则)

group_user.query('订单数 < 100').订单数.hist(bins = 40)

六.用户消费周期分析

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

#每个用户的每次购买时间间隔
order_diff = df.groupby('用户ID').apply(lambda x:x['购买日期'] - x['购买日期'].shift())
order_diff.head(10)
用户ID   
1     0        NaT
2     1        NaT
      2     0 days
3     3        NaT
      4    87 days
      5     3 days
      6   227 days
      7    10 days
      8   184 days
4     9        NaT
Name: 购买日期, dtype: timedelta64[ns]
order_diff.describe()
count                      46089
mean     68 days 23:22:13.567662
std      91 days 00:47:33.924168
min              0 days 00:00:00
25%             10 days 00:00:00
50%             31 days 00:00:00
75%             89 days 00:00:00
max            533 days 00:00:00
Name: 购买日期, dtype: object

6.1.2 用户消费周期分布

plt.figure(figsize=(15,5))
plt.hist((order_diff / np.timedelta64(1, 'D')).dropna(), bins = 50)
plt.xlabel('消费周期',fontsize=24)
plt.ylabel('频数',fontsize=24)
plt.title('用户消费周期分布图',fontsize=24);

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

orderdt_min=df.groupby('用户ID').购买日期.min()#第一次消费
orderdt_max=df.groupby('用户ID').购买日期.max()#最后一次消费
(orderdt_max-orderdt_min).head()
用户ID
1     0 days
2     0 days
3   511 days
4   345 days
5   367 days
Name: 购买日期, dtype: timedelta64[ns]
#计算用户的平均生命周期
(orderdt_max-orderdt_min).mean()
Timedelta('134 days 20:55:36.987696')
((orderdt_max-orderdt_min)/np.timedelta64(1,'D')).hist(bins=15)
#因为数据类型是timedelta时间,无法直接作出直方图,所以先换算成数值。
#换算的方式直接除timedelta函数即可,np.timedelta64(1, ‘D’),D表示天,1表示1天,作为单位使用的。
#因为max-min已经表示为天了,两者相除就是周期
#计算所有消费过两次以上的老客的生命周期
life_time = (orderdt_max - orderdt_min).reset_index()
life_time.head()
#用户生命周期分布图
plt.figure(figsize=(10,5))
life_time['life_time'] = life_time.购买日期 / np.timedelta64(1,'D')
life_time[life_time.life_time > 0].life_time.hist(bins = 100, figsize = (12,6))
#去掉0天生命周期的用户之后的用户生命周期的平均值
life_time[life_time.life_time>0].购买日期.mean()
Timedelta('276 days 01:04:31.344216')

七.用户分层

按用户价值分层---RFM模型
为了进行精细化运营,可以利用RMF模型对用户价值指数(衡量历史到当前用户贡献的收益)进行计算,其中
最近一次消费-R:客户最近一次交易时间的间隔。R值越大,表示客户交易发生的日期越久,反之则交易发生的日期越近。
消费频率-F:客户在最近一段时间内交易的次数。F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。
消费金额-M:客户在最近一段时间内交易的金额。M值越大,表示客户价值越高,反之则表示客户价值越低。
根据上述三个维度,对客户做细分

rfm = df.pivot_table(index = '用户ID',
                     values = ['订单金额','购买日期','订单数'],
                     aggfunc = {'订单金额':'sum',
                                '购买日期':'max',
                                '订单数':'sum'})
rfm.head()

# 日期的最大值与当前日期的差值为R
rfm['R'] = (rfm['购买日期'].max() - rfm['购买日期']) / np.timedelta64(1,'D')
rfm.rename(columns = {'订单金额':'M',
                     '订单数':'F'},
          inplace=True)

# 构建rfm模型公式
def get_rfm(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()) / x.std()).apply(get_rfm,axis=1)
rfm.head()
#求和
rfm.groupby('label').sum()
rfm.groupby('label').size()
label
一般价值客户       77
一般保持客户      206
一般发展客户     3300
一般挽留客户    14074
重要价值客户      787
重要保持客户     4554
重要发展客户      331
重要挽留客户      241
dtype: int64

八.用户质量分析

a = df.groupby('用户ID')['购买日期'].agg(['min','max']).reset_index()
new_old = (a['min'] == a['max']).value_counts().values

plt.pie(x = new_old,
       autopct = '%.1f%%',
       shadow = True,
       explode = [0.08,0],
       textprops = {'fontsize' : 11})
plt.axis('equal') 
plt.legend(['仅消费一次','多次消费'])
#每个用户在每月的订单数
pivoted_df=df.pivot_table(index='用户ID',columns='月份',values='购买日期',#pivot_table透视表
                       aggfunc='count').fillna(0)#某些用户在某月没有消费过,用nan表示,这里用0填充

pivoted_df.head()
#转换:消费2次以上记为1,消费1次记为0,消费0次记为NAN  
#applymap针对dataframe所有数据
pivoted_df_transf=pivoted_df.applymap(lambda x: 1 if x>1 else np.nan if x==0 else 0)
pivoted_df_transf.head()
#count统计所有非空数据个数表示总消费用户数,sum计算非0数据的和表示消费两次以上的用户数
df_duplicate =pd.DataFrame(pivoted_df_transf.sum()/pivoted_df_transf.count()).reset_index()
df_duplicate.columns = ['Date', 'DuplicatedRate']
df_duplicate['Date'] = df_duplicate.Date.astype(str).apply(lambda x:x[:-3])

plt.figure(figsize = (15,6))
plt.plot(df_duplicate.Date, df_duplicate.DuplicatedRate)
plt.xlabel('时间', fontsize=24)
plt.ylabel('复购率',fontsize=24)
# plt.ylim(0,1)
plt.title('复购率的变化',fontsize=24)

回购率:是某一个时间窗口内消费的用户,在下一个时间窗口仍旧消费的占比。比如,我1月消费用户1000,他们中有300个2月依然消费,回购率是30%。

#回购率
#每个用户每个月平均消费金额
pivoted_money=df.pivot_table(index='用户ID',columns='月份',values='订单金额',
                             aggfunc='mean').fillna(0)

columns_month=df.月份.sort_values().astype('str').unique()
pivoted_money.columns=columns_month
pivoted_money.head()
#将有消费的记为1,没有消费的记为0
pivoted_purchase=pivoted_money.applymap(lambda x:1 if x>0 else 0)
pivoted_purchase.head()
#如果本月进行消费,下月也进行消费,则记为1;如果下月没有消费,则记为0,若本月没有记为消费,则记为nan
def purchase_return(data):
    status=[]
    for i in range(17):#循环17个月
        if data[i]==1:#若本月消费
            if data[i+1]==1:#下个月也消费
                status.append(1)#就记为1
            if data[i+1]==0:#下个月不消费,就记为0
                status.append(0)
        else:
            status.append(np.nan)
    status.append(np.nan)
    return pd.Series(status, index=columns_month)

pivoted_purchase_return=pivoted_purchase.apply(purchase_return,axis=1)#axis=1表示计算方向在行的方向上,左右运算
pivoted_purchase_return.head()
df_purchase = (pivoted_purchase_return.sum() / pivoted_purchase_return.count()).reset_index()
df_purchase.columns = ['Date', 'PurchaseRate']
df_purchase['Date'] = df_purchase.Date.astype(str).apply(lambda x:x[:-3])

plt.figure(figsize = (15,5))
plt.plot(df_purchase.Date, df_purchase.PurchaseRate)
plt.xlabel('时间', fontsize=24)
plt.ylabel('回购率', fontsize=24)
plt.title('回购率的变化', fontsize=24);
上一篇下一篇

猜你喜欢

热点阅读