python数据分析项目:用户消费行为分析

为了创造更多利润、实现数据驱动运营,某CD网站拟对18个月以来的近7万条消费数据进行分析。具体的研究思路如下:


【思维导图】用户消费行为分析(python).png

1、数据获取与探索

1.1数据获取

数据来源:CDNow网站的用户购买明细(不包含隐私信息)。
数据格式:转化成.csv格式。
时间跨度:18个月(199701-199806)。
字段:用户ID,购买日期,购买数量,购买金额四个字段。

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

1.2数据导入

#导入相关库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot') # 更改设计风格
import seaborn as sns
plt.rcParams['font.sans-serif']=['SimHei'] #用来显示中文标签
df0=pd.read_csv('D:CDNOW_master.csv',names=['user_id','order_dt','order_products','order_amount'],sep='\s+')
df0.head()

#读取csv,此处用的是  names=  而非  columns=
1.PNG

2、数据预处理

2.1数据描述

df0.info()
#使用 .info() 查看数据类型及缺失情况。
2.PNG

2.2数据清洗

缺失值:本次数据,无缺失值,故无需对缺失值处理;
数据类型:购买日期的数据类型是int,为方便后面数据处理,在这先将其转化成datetime格式;

新增['month']列,便于后续按月分析。

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

重新查看,此时的时间列已转换为正常格式。


3.PNG
df0.describe()
#使用 .describe() 查看数据分布及极值情况。
4.png

由上图可知,

  • order_products列:用户平均每笔订单购买2.4个商品,标准差在2.3,故有一定的波动性。中位数在2个商品,75分位数在3个商品,说明绝大部分订单的购买量都不多。最大值为99,受极值影响。
df0['order_amount'].plot.hist(bins=200)
5.PNG

正偏态分布(也称右偏态分布):频数分布的高峰向左偏移,长尾向右延伸;【峰左移,右偏,正偏 偏度大于0】
负偏态分布(也称左偏态分布):频数分布的高峰向右偏移,长尾向左延伸。【峰右移,左偏,负偏 偏度小于0】

  • order_amount列:用户的消费金额受极值影响,呈正偏态分布(也称右偏态分布)【中位数50%(25元) < 平均值mean(35元)】

3、数据分析

接下来我们用之前清洗好的字段进行数据分析。

3.1消费趋势分析(按每个月分析)

3.1.1每月的消费总金额

grouped_month=df0.groupby('month') # 按月分组

grouped_month['order_amount'].sum()
6.PNG
grouped_month['order_amount'].sum().plot()

7.PNG

本次数据时间跨度为18个月(199701-199806),消费金额总体描述分析如下:由上图可知,消费金额在前三个月 {1997-01-01:299060.17,1997-02-01:379590.03,1997-03-01:393155.27}增长至峰值,在3-4月出现断崖式下滑,后续消费金额较为平稳,但有轻微下降趋势。原因分析如下:

  • 前3个月的增长,推测是由于CD网站的促销活动;
  • 而3-4月的异常下跌,推测是受促销活动结束的影响较大。同时由于暂缺乏详细数据,所以未对3-4月的异常收入下滑进行实际细剖与分析。收入异常下跌的其他原因整理如下:

比如:
(1)【竞争对手】方面,近期是否出现其他竞争与替代产品;
(2)【渠道】方面,可从用户数量、渠道收入(环比、下滑)两个维度来评判渠道质量,可从查看不同渠道的等数据指标。不同渠道如web端直接访问、搜索引擎渠道、第三方合作渠道等,进一步定位是否是渠道推广有问题;
(3)【用户来源】方面,从新老客构成、流失客户的特征、聚类与分类等角度;
(4)【CD产品体验】方面,针对客户的评价、网站评论及主动与用户沟通收集反馈意见问题,对产品体验进行分析,如是包装不流行、价格没优惠了等等进行优化。
(5)【用户体验流程】方面,通过埋点数据查看流失客户在网站上的行为数据:启动次数、停留时长、流程不畅(如流失客户大部分在支付环节未完成支付,那么就需要回访部分客户、进行优化改进);

3.1.2每月的消费订单数

grouped_month['user_id'].count().plot()
8.PNG

前三个月消费订单数在10000笔左右,后续月份的平均则在2500笔。

3.1.3每月的产品购买量

grouped_month['order_products'].sum().plot()
9.PNG

前三个月产品购买数在20000以上,后续月份的产品购买量在6000~8000左右 。

3.1.4每月的消费人数

grouped_month['user_id'].apply(lambda x:len(x.unique())).plot()

#法二:grouped_month['user_id'].apply(lambda x:len(x.drop_duplicates())).plot()
10.PNG

前三个月每月的消费人数在8000-10000之间,后续月份平均消费人数在2000人不到

3.1.5直接利用数据透视表分析消费金额、消费次数、产品购买量

上述消费趋势的分析可以通过数据透视表分析(不建议数据透视表进行去重操作)

pivot_df0=df0.pivot_table(index='month',values=['order_amount','user_id','order_products'],aggfunc={
    'order_amount':'sum',
    'user_id':'count',
    'order_products':'sum'   
    })
pivot_df0.head()
#此处是values=  而非columns=
#数据透视表进行去重操作比较麻烦,不建议
11.PNG
pivot_df0.plot()
12.PNG

本章小结——
趋势分析:总体来看,消费总金额、消费次数、产品购买量、消费人数的趋势想似:均先上升、下跌、趋于平稳并下降。
可以看出网站的流失用户在增加,采用开源(拉新)节流(留存)的运营方式,来增加销售收入。

3.2用户个体消费能力分析(按每个用户分析)

上一部分是按月分析,主要看趋势;本部分按用户个体分析,来看消费能力。

3.2.1用户消费金额、消费数量的描述统计

grouped_user=df0.groupby('user_id')
grouped_user.sum().describe()
13.PNG
  • 【order_products数量】用户平均购买了7张CD,但中位数只有3,说明小部分用户购买了大量的CD
  • 【order_amount金额】用户平均消费106元,中位数为43,判断同上,有极值干扰

消费、金融和钱相关的数据,基本上都符合二八法则,小部分的用户占了消费的大头

3.2.2用户消费金额和消费数量的散点图

grouped_user.sum().plot.scatter(x = "order_amount",y = "order_products")
14.PNG
##画图前,用query先筛选:.query("order_amount<4000")【df用query,series不能用】

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

3.2.3用户消费金额的分布图

grouped_user['order_amount'].sum().plot.hist(bins=200)
#bins是分组,分200个组
16.PNG
  • 从直方图可知,用户消费金额,绝大部分呈现集中趋势
  • 部分异常值干扰了判断。可以使用【切比雪夫定理】过滤异常值,计算95%(mean ± 5std)的数据的分布情况。则:
  1. order_products为:7+17X5=92;
  2. order_amount为106+5X241=1311。
grouped_user.sum().query('order_products<92').order_products.plot.hist(bins=20)
# .query作用在df上
17.PNG
grouped_user.sum().query('order_amount<1311').order_amount.plot.hist(bins=80)
18.PNG

3.2.4用户累计消费金额占比(百分之多少的用户占了百分之多少的消费额)

user_cumsum=grouped_user.sum().sort_values('order_amount', ascending=False).apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().order_amount.plot() #reset_index()去掉索引 ,才能作图

#grouped_user['order_amount'].sum().sort_values()
##df和series都可以调用.sort_values()方法
##list需要转变成df才可以作图
#方法二:(grouped_user['order_amount'].sum().sort_values().cumsum()/2500315.63).reset_index().order_amount.plot()
19.PNG

按用户消费金额进行降序排列,由图可知,共计约25000个用户:

  • 20%(约5000)的客户贡献了70% 的消费额度,近似符合二八定律
  • 50%的客户贡献了90% 的消费额度(即剩余50%的客户仅贡献10% 的消费额度)。

启发,只要维护好这5000个用户(占比20%)就可以把业绩KPI完成70%,如果能把5000个用户运营的更好就可以占比更高。

3.3用户消费行为分析

通过以上基本数据描述分析可以清楚该网站整体的消费趋势和用户消费能力,现在进一步挖掘用户消费行为数据,通过RFM模型、生命周期等方法对用户进行分层,为后续运营管理提供依据。

3.3.1用户第一次消费(首购)

首购可以进一步依渠道划分,衡量不同渠道的差异性,从而量化渠道能力,为后期渠道优化提供依据。

grouped_user.min().order_dt.value_counts().plot()
20.PNG

用户第一次购买分布,集中在前三个月(1997年1-3月);其中,在2月11日至2月25日有一次剧烈波动

3.3.2用户最后一次消费

grouped_user.max().order_dt.value_counts().plot()
21.PNG
  • 用户最后一次购买分布(1997年1月-1998年6月)比第一次分布(1997年1-3月)广;大部分最后一次购买,集中在前三个月,说明很多用户购买了一次后就不再进行购买。
  • 随着时间的增长,用户最后一次购买数略微增加。原因分析如下:
  • 可能因网站体验确实不好而流失,故有必要进行流失原因分析,并进一步建立预警系统。
  • 也可能与CD产品属性(消费频次较低 / 购买周期较长)有关,此时相对于此网站的用户流失,用户拉新与首购才是最核心的指标。

3.3.3新老客消费比

3.3.3.1多少用户仅消费了一次?

user_life = grouped_user.order_dt.agg(["min","max"])
##groupby后使用多个聚合函数用agg而非apply
user_life.head()
22.PNG
(user_life['min']==user_life['max']).value_counts()

23.PNG

有一半用户,就消费了一次,可以通过定期发送邮件、信息等方式进行用户唤回。

3.3.3.2每月新客户占比?

grouped_um=df0.groupby(['month','user_id']).order_dt.agg(['min','max']) 
#按month分组下的userid分组,求每月每个用户的最早购买日期和最晚消费日期

grouped_um['new'] = (grouped_um['min'] == grouped_um['max'] )                
# ['new']为新增列:若为新用户,则为 True。 

grouped_um.reset_index().groupby('month').new.value_counts()
24.PNG
grouped_um.reset_index().groupby('month').new.count()
#计算出每个月下订单的客户总数
25.PNG
grouped_um1=grouped_um.reset_index().groupby('month')
grouped_um2=grouped_um1['new'].apply(lambda x:x.value_counts()/x.count()).reset_index()
grouped_um2[grouped_um2['level_1']].plot(y = 'new', x ="month")
#利用布尔值筛选True 作图
26.PNG

由图可知,1997年1-4月新用户数量由90%跌落至80%以下;之后几个月的新用户量保持在80~82%区间。

3.3.3.4.用户分层 -RFM

RFM是一个经典的用户分类模型,模型利用通用交易环节中最核心的三个维度——最近消费(Recency)、消费频率(Frequency)、消费金额(Monetary)细分用户群体,从而分析不同群体的用户价值,最终达到精准营销。

3.3.3.4.1基于规则(均值)划分的RFM

RFM从3个维度、分2个等级(均值)得到8类用户分层。

rfm=df0.pivot_table(index='user_id',values=['order_products','order_amount','order_dt'],
               aggfunc={'order_dt':'max',
                           'order_amount':'sum',
                           'order_products':'sum'})
rfm.head()
27.PNG
rfm['R'] = -(rfm['order_dt']-rfm['order_dt'].max())/np.timedelta64(1,'D')
#/np.timedelta64(1,"D") 换成浮点数
rfm.rename(columns={'order_products':'F','order_amount':'M'},inplace=True)
#inplace 代表 是否覆盖原始二维表
def rfm_func(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()).apply(rfm_func,axis = 1)
#axis = 1是逐行应用  #默认axis=0,即表示apply函数逐列应用。
rfm.head()

通过RFM模型,把用户分为8个类别,分别给用户打标签、将客户分为重要价值、重要保持、重要挽留、重要发展、一般价值、一般保持、一般保留、一般发展8类客户。


28.PNG
rfm.groupby('label').count().sort_values('order_dt',ascending=False)
29.PNG

从RFM分层可知,本网站的大部分用户为一般挽留客户(可适当放弃这部分低价值客户、也可进一步提高活跃度)、重要保持客户(企业优质的客户群,采用会员制运营)。具体运营策略依据参照如下:

【'111':'重要价值客户'】该类用户与企业交易频繁、交易金额大,但长时间没有二次消费,存在流失风险。——企业利润的潜在来源,采用用户唤回运营。
【'011':'重要保持客户'】该类用户与企业交易频繁、交易金额大,且最近一次消费时间间隔短,实际贡献价值很高。——企业优质的客户群,采用会员制运营。
【'101':'重要挽留客户'】该类用户交易金额大,但交易并不频繁、且长时间没有二次消费,也存在流失风险。——有很高潜在价值的客户,可针对性研究用户特征,通过特定营销手段吸引用户提高购买频率。
【'001':'重要发展客户'】该类用户最近消费间隔短、购买金额大,但交易不频繁。——有很高潜在价值的客户。对该类用户展开专题研究,重点分析用户群特征,以便在下次促销时向合适的用户传递合适的CD产品,转化为留存客户。
【'110':'一般价值客户'】该类用户购买频率高,但长时间没有交易,而且购买金额较低,企业已很难获取更多利润。
【'010':'一般保持客户'】该类用户最近交易时间间隔短、购买频率高,属于活跃用户,但由于累计购买金额较少,消费能力有限,属于企业的一般维持用户。
【'100':'一般挽留客户'】低价值客户。
【'000':'一般发展客户'】 无法立即给企业带来较大利润的用户。

针对上述8类用户分层情况,可初步制定三大运营策略

  • 提高活跃度:注重提升一般客户、低价值客户的活跃度,将其转化为优质客户。
  • 提高留存率:与重要价值、重要挽留客户互动,提高这部分用户的留存率。
  • 提高付费率:维系重要保持客户、重要发展客户的忠诚度,保持网站的良好收入。
    具体手段包括搭建会员体系、会员分类管理与升级、积分兑换、发放折扣券等。
rfm.loc[rfm.label=='重要价值客户','color'] = 'g'     #green
rfm.loc[~(rfm.label=='重要价值客户'),'color'] = 'r'  #red
rfm.plot.scatter('F','R',c=rfm.color)

30.PNG

上图为R(最近消费)与F(消费次数)的散点图,可以看出重要价值用户的R大F大,表示消费次数多且消费时间远。可采用重点唤回方式进行运营。

3.3.3.4.2基于聚类(Kmeans)划分的RFM

为了避免划分用户群体过多(RFM从3个维度、分2个等级得到8类用户分层的数据立方),可能导致针对性的营销成本负担上升;下面将通过聚类方法,基于RFM模型划分成4类用户,更快实现后期用户管理。

rfm_julei=rfm.loc[:,['R','F','M']]
rfm_julei.head()
基于聚类(Kmeans)划分的RFM 1.PNG
#聚类之前先作图观察
plt.figure()
rfm_julei.plot(x='R',y='F',c='M',cmap='viridis_r', s=10,kind='scatter')
聚类之前先作图观察
from sklearn.preprocessing import StandardScaler   ##均值为0,方差为1的标准正态分布

st=StandardScaler()
st = st.fit(rfm_julei)
st_rfm_julei = st.transform(rfm_julei)

from sklearn.preprocessing import MinMaxScaler  ## 0-1 X=mm_rfm_julei.astype('float32')

mm=MinMaxScaler()
mm = mm.fit(rfm_julei)
mm_rfm_julei = mm.transform(rfm_julei)
#轮廓系数(silhouette coefficient)【样本i的簇内不相似度a i 和簇间不相似度b i ,定义样本i的轮廓系数】:
#选择使轮廓系数较大所对应的k值——轮廓系数范围在[-1,1]之间。该值越大,越合理。
#si接近1,则说明样本i聚类合理;
#si接近-1,则说明样本i更应该分类到另外的簇;
#若si 近似为0,则说明样本i在两个簇的边界上

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np

X=mm_rfm_julei.astype('float32')
for n_clusters in [2,3,4,5,6,7]:
    n_clusters = n_clusters
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(8, 4)
    ax1.set_xlim([-0.1, 1])
    ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])
    clusterer = KMeans(n_clusters=n_clusters, random_state=10).fit(X)
    cluster_labels = clusterer.labels_
    silhouette_avg = silhouette_score(X, cluster_labels)
    print("For n_clusters =", n_clusters,"The average silhouette_score is :", silhouette_avg)
    sample_silhouette_values = silhouette_samples(X, cluster_labels)
    y_lower = 10
    for i in range(n_clusters):
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i
        color = cm.nipy_spectral(float(i)/n_clusters)
        ax1.fill_betweenx(np.arange(y_lower, y_upper)
                          ,ith_cluster_silhouette_values
                          ,facecolor=color
                          ,alpha=0.7)
        ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
        y_lower = y_upper + 10
    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
    ax1.set_yticks([])
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    ax2.scatter(X[:, 0], X[:, 1],marker='o',s=8,c=colors)
    centers = clusterer.cluster_centers_
    # Draw white circles at cluster centers
    ax2.scatter(centers[:, 0], centers[:, 1], marker='x',c="red", alpha=1, s=200)

    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")
    plt.suptitle(("Silhouette analysis for KMeans clustering on sample data "
                  "with n_clusters = %d" % n_clusters),
                 fontsize=14, fontweight='bold')
    plt.show()
显然,归一化预处理后,当n=2时,轮廓系数取最大值0.79,仅从模型聚类效果来讲分2类合适

显然,归一化预处理后,当n=2时,轮廓系数取最大值0.79,仅从模型聚类效果来讲分2类合适;而标准正态化预处理后显示,分4类的轮廓系数最大,达0.6964(但2-7类的轮廓系数整理差别波动不大)

#利用“手肘法”去判定分多少类比较合适
import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
% matplotlib inline

SSE_st = [] # 存放  标准正太  结果的误差平方和
SSE_mm = [] # 存放  归一化后  结果的误差平方和
for k in range(1,8): 
    kmodel = KMeans(n_clusters=k) # 构造聚类器 
    kmodel.fit(st_rfm_julei.astype('float32'))  ## 标准正太:st_rfm_julei.astype('float32')
    SSE_st.append(kmodel.inertia_)  # .inertia_获取聚类准则的总和
    print('SSE_st',k,SSE_st)
    
    kmodel = KMeans(n_clusters=k) # 构造聚类器 
    kmodel.fit(mm_rfm_julei.astype('float32'))  ## 0-1归一化:mm_rfm_julei.astype('float32')
    SSE_mm.append(kmodel.inertia_)  # .inertia_获取聚类准则的总和
    print('SSE_mm',k,SSE_mm)

fig = plt.figure(figsize=(10, 4))
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)

X = range(1,8)
ax1.set_xlabel('k')
ax1.set_ylabel('SSE_st')
ax1.plot(X,SSE_st,'o-')

plt.figure()
X = range(1,8)
ax2.set_xlabel('k')
ax2.set_ylabel('SSE_mm')
ax2.plot(X,SSE_mm,'o-')

plt.show()
归一化比标准正太更容易看出,手肘部对于的k值为2(曲率最高),故对于这个数据集的聚类而言,最佳聚类数应该选2。
查看聚类中心及2类样本占比
#导入机器学习及三维作图相关库
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d import proj3d
import pylab
##作图:RFM模型中的三个变量在空间中的分布特征
ax=plt.subplot(111,projection='3d')
ax.scatter(mm_rfm.iloc[:,0],mm_rfm.iloc[:,1],mm_rfm.iloc[:,2],c=label)
ax.set_xlabel('R')
ax.set_ylabel('F')
ax.set_zlabel('M')

plt.show()
经归一化预处理:基于聚类(Kmeans)划分的RFM

【仅对比展示用】未经归一化预处理:基于聚类(Kmeans)划分的RFM
crowd_1=mm_rfm.loc[mm_rfm['label']==0]
crowd_1_num=len(crowd_1.index) #第1类用户人数

crowd_2=mm_rfm.loc[mm_rfm['label']==1]
crowd_2_num=len(crowd_2.index) #第2类用户人数

print('第1类用户特征({}人):\n{}\n第2类用户特征({}人):\n{}\n'.format(crowd_1_num,crowd_1.mean(),crowd_2_num,crowd_2.mean()))
基于聚类(Kmeans)划分的RFM
ax=plt.subplot(321)
plt.xlabel('crowd_1_R')
plt.ylabel('crowd_1_M')
plt.scatter(crowd_1.iloc[:,0],crowd_1.iloc[:,2],s=1)

ax=plt.subplot(322)
plt.xlabel('crowd_1_F')
plt.ylabel('crowd_1_M')
plt.scatter(crowd_1.iloc[:,1],crowd_1.iloc[:,2],s=1)

ax=plt.subplot(323)
plt.xlabel('crowd_2_R')
plt.ylabel('crowd_2_M')
plt.scatter(crowd_2.iloc[:,0],crowd_2.iloc[:,2],s=1)

ax=plt.subplot(324)
plt.xlabel('crowd_2_F')
plt.ylabel('crowd_2_M')
plt.scatter(crowd_2.iloc[:,1],crowd_2.iloc[:,2],s=1)

plt.show()
基于聚类(Kmeans)划分的RFM

第1类用户人数少,占比超30%(7422/23570),其消费时间间隔短,频率较高,且金额较大,是高价值用户,重要管理;
第2类用户人数次多,占比70%,其消费时间间隔较长,频率较低,且金额较少,是价值次低的用户,做次要管理;

3.3.3.5.用户分层:新、活跃、回流、消失

参考漏斗模型,针对每个用户,按18个月内的每个月对用户情况进行分类,即新用户、活跃用户、回流用户、流失用户。
通过下面的数据透视表即可得到每个用户每个月的购买情况,从而进行转化分析。

pivoted_counts=df0.pivot_table(index='user_id',
                               columns = 'month',
                               values='order_dt',
                               aggfunc='count').fillna(0)
pivoted_counts.head()
31.PNG
df_purchase=pivoted_counts.applymap(lambda x: 1 if x>0 else 0)#简化模型,只需判断是否存在 即 1与0
#此处用.applymap()
df_purchase.head ()  
32.PNG
def active_status(data):
    status=[]
    for i in range(18): #12+6个月
        #若本月没有消费
        if data[i]==0:
            if len(status)>0:
                if data[i-1]==0:
                    if status[i-1]=='unreg':
                        status.append('unreg')
                    else:
                        status.append('unactive')
                else:
                    status.append('unactive')
            else:
                status.append('unreg')
                    
           
        #若本月消费
        else:
            if len(status)==0:
                status.append('new')
            else:
                if data[i-1]==0:
                    if status[i-1]=='unreg':
                        status.append('new')
                    else:
                        status.append('return')
                else:
                    status.append('active')
    
    return pd.Series(status,index=df_purchase.columns)
#或者index = pivoted_counts.columns
  
purchase_stats=df_purchase.apply(lambda x:active_status(x),axis=1)
purchase_stats.head()
33.PNG

若本月无消费(即为0)

  • 之前有记录
    1.上条记录为无消费(即为0)
    1.1若为未注册,则为未注册
    1.2若为不活跃,则为不活跃
    2.上条记录为有消费(即为1),则为不活跃
  • 之前无记录,则为未注册

若本月有消费(即为1)

  • 之前无记录,则为新客户
  • 之前有记录
    3.上条记录为无消费(即为0)
    3.1若为未注册,则为新客户
    3.2若为不活跃,则为回流客户
    4.上条记录为有消费(即为1),则为活跃客户
purchase_stats_ct=purchase_stats.replace('unreg',np.NaN).apply(lambda x :x.value_counts())
purchase_stats_ct
#也可以使用pd.value_counts(x)   #注意unreg区别
34.PNG
purchase_stats_ct.fillna(0).T.apply(lambda x : x/x.sum(),axis=1)
35.PNG

由上表可知,每月的用户消费状态变化

  • 活跃用户(持续消费用户),对应的是消费运营的质量
  • 回流用户(之前不消费本月才消费),对应的是唤回运营
  • 不活跃用户,对应的是流失
  • 新客户,对应渠道与市场
purchase_stats_ct.fillna(0).T.apply(lambda x : x/x.sum(),axis=1).plot(figsize = (5,5))
36.PNG
purchase_stats_ct.fillna(0).T.plot.area()  #.plot.area()面积图

37.PNG

active:活跃用户越来越少,说明运营的质量在降低,可能是用户体验不好、也可能是竞争加剧;
new:新用户前三个月之后就显著降低,说明市场和渠道部门需要加大拉新;
return:回流客户多,说明唤回运营(促销)起效;
unactive:不活跃用户正在增加,说明存在用户流失(也可能因为CD购买周期较长的原因);

3.3.3.6.用户购买周期(按订单)

#用户消费周期描述
#用户消费周期分布

order_diff = grouped_user.apply(lambda x: x.order_dt-x.order_dt.shift()) 
#体会df.apply(列.func)
order_diff.head(15)
#shift() 为 两个日期错行相减
##目的是求 时间差值 
38.PNG
order_diff.describe() #对时间差值进行描述统计分析(会自动过滤空值)
39.PNG

CD产品的平均购买周期是2个月(均值为68天)

(order_diff/np.timedelta64(1,'D')).plot.hist(bins=20)
#订单周期呈指数分布  
40.PNG

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

#用户生命周期描述
#用户生命周期分布

user_life=grouped_user.order_dt.agg(['min','max'])
user_life.head()
41.PNG
(user_life['max']-user_life['min']).describe()

42.PNG

用户生命周期的均值为134天,但一半的用户生命周期仅0天【显然受只购买一次的用户影响比较厉害,下面将分排除与否分别作图

#不排除:
((user_life['max']-user_life['min'])/np.timedelta64(1,'D')).plot.hist(bins=40)
#((user_life.apply(lambda x :x.max()-x.min(),axis=1))/np.timedelta64(1,'D')).plot.hist(bins=20)
43.PNG
user_life['差值']=(user_life['max']-user_life['min'])/np.timedelta64(1,'D')
user_life.head()
44.PNG
#排除(一半生命周期仅0天的用户):
user_life.query('差值>0')['差值'].plot.hist(bins=40)

## .query('差值>0')
45.PNG
#排除(一半生命周期仅0天的用户):
##法二

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

#不用  .reset_index()[0]  也可以作图
46.PNG

3.4复购率和回购率分析

通过对复购率、回购率进行分析,可找到较高的月份进行细化分析,观察较高的产品(产品特征),以及较高的用户(用户特征),可根据这些细化的数据做产品的爆款营销、推广、个性化营销等。

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

pivoted_counts.head()
47.PNG

3.4.1复购率

purchase_r = pivoted_counts.applymap(lambda x :1 if x>1 else np.NaN if x==0 else 0)
purchase_r.head()
48.PNG
(purchase_r.sum()/purchase_r.count()).plot(figsize = (10,4))  
#sum()对于1的求和,count()过滤掉np.Nan
##使用元组的方式赋值
49.PNG

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

3.4.2回购率

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)
    status.append(np.NaN)
    ##需注意 语句中的是 第一位与第二位相比 输出结构放第一位, 
    #循环17次,17/18的判断结束后,输出的status仅17个,需补上最后一个
    return pd.Series(status,index=df_purchase.columns)

#1代表 本月购买及下个月购买 ,sum()可计算 ,
#0代表 本月购买,下个月未购买 count()可计算总人数, 
#回购率: 本月及下月购买/本月购买人群
purchase_b=df_purchase.apply(lambda x:purchase_back(x),axis=1)
purchase_b.head()
##此处的透视表为何用的方法不是.applymap  而是  .apply()
50.PNG
(purchase_b.sum()/purchase_b.count()).plot(figsize=(20,4))##使用元组的方式赋值
51.PNG

从图中可以看出,用户的回购率约30%左右(高于复购率),说明客户忠诚度表现较好。

总结

本研究运用多种分析方法,如趋势分析、用户行为分析、生命周期分析、RFM模型分析、聚类分析、漏斗转化分析等,较全面刻画了CD网的运营现状。主要结论有:
(1)总体来看,每月的消费总金额、消费次数、产品购买量、消费人数趋势相似:均先上升、再下跌、最后趋于平稳并下降。初步推断3-4月的下跌是由于促销活动结束,导致异常。
(2)20%(约5000)的客户贡献了70% 的消费额度,近似符合二八定律。故只要维护好这5000个用户(占比20%)就可以把业绩KPI完成70%,如果能把5000个用户运营的更好就可以占比更高。
(3)每月新客占比状态良好:1997年1-4月新用户数量由90%跌落至80%以下;之后几个月的新用户量保持在80~82%区间。但有一半用户,就消费了一次,可以通过定期发送邮件、信息等方式进行用户唤回。
(4)两种RFM划分模型(均值、聚类)均显示:低价值客户数量占比较大,可做次要管理(进一步提升活跃度);而对高价值客户,尤其是聚类分析得出的171个客户,更要突出精准营销与会员管理手段,进一步提升留存率和付费率。
(5)用户转化分析:
active:活跃用户越来越少,说明运营的质量在降低,可能是用户体验不好、也可能是竞争加剧;
new:新用户前三个月之后就显著降低,说明市场和渠道部门需要加大拉新;
return:回流客户多,说明唤回运营(促销)起效;
unactive:不活跃用户正在增加,说明存在用户流失(也可能因为CD购买周期较长的原因);
(6)生命周期分析:用户生命周期的均值为134天,但一半的用户生命周期仅0天;而CD产品的平均购买周期是2个月(均值为68天)。
(7)用户的复购率稳定在20%;回购率约30%左右(高于复购率),说明客户忠诚度表现较好。

网站可根据以上结论,为网站后续的运营、营销等方面制定策略,并根据反馈数据进一步复盘分析与优化,最终达到数据驱动的精细化运营目的。

k-means与RFM模型结合进行用户群体划分
不错的参考:Mysql与Pyhton实现复购率和回购率
【运营】任意两个时间段的复购率?Power BI一招帮你搞定

技巧总结

1、得到新字段%Y-%m的方法:
df0['order_dt'].astype('datetime64[M]')
2、去重:
series.apply(lambda x:len(x.unique()))
3、查询query:
df.query("order_amount<4000").plot(kind='scatter',x = "order_amount",y = "order_products") 
4、作图:
df.plot()可以直接作图,而非仅series.plot()
5、排序.sort_values():
df和series都可以调用.sort_values()方法
6、分组.agg():
grouped.agg([min,max])

df.groupby('A').agg({'B': ['min', 'max'], 'C': 'sum'})
7、每月新客户占比(两次分组统计+分组后可用value_counts):
grouped_um=df0.groupby(['month','user_id']).order_dt.agg(['min','max'])
grouped_um['new'] = (grouped_um['min'] == grouped_um['max'] )    
grouped_um.reset_index().groupby('month').new.value_counts()

或者
grouped_um1=grouped_um.reset_index().groupby('month')
grouped_um2=grouped_um1['new'].apply(lambda x:x.value_counts()/x.count()).reset_index()
grouped_um2[grouped_um2['level_1']].plot(y = 'new', x ="month")
8、








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

推荐阅读更多精彩内容