GMV异常分析及RFM用户价值分析

一、背景介绍

考拉电子公司(虚拟公司)是一家电子类产品经销商,成立于2020年1月,旗下产品有手机、电脑、影音设备等。公司从2020年1月至8月期间,整体发展良好,尤其是5至8月期间,公司GMV直线上升,但是从9月份开始,GMV出现下滑。考拉公司成立不足一年,GMV大幅下降是一个危险信号。

二、分析目的

1) 8月之后,考拉公司GMV下滑的原因?
2)如何提升考拉公司GMV?

三、分析思路

根据分析目的,我们以每月的GMV为核心指标。将GMV从产品和用户两个角度进行拆解,从用户角度分析,GMV=消费者数量客单价,消费者数量=老用户数量+新用户数量;从产品维度分析,GMV=产品销量平均单价,产品又有不同的品类,品类下面又分为不同的品牌。综上,本文根据下图的维度进行分析GMV下降原因,并提出改进建议,其中客单价和产品平均单价作为外部因素,不在分析范围内。
在这里插入图片描述

四、GMV异常原因分析
4.1 整体GMV分析

1到11月GMV:
在这里插入图片描述

从图中看出,考拉公司1~3月份处于缓慢发展阶段,4月份GMV出现大幅下滑后,公司做出调整,5月份GMV开始直线上升,并在8月份达到最大值,9月份开始下滑,10月虽有回升,但是仍没有回到8月的水平。
7至11月每日GMV
在这里插入图片描述

我们再把数据细分,以日为单位统计GMV,时间从7月到11月。从上图中可以看出,8月17日之后,GMV开始呈下降趋势,10月份,公司做出了调整,GMV有所回升,但是很不稳定,为了公司长期稳定发展,还需进一步分析。

4.2 用户维度分析

新用户:本月初次消费
老用户:本月之前,已经购买过考拉公司的产品
每月新老用户总量如下图:
在这里插入图片描述

从上图可以看出,8月之后,新老用户数都是直线下降,9月份开始,每月消费的老用户总量已经超越新用户。说明,8月份之后,公司老用户复购率直线下降,而新用户增长也尽显疲态。
我们再从性别、年龄和地区维度分析:
性别
在这里插入图片描述
年龄

在这里插入图片描述
地区
在这里插入图片描述
从上图看出,新老用户的变化与性别和年龄关系不大,而地区方面,广东、北京、上海三地每月消费者数量下降最多,当然这与这三地基数大有关。

4.3 产品维度分析

考拉公司各产品品类总GMV占比:
在这里插入图片描述
考拉公司销售的产品品类有124种,其中GMV前十的产品就占到了总量的75.13%,尤其是smartphone类产品,占到总体GMV的28.98%,所以我们单独来分析GMV前十的品类,如下图:在这里插入图片描述
从图中可以看出,8月份中,smartphone、notebook、kitchen refrigerators、video tv、kitchen washer这六类产品都出现了大幅下滑,其中smartphone最为严重。
接着,我们以smartphone为例,进一步拆解到品牌
在这里插入图片描述
从图中可以看出,8月份开始,apple销量直线下滑,samsung也出现大幅波动。
综上说明,考拉公司GMV中占比最大前六类产品,在8月份销量都明显下降,并对公司GMV产生了主要影响。其中,smartphone中的apple和samsung是“罪魁祸首”。

4.4 GMV出现异常的原因总结

在这里插入图片描述

五、解决方案
5.1 留住老用户

RFM用户价值分析
客户营销战略的倡导者Jay & Adam Curry从国外数百家公司进行的客户营销实施经验中提炼了如下经验:
1)公司收入的80%来自顶端20%的客户
2)20%的客户其利润率为100%
3)90%以上的收入来自现有客户
4)大部分的营销预算经常被用在非现有客户上
5)5%至30%的客户在客户金字塔中具有升级潜力
6)客户金字塔中客户升级2%,意味着销售收入增加10%,利润增加50%
通过数据库查询得知,顶端20%的客户消费占到了总体消费的73%,这也印证了Jay & Adam Curry总结的经验。所以,我们的目的不是提升所有用户的复购率,而是把预算用在最有价值的客户身上。这就需要我们对用户价值划分,下面根据RFM模型,量化用户价值,将用户划分成:
1)钻石用户(diamond)
2)黄金用户(gold)
3)白银用户(sliver)
4)青铜用户(general)
下图节选了20位用户的价值,我们先对RFM量化评分,再更根据总分划分用户价值。以公司目前状况,考拉公司的运营策略应该是在留住钻石用户的基础上,将黄金用户转化成钻石用户。
在这里插入图片描述

5.2 开发新客户

考拉公司用户分布:
在这里插入图片描述
从图中看出,北京、上海、广东三地是考拉公司的主营市场,三者总用户占到总体用户的57.50%。
我们再根据国家统计局统计的2020年各地区人口,计算出考拉公司用户占到当地总口的比例:
在这里插入图片描述
从图中可以看出北京和上海两地的用户占比最高,而广东作为主营地区,却与重庆持平。
综上,考拉公司的主战场是广东、北京和上海,其中广东人口基数大,潜在客户最多,也是开发新客户的最大突破口。其他地区仍处于起步阶段,重心要放在获客上面。

5.3 主营产品用户画像(以apple手机为例)

下图从性别、年龄和地区三个角度分析apple消费者:
在这里插入图片描述
从上图中看出,apple消费者与性别、年龄和地区并无太大区别,但这并不意味着不需要对apple消费者精细化运营,只是因为该数据集的数据不够。提升主营产品的销量是重中之重,所以还需要其他数据进一步分析。

5.4 用户消费习惯

下图统计了消费者消费的周次和时间:
在这里插入图片描述
从上图看出,周末的销量比工作日高,说明客户比较喜欢在周末购物,所以,考拉公司可以在周末做些活动,促销产品和获客。在消费时间上,用户喜欢在早上6点至中午12点之间进行购物,可以在这个时间段发送推荐内容给用户。

5.5 小结

为了使考拉公司GMV稳步上升,提出如下建议:
1)对于老用户,要精细化运营,将用户运营的预算,主要聚焦在钻石用户上;
2)开发新用户方面,以上海、北京、广东三地为基石,着力开发广东地区新用户;
3)少数主营产品,如:手机、笔记本电脑等,是公司盈利的根本,应先将精力主要放在这几类产品的推广和促销上,还需在根据其他数据进一步分析主营产品的用户画像,实现精细化推广;
4)周末和早上6点至中午12点之间是用户消费的主要时间,应在着力在这期间给用户推送产品最新消息等。

六、相关代码

python数据处理

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import seaborn as sns
import datetime as dt

# matplotlib与pandas初始设置
plt.rcParams['font.sans-serif'] = ['SimHei']  #设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False #正常显示负号
pd.set_option('display.max_columns', 30)
plt.rcParams.update({"font.family":"SimHei","font.size":14})
plt.style.use("tableau-colorblind10")

pd.set_option('display.float_format',lambda x : '%.2f' % x)#pandas禁用科学计数法
%matplotlib inline 

#忽略警告
import warnings
warnings.filterwarnings('ignore')
1、数据预处理
查看数据初始状态
# 导入数据(指明格式,节省空间)
data = pd.read_csv('sales_report.csv')
data.info(memory_usage='deep')
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 564169 entries, 0 to 564168
Data columns (total 12 columns):
 #   Column         Non-Null Count   Dtype  
---  ------         --------------   -----  
 0   Unnamed: 0     564169 non-null  int64  
 1   event_time     564169 non-null  object 
 2   order_id       564169 non-null  int64  
 3   product_id     564169 non-null  int64  
 4   category_id    564169 non-null  float64
 5   category_code  434799 non-null  object 
 6   brand          536945 non-null  object 
 7   price          564169 non-null  float64
 8   user_id        564169 non-null  float64
 9   age            564169 non-null  float64
 10  sex            564169 non-null  object 
 11  local          564169 non-null  object 
dtypes: float64(4), int64(3), object(5)
memory usage: 235.3 MB
data.tail()
Unnamed: 0event_timeorder_idproduct_idcategory_idcategory_codebrandpriceuser_idagesexlocal
56416426335162020-11-21 10:10:01 UTC238844098113469394215159662235266028482268105428166508800.00electronics.smartphoneoppo138.871515915625514888704.0021.00上海
56416526335172020-11-21 10:10:13 UTC238844098113469394315159662235090892822268105428166508800.00electronics.smartphoneapple418.961515915625514891264.0021.00北京
56416626335182020-11-21 10:10:30 UTC238844098113469394415159662235090899172268105402447036928.00appliances.personal.scalesvitek12.481515915625514834176.0019.00上海
56416726335192020-11-21 10:10:30 UTC238844098113469394422739481848394548372268105440371933952.00NaNmoulinex41.641515915625514834176.0019.00上海
56416826335202020-11-21 10:10:30 UTC238844098113469394415159662235091275662268105441101742848.00appliances.kitchen.blenderredmond53.221515915625514834176.0019.00上海
data.describe(include='object')
event_timecategory_codebrandsexlocal
count564169434799536945564169564169
unique389835123868211
top1970-01-01 00:33:40 UTCelectronics.smartphonesamsung广东
freq130710269796239284421122909
压缩数据

指定数据类型,object类型少于50%的转换成:category

# 指定数据类型
d_type = {'category_code':'category','brand':'category','sex':'category','local':'category','price':'float32','age':'int8'}
df = pd.read_csv('sales_report.csv',dtype=d_type)
df.info(memory_usage='deep')
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 564169 entries, 0 to 564168
Data columns (total 12 columns):
 #   Column         Non-Null Count   Dtype   
---  ------         --------------   -----   
 0   Unnamed: 0     564169 non-null  int64   
 1   event_time     564169 non-null  object  
 2   order_id       564169 non-null  int64   
 3   product_id     564169 non-null  int64   
 4   category_id    564169 non-null  float64 
 5   category_code  434799 non-null  category
 6   brand          536945 non-null  category
 7   price          564169 non-null  float32 
 8   user_id        564169 non-null  float64 
 9   age            564169 non-null  int8    
 10  sex            564169 non-null  category
 11  local          564169 non-null  category
dtypes: category(4), float32(1), float64(2), int64(3), int8(1), object(1)
memory usage: 70.0 MB
# 删除不需要的列
df.drop('Unnamed: 0',axis=1,inplace=True)
df.head()
event_timeorder_idproduct_idcategory_idcategory_codebrandpriceuser_idagesexlocal
02020-04-24 11:50:39 UTC229435993205453698615159662235090899062268105426648171520.00electronics.tabletsamsung162.011515915625441993984.0024海南
12020-04-24 11:50:39 UTC229435993205453698615159662235090899062268105426648171520.00electronics.tabletsamsung162.011515915625441993984.0024海南
22020-04-24 14:37:43 UTC229444402405808622022739483190571836582268105430162997248.00electronics.audio.headphonehuawei77.521515915625447879424.0038北京
32020-04-24 14:37:43 UTC229444402405808622022739483190571836582268105430162997248.00electronics.audio.headphonehuawei77.521515915625447879424.0038北京
42020-04-24 19:16:21 UTC229458426315407423622739483168174244392268105471367840000.00NaNkarcher217.571515915625443148032.0032广东
创建年月、日、周、时间
# 创建日期列
df['date'] = df.event_time.apply(lambda x: x.split(' ')[0])
# 转换成日期格式
df['date'] = pd.to_datetime(df['date'])
# 创建月份列
df['month'] = df['date'].dt.strftime('%m')
# 创建小时列
df['hour'] = df.event_time.apply(lambda x: x.split(' ')[1].split(':')[0])
# 创建周列
df['weekday'] = df['date'].dt.strftime('%w')
# 删除多余列
df.drop('event_time',axis=1,inplace=True)
# 压缩新增数据列
hh = ['month','hour','weekday']
for i in hh:
    df[i] = df[i].astype(dtype='category')
df.head()
order_idproduct_idcategory_idcategory_codebrandpriceuser_idagesexlocaldatemonthhourweekday
0229435993205453698615159662235090899062268105426648171520.00electronics.tabletsamsung162.011515915625441993984.0024海南2020-04-2404115
1229435993205453698615159662235090899062268105426648171520.00electronics.tabletsamsung162.011515915625441993984.0024海南2020-04-2404115
2229444402405808622022739483190571836582268105430162997248.00electronics.audio.headphonehuawei77.521515915625447879424.0038北京2020-04-2404145
3229444402405808622022739483190571836582268105430162997248.00electronics.audio.headphonehuawei77.521515915625447879424.0038北京2020-04-2404145
4229458426315407423622739483168174244392268105471367840000.00NaNkarcher217.571515915625443148032.0032广东2020-04-2404195
缺失值处理
df.isnull().sum()
order_id              0
product_id            0
category_id           0
category_code    129370
brand             27224
price                 0
user_id               0
age                   0
sex                   0
local                 0
date                  0
month                 0
hour                  0
weekday               0
dtype: int64
# category_code这一列缺失值过多,所以选择填充
# 先转化成object再填充
df['category_code'] = df['category_code'].astype('object')
df['category_code'] = df['category_code'].fillna('unkonwn')
df['category_code'] = df['category_code'].astype('category')
# brand这一列缺失值较少,选择删除
df = df[df.brand.notnull()]
df.info(memory_usage='deep')
<class 'pandas.core.frame.DataFrame'>
Int64Index: 536945 entries, 0 to 564168
Data columns (total 14 columns):
 #   Column         Non-Null Count   Dtype         
---  ------         --------------   -----         
 0   order_id       536945 non-null  int64         
 1   product_id     536945 non-null  int64         
 2   category_id    536945 non-null  float64       
 3   category_code  536945 non-null  category      
 4   brand          536945 non-null  category      
 5   price          536945 non-null  float32       
 6   user_id        536945 non-null  float64       
 7   age            536945 non-null  int8          
 8   sex            536945 non-null  category      
 9   local          536945 non-null  category      
 10  date           536945 non-null  datetime64[ns]
 11  month          536945 non-null  category      
 12  hour           536945 non-null  category      
 13  weekday        536945 non-null  category      
dtypes: category(7), datetime64[ns](1), float32(1), float64(2), int64(2), int8(1)
memory usage: 31.3 MB
重复值处理
df.duplicated().sum()
634
# 重复值是单次订单购买了多件商品,所以增加购买数量和总金额列
df = df.value_counts().reset_index().rename(columns={0:'buy_cnt'})
df['buy_cnt'] = df['buy_cnt'].astype('int16')
df['expense'] = df['price'] * df['buy_cnt']
df.head()
order_idproduct_idcategory_idcategory_codebrandpriceuser_idagesexlocaldatemonthhourweekdaybuy_cntexpense
0231894587981116298323090182048333178162268105479144079872.00unkonwncompliment0.561515915625465863936.0028浙江2020-05-280509442.24
1229574059474970222915159662235091048922268105428166508800.00electronics.smartphoneapple1387.011515915625448766464.0021北京2020-04-260409045548.04
2238844098113467469815159662235091067572360741867017995776.00appliances.environment.air_conditionersamsung366.411515915625514599680.0050广东2020-11-161104141465.64
3237504333155506674022739483083700967642268105409048871168.00computers.network.routeraltel57.851515915625504379136.0019上海2020-08-13081944231.40
4233499988703838308915159662235090900312268105402673529600.00unkonwnvitek18.501515915625447765248.0018广东2020-06-1906135355.50
查看异常值
df.describe(include='all').T
countuniquetopfreqfirstlastmeanstdmin25%50%75%max
order_id536311.00NaNNaNNaNNaTNaT2370510904961807360.0020245175202363216.002294359932054536960.002353674406846267392.002376454570843832320.002388440981134596608.002388440981134693888.00
product_id536311.00NaNNaNNaNNaTNaT1692699904749532672.00327324678971262912.001515966223509088512.001515966223509104896.001515966223509261824.001515966223527326208.002388434452476881920.00
category_id536311.00NaNNaNNaNNaTNaT2273068434482616576.0021891838583177408.002268105388421284352.002268105406549066752.002268105428166508800.002268105439323357952.002374498914001945600.00
category_code536311124unkonwn116093NaTNaTNaNNaNNaNNaNNaNNaNNaN
brand536311868samsung96123NaTNaTNaNNaNNaNNaNNaNNaNNaN
price536311.00NaNNaNNaNNaTNaT214.54305.980.0024.5199.51289.3311574.05
user_id536311.00NaNNaNNaNNaTNaT1515915625486138112.0023760191.501515915625439951872.001515915625467037184.001515915625486696704.001515915625511521280.001515915625514891264.00
age536311.00NaNNaNNaNNaTNaT33.1810.1216.0024.0033.0042.0050.00
sex5363112270454NaTNaTNaNNaNNaNNaNNaNNaNNaN
local53631111广东117097NaTNaTNaNNaNNaNNaNNaNNaNNaN
date5363113232020-10-22 00:00:0083101970-01-012020-11-21NaNNaNNaNNaNNaNNaNNaN
month536311111098047NaTNaTNaNNaNNaNNaNNaNNaNNaN
hour536311240950233NaTNaTNaNNaNNaNNaNNaNNaNNaN
weekday5363117686379NaTNaTNaNNaNNaNNaNNaNNaNNaN
buy_cnt536311.00NaNNaNNaNNaTNaT1.000.041.001.001.001.004.00
expense536311.00NaNNaNNaNNaTNaT214.73306.480.0024.9899.51289.3311574.05
# date列有异常数据
df = df[df.date>'1970-01-01']
df.date.min()
Timestamp('2020-01-05 00:00:00')
备份并导出已处理数据
# 备份数据
df_backup = df.copy()
# 导入处理数据
df.to_csv('new_sales_report.csv')
2、建立RFM模型
查看数据分布情况
# 选择统计日为最后一日的后一天
cal_date = max(all_user.date) + dt.timedelta(days=1)
def cal_frequency(date):
    return (cal_date-date.max()).days
#通过groupby语法对每一个用户进行分组聚合
rfm = all_user.groupby(['user_id']).agg({
    'date': cal_frequency,
    'order_id': 'count',
    'expense': 'sum'
}).sort_index(ascending=True)
rfm.rename(columns = {'date': 'Recency',
                           'order_id': 'Frequency',
                           'expense': 'Monetary'}, inplace=True) 
rfm.head()
RecencyFrequencyMonetary
user_id
1515915625439951872.001361416.64
1515915625440038400.0025256.43
1515915625440051712.006137489.53
1515915625440099840.0014204929.86
1515915625440121600.001312182.83
#查看rfm数据分布
def data_distribution(keyvalue,data):
    plt.figure(figsize = (18,4),dpi=600) 
    j=1
    for i in keyvalue:  
        plt.subplot(1,3,j)
        sns.distplot(data[i])
        plt.title(i,fontsize = 15)
        j+=1
        

keyvalue=['Recency', 'Frequency', 'Monetary']        
data_distribution(keyvalue,rfm)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipGBCcXR-1666960335864)(output_36_0.png)]

rfm.describe()
RecencyFrequencyMonetary
count92755.0092755.0092755.00
mean99.595.771239.68
std54.5327.144129.72
min1.001.000.00
25%62.001.00148.11
50%102.002.00456.89
75%128.004.001141.17
max322.001026.00160604.07
对数据进行分箱处理
# 创建三个新的Column, 分别表示R,F,M的quntitle值
# 按照各个数值的1/4,1/2,3/4中位数进行数据分类
# 创建三个新的Column, 分别表示R,F,M的quntitle值
labels= list(range(1,5))
labels_reverse = list(range(4,0,-1))

Rquartiles = pd.cut(rfm['Recency'],bins=[0,62,102,128,322],labels=labels_reverse)
rfm = rfm.assign(R = Rquartiles.values)
 
Fquartiles = pd.cut(rfm['Frequency'],bins=[0,1,2,4,1026],labels=labels)
rfm = rfm.assign(F = Fquartiles.values)
 
Mquartiles = pd.cut(rfm['Monetary'],bins=[-1,148.11,456.89,1141.17,160605],labels=labels) 
rfm = rfm.assign(M = Mquartiles.values)
rfm['RFM_Score'] = rfm[['R','F','M']].sum(axis=1)
labels=['general', 'sliver', 'gold', 'diamond']
RFM_Score=pd.cut(rfm['RFM_Score'],4,labels=labels)
rfm = rfm.assign(Category =RFM_Score.values)
rfm['Category'].value_counts().sort_index(ascending=True)
general    28335
sliver     22282
gold       18170
diamond    23968
Name: Category, dtype: int64
rfm.to_csv('RFM用户价值.csv')
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值