Python分析:CD网站消费数据

背景:

该项目是某国外CD网站的消费数据,通过分析该网站的消费情况,通过对消费情况分布,从用户消费价值等数据进行分析,为平台创造更多利润,合理辨别广告投放人群,对平台商品销售数据进行分析,根据复购率、回购率、高价值用户等标签,从而进行有针对性的客情管理。数据集来源网上CD国外网站,不包含隐私信息,从数据网站获取。


分析思路.png

数据字段:
user_id:用户ID
order_id:订单日期
order_products:购买产品数
order_amount:购买金额

数据清洗:

1、导入数据集并查看数据类型:
通过pandas导入数据集,源数据缺少列标签需要添加列标签,用\s+表示匹配任意空白符,并查看前5行数据

数据导入:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei'] 

names = ["user_id","order_dt","order_products","order_amount"] 
df = pd.read_table("CDNOW_master.txt",sep = "\s+",names = names)
df.info()
df.head()
查看数据

2、为了后期数据分析需要对主要是针对异常值\缺失值的处理, 此份数据主要两方面:时间转化为标准格式、缺失值处理。

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

通过描述统计判断数据分布情况,存在极大值干扰,可在后面处理根据切比雪夫定理排除极值干扰
df.describe()
image.png
  • 大部分订单只消费了少量商品(平均2.4),有一定极值干扰(最大值99)
  • 用户消费金额比较稳定,平均消费35元,有一定极值干扰(最大值1286)

消费分析

一、根据时间维度划分,查看网站整体的消费水平
group_month = df.groupby('month')
fig,axes = plt.subplots(1,2,figsize=(12,4))
axes0,axes1 = axes.flatten()

# 每月销售金额
group_month.order_amount.agg('sum').plot(ax=axes0)
axes0.set_title('每月消费金额')
axes0.set_ylabel('消费金额')

# 每月销售产品数
group_month.order_products.sum().plot(c="g",ax=axes1)
axes1.set_title('每月销售产品数')
axes1.set_ylabel('销售产品数')

image.png

由图表可知:在网站的消费金额和销售产品数在前3个月较高,并且在3月达到峰值,但是从4月份开始就呈现断崖式下跌(可以初步判断出前三个月应该是网站新上线大/大促吸引大量用户涌入,因为没有具体业务场景就不做展开分析)

group_month = df.groupby('month')
fig,axes = plt.subplots(1,2,figsize=(12,4))
axes2,axes3 = axes.flatten()
# 每月订单数
group_month.order_products.count().plot(c="y",ax=axes2)
axes2.set_title('每月订单数')
axes2.set_ylabel('订单数')

#每月客户数
group_month.user_id.apply(lambda x :len(x.drop_duplicates())).plot(c="r",ax=axes3)   #客户去重:lambda x :drop_duplicates()
axes3.set_title('每月客户数')
axes3.set_ylabel('客户数')

image.png

由图表可知:订单数和客户数同消费金额可产品数变化趋势一致,均在前三个月较高,4月开始下滑,变化趋势一致也是符合常识规律的。4月断崖式的流失需要马上查询出现问题原因,因为缺少相关业务场景就不做具体描述(一般可从数据质量、网站bug、产品质量、竞对情况等分析原因)
注意:每月客户数处理时,因为一个客户可以在某月多次下单,所以需要去重处理

fig,axes = plt.subplots(1,2,figsize=(12,4))
axes0,axes1 = axes.flatten()

# 每月客单价
group_month_pice = group_month.order_amount.sum()/group_month.user_id.unique().apply(lambda x:len(x))
group_month_pice.plot(ax=axes0)
axes0.set_title('每月客单价')
axes0.set_ylabel('客单价')

# 每月件单价
group_month_up = group_month.order_amount.sum()/group_month.order_products.sum()
group_month_up.plot(ax=axes1,c="y")
axes1.set_title('每月件单价')
axes1.set_ylabel('件单价')
image.png
二、根据用户维度划分:查看用户消费水平
group_user = df.groupby('user_id')
group_user_des = group_user[['order_amount','order_products','order_dt',]].agg({"order_amount":"sum",
                                                                              "order_products":"sum",
                                                                              "order_dt":"count"}).describe()
                                                                              
group_user_des.rename(columns = {'order_dt':'消费次数','order_amount':'消费总金额','order_products':'产品购买数(件)'})
image.png

2、用户的消费情况分布

fig,axes = plt.subplots(1,3,figsize=(16,4))
axes0,axes1,axes2 = axes.flatten()

# 用户消费金额的分布情况
group_user.sum().query('order_amount<1200').order_amount.plot.hist(bins=20,ax=axes0)
axes0.set_title('用户消费金额的分布情况')
axes0.set_xlabel('消费金额')
axes0.set_ylabel('人数')

# 用户消费次数的分布情况
group_user.sum().query('order_products<100').order_products.plot.hist(bins=20,color="g",ax=axes1)
axes1.set_title('用户消费次数的分布情况')
axes1.set_xlabel('消费次数')
axes1.set_ylabel('人数')

# 用户购买产品数量的分布情况
group_user.count().query('order_dt<20').order_dt.plot.hist(bins=20,color="y",ax=axes2)
axes2.set_title('用户购买产品数量的分布情况')
axes2.set_xlabel('购买产品数量')
axes2.set_ylabel('人数')
image.png

3、用户消费金额与购买产品数量散点图:查看消费金额和购买数量的关系

#用户消费金额与购买产品数量散点图
group_user.sum().query("order_amount<4000").plot.scatter(x="order_amount",y="order_products",title="'用户消费金额与产品购买数量")
image.png
group_user_cum = group_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum()/x.sum())
group_user_cum.reset_index().order_amount.plot(figsize=(6,4))
plt.title('用户消费金额占比')
plt.xlabel('用户数量')
plt.ylabel('消费金额占比')

image.png

图片可知:用户个体的消费数据来看,基本符合二八法则,少部分高价值用户贡献了大部分的业绩,针对高价值用户进行维护,对于新用户转化成高价值用户,形成良好的用户分成结构体系

三、用户消费行为:

通过前面按时间和用户的维度分析数据,了解了网站的基本情况,现需通过用户的数据对用户的消费行为进行进一步细化分析,通过数据指标、RFM等模型、生命周期等对用户进行分层分析,为后续的用户运营提供数据支撑

1、用户的首购和最后一次消费时间分布

#用户消费行为
group_user = df.groupby('user_id')
fig,axes = plt.subplots(1,2,figsize=(12,4))
axes0,axes1 = axes.flatten()

#用户首购时间
group_user.min().order_date.value_counts().plot(ax=axes0)
axes0.set_title("用户首购时间")
axes0.set_ylabel("人数")

#用户最后一次消费时间
group_user.max().order_date.value_counts().plot(ax=axes1)
axes1.set_title("用户最后一次消费时间")
axes1.set_ylabel("人数")
df.groupby('user_id')
image.png

2、以时间为划分,如果最后一次消费时间等于第一次消费时间,则就是只消费一次的,反之消费多次

user_life = df.groupby('user_id').order_date.agg(["max","min"])
user_life.head()
image.png

3、新老用户的占比

(user_life["min"] == user_life["max"]).value_counts()

group_user_lifecycle = group_user.order_date.apply(lambda x:x.max()-x.min())/np.timedelta64(1,'D')
# 用户生命周期为0天的用户占比
lifecycle_new = len(group_user_lifecycle[group_user_lifecycle==0])/len(df.user_id.unique())
lifecycle_num = len(group_user_lifecycle[group_user_lifecycle>0])/len(df.user_id.unique())
values = [lifecycle_new,lifecycle_num]
label = ['用户仅消费一次','用户消费多次']
fig = plt.figure(figsize=(8,8)) 
plt.pie(values,labels=label,autopct='%1.1f%%')
plt.title('用户消费次数占比')
plt.show()
image.png

4、用户分层:
RFM模型是网点衡量当前用户价值和客户潜在价值的重要工具和手段


image.png

1、通过数据透视,得到用户的消费金额、最近一次消费时间、消费购买产品数。

rfm = df.pivot_table(index = "user_id",
                    values = ["order_amount","order_products","order_date"],
                    aggfunc = {"order_amount":"sum",
                              "order_products":"sum",
                              "order_date":"max"})
rfm.head()

2、为了方便查询,对数据进行重命名管理
购买产品数重新命名为f,消费金额重新命名为m,r为用户最近一次消费的时间距目前(数据取最大值)的时间间隔

rfm['R']=(df.order_date.max() - group_user.order_date.max()) / np.timedelta64(1,"D")                     #去除时间单位
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
rfm=rfm[['R',"F",'M']]
rfm.head()

3、RFM用户分层:通过与平均值的差来判断每个指标的水平。如:m,如果与平均值差值小于0,则该用户总消费小于平均水平,消费金额贡献小将客户分

标签:重要价值(距离上一次消费时间近、购买次数多、消费金额高)、重要保持、重要挽留、重要发展、一般价值、一般发展、一般维持、一般挽留

rfm[["R","F","M"]].apply(lambda x:x-x.mean())

def level_label(x):
    level = x.apply(lambda x:'1' if x> 0 else '0')
    model = level.R+level.F+level.M
    dic = {
          "111":"重要价值客户",
        '101':'重要发展客户',
        "011":"重要维护客户",
        '001':'重要挽留客户',
        '001':'重要挽留客户',
        '110':'一般价值客户',
        '100':'一般发展客户',
        '010':'一般维持客户',
        '000':'一般挽留客户'}
    return dic[model]

rfm['label'] = rfm.apply(lambda x:x-x.mean()).apply(level_label,axis=1)
rfm.groupby("label").sum()                 #按照RFM模型划分,各层次消费金额
rfm.groupby("label").count()               #按照RFM模型划分,各层次有多少人
image.png

按照RFM模型划分,查看到各类标签用户人数分布,一般发展用户人数占比最高


fig,axes = plt.subplots(1,3,figsize=(14,4))
axes0,axes1,axes2 = axes.flatten()
rfm_per=rfm.groupby('label').M.agg(['count','sum']).apply(lambda x :x/x.sum())
rfm_per['avg']=rfm_per['sum'] / rfm_per['count']

rfm_per['count'].plot.bar(ax=axes0)
axes0.set_title('用户分层人数占比')
axes0.set_xlabel('人数占比')

rfm_per['sum'].plot.bar(ax=axes1)
axes1.set_title('用户分层消费金额占比')
axes1.set_xlabel('消费金额占比')

rfm_per['avg'].plot.bar(ax=axes2)
axes2.set_title('用户分层平均消费金额')
axes2.set_xlabel('平均消费金额')
image.png

从图表可知,接近60%的用户属于一般发展客户,但是仅仅贡献消费金额不到20%,平均消费金额仅仅0.25万美元,在各类客户当中平均消费金额属于最低水平;第二大用户群是重要维护客户,人数占比接近20%,贡献的销售额达到63%左右,平均消费金额达3.3万元,这一类客户价值极高,对于这这一类用户重点维护。
4、用户的复购和回购率
复购率:自然月内,用户多次购买占比(当月多次购买)
回购率:曾经购买的用户在某一时间内再次回购占比

pivot_user=df.pivot_table(index='user_id',
                              columns='month',
                             values='order_amount',
                             aggfunc='count').fillna(0)
fig,axes = plt.subplots(1,2,figsize=(12,4))
axes0,axes1=axes.flatten()

fig,axes = plt.subplots(1,2,figsize=(12,4))
axes0,axes1=axes.flatten()

# 用户每月复购率
repeated_purchase = pivot_user.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0)
repeated_purchase.apply(lambda x:x.sum()/x.count()).plot(ax=axes0)
axes0.set_title('每月复购率')
axes0.set_ylabel('复购率')

# 用户每月回购率
def buy_back_choose(x):
    mask = x>0
    label = []
    for i in range(len(x)-1):
        if mask[i] and mask[i+1]:
            label.append(1)
        elif mask[i]:
            label.append(0)
        else:
            label.append(np.NaN)
    return pd.Series(label,index = x.index[:-1])  #index[:-1]去掉最后一列,因为6月首次购买的话就不存在回购

buy_back = pivot_user.apply(buy_back_choose,axis=1)
buy_back.apply(lambda x:x.sum()/x.count()).plot(ax=axes1)
axes1.set_title('每月回购率')
axes1.set_ylabel('回购率')
image.png

前三个月用户基本为新用户的首购,复购率和回购率较低属于正常
四月之后复购率在20%上下波动,回购率25% - 35%波动(波动大小需要看贴合业务场景判读这个数据是否在正常范围波动,因没有想过的标杆数据对比,因此不能判断出这个数据是否正常)

总结:

通过对网站消费数据的获取导入、数据处理与探索,从用户和时间维度的划分,对网站的经营情况进行了了解。在前3个月网站新用户大量涌入,消费总金额和产品购买数都达到最高值,在4月后呈下跌,但是回购率和复购率开始呈上升趋势(是否在正常范围波动需要结合业务场景)。消费总金额也符合28原则,少部分用户贡献了大部分业绩,对于少部分用户购买了大金额产品可能会拉升平均值,需要结合分位数进行判断。根据对用户消费行为、用户生命周期、用户标签的划分,可有效地进行精细化的运营到达提升业绩目的

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

推荐阅读更多精彩内容