2019-02-17 CD网站 用户消费记录分析BY懒猫Leo

数据背景:用户在一家CD网站的消费记录,仅包含4列,不确定是具体是哪类数据
使用工具:jupyter

0、数据准备

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

导入所需包

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

数据仅包含四列
user_id:用户ID
order_dt:购买日期
order_prodects:购买产品数
order_amount:购买金额


df.describe()

数据描述 根据数据面熟结果,75%的客户的购买量在3件以内,购买金额也较低,符合一个普通CD店的消费习惯分布

df['order_dt'] = pd.to_datetime(df.order_dt,format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')
df.head()

将购买日期列的格式更改为日期格式,并添加月份(month)列
![](https://upload-images.jia
nshu.io/upload_images/14624538-62b90c62c24b829e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

  • 每月的消费总金额
  • 每月的消费次数
  • 每月的产品购买量
  • 每月的消费人数
grouped_month=df.groupby('month')
order_month_amount=grouped_month.order_amount.sum()
order_month_amount.head()
plt.style.use('ggplot')
order_month_amount.plot()

消费金额前三个月最高,后续波动下降(原因?)



user_id列不唯一,是用户消费次数数据,或与产品关联的订单数据(仅猜测)

grouped_month.user_id.nunique().plot()
购买用户数趋势
grouped_month.order_products.sum().plot()
购买数量趋势

购买金额、用户数、数量高度关联,可知多数ID购买的金额、数量相差不大,主营CD价格相差不大(推测)

df.pivot_table(index= 'month',
              values = ['order_products','order_amount','user_id'],
              aggfunc = {'order_products':'sum','order_amount':'sum','user_id':'count'})

2.用户个体消费分析

  • 用户消费金额,消费次数的秒速统计
  • 用户消费金额和消费次数的散点图
  • 用户消费金额的分布图
  • 用户消费次数的分布图
  • 用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()

用户平均购买了7张CD,中位数3,部分用户购买了较多数量的CD;用户平均消费106元,中位数43,可验证上条结论

grouped_user.sum().query('order_amount<4000').plot.scatter(x= 'order_amount',y = 'order_products')

去除极值(>=4000)


grouped_user.sum().query('order_products<100').order_products.plot.hist(bins=40)

去除极值(>=100)



从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,使用过滤操作排除异常(利用切比雪夫定理过滤异常值,95%的数据都分布在5个标准差之内)

user_cumsum =grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum() / x.sum())
user_cumsum.reset_index().order_amount.plot()

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

3.用户消费行为

  • 用户第一次消费(首购)
  • 用户最后一次消费
  • 新老客户消费比
    • 多少用户仅消费一次
    • 每月新客占比
  • 用户分层
    • RFM模型
    • 新、老、活跃、回流、流失
  • 用户购买周期(按订单)
    • 用户消费周期描述
    • 用户消费周期分布
  • 用户生命周期(按第一次和最后一次消费) -用户生命周期描述
    • 用户生命周期分布
grouped_user.min().order_dt.value_counts().plot()
grouped_user.max().order_dt.value_counts().plot()

断崖式下跌,看热闹人可能意味着前期活动引流效果明显,但未能留住客户
大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买

user_life = grouped_user.order_dt.agg(['min','max'])
user_life.head()
(user_life['min'] ==user_life['max']).value_counts()

过半客户仅消费一次

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')
rfm.rename(columns = {'order_products':'F','order_amount':'M'},inplace = True)
rfm[['R','F','M']].apply(lambda x:x-x.mean())


def rfm_func(x):
    level = x.apply(lambda x:'1' if x>=1 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)
rfm.groupby('label').sum()
rfm.loc[rfm.label == '重要价值客户','color'] = 'g'
rfm.loc[~(rfm.label == '重要价值客户'),'color'] = 'r'
rfm.plot.scatter('F','R',c=rfm.color)

RFM分析。将用户以三个维度分为8种进行区分,认为可能在一张图中对八种用户进行分类,需缩减坐标轴长度,划分更多颜色等
从RFM 分层可知,大部分用户是重要保持客户,但是这是由于极值的影响,所以 RFM 的划分标准应该以业务为准,也可以通过切比雪夫去除极值后求均值,并且 RFM 的各个划分标准可以都不一样

pivoted_counts = df.pivot_table(index='user_id',
                             columns='month',
                             values='order_dt',
                             aggfunc='count').fillna(0)
pivoted_counts
df_purchase = pivoted_counts.applymap(lambda x :1 if x >0 else 0)
df_purchase.tail()
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:
                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

indexs=df['month'].sort_values().astype('str').unique()
purchase_status = pivoted_counts.apply(lambda x:pd.Series(active_status(x),index=indexs),axis=1)
purchase_status.head(5)

这里遇到个麻烦,可能由于pandas版本是23.4的关系,导出的结果并非表格。所以需要对产生的数据再设置


purchase_status_ct = purchase_status.replace('unreg',np.NaN).apply(lambda x:pd.value_counts(x))
purchase_status_ct
purchase_status_ct.fillna(0).T.plot.area()

新用户仅前期存在。如果数据完整且正常,该网站长期未招新(?不合理)

purchase_status_ct.fillna(0).T.apply(lambda x:x/x.sum(),axis =1)
order_diff = grouped_user.apply(lambda x:x.order_dt - x.order_dt.shift())
order_diff.head(10)
order_diff.describe()

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

(order_diff / np.timedelta64(1,'D')).hist(bins=20)
(user_life['max']-user_life['min']).describe()

u_1=(user_life['max']-user_life['min'])/np.timedelta64(1,'D')
u_1[u_1>0].hist(bins = 40)

4.复购率和回购率分析

  • 复购率
    • 自然月内,购买多次的用户占比(即,购买了两次以上)
pivoted_counts.head()
purchase_r = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x ==0 else 0)
purchase_r.head()
(purchase_r.sum()/purchase_r.count()).plot(figsize = (10,4))
image.png

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

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

推荐阅读更多精彩内容