0. 获取数据集
关注公众号:『AI学习星球
』
回复:某电子产品销售数据分析报告及RFM模型
即可获取数据下载。
论文辅导
或算法学习
可以通过公众号滴滴我
1. 项目背景
通过对某电子产品销售的数据进行清洗,对产品进行分析,对用户分析,销售情况分析,RFM模型分析。
2. 数据抽取
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
plt.rcParams['font.sans-serif'] = ['SimHei'] #设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False #正常显示负号
pd.set_option('display.float_format',lambda x : '%.2f' % x)#pandas禁用科学计数法
#忽略警告
import warnings
warnings.filterwarnings('ignore')
data = pd.read_csv('电子产品销售分析.csv',index_col=0,dtype={'category_id':'int64','user_id':'int64'},encoding='utf8')
data.head()
2.1 字段介绍
- Unnamed: 行号
- event_time:下单时间
- order_id:订单编号
- product_id:产品标号
- category_id :类别编号
- category_code :类别
- brand :品牌
- price :价格
- user_id :用户编号
- age :年龄
- sex :性别
- local:省份
2.2 创建新列-日期、月份、小时、周几
创建日期列
data['date'] = data.event_time.apply(lambda x: x.split(' ')[0])
#转换为日期格式
data['date'] = pd.to_datetime(data['date'])
创建月份列
data['month'] = data.date.dt.month
创建小时列
data['hour'] = data.event_time.apply(lambda x: x.split(' ')[1].split(':')[0])
创建周几列—周日为0,周一为1
data['weekday'] = data.date.apply(lambda x:x.strftime("%w"))
删除event_time列
del data['event_time']
data.head()
2.3 查看数据缺失、重复情况
data.shape
(564169, 14)
data.info()
缺失数据有category_code-产品类别
和brand-品牌
这两列,对于category_code
用"R
"来代替缺失值
而不是选择删除缺失值的数据
brand
这一列数据缺失比较少,直接删除缺失值
data['category_code'] = data['category_code'].fillna("R")
#删除brand这一列有缺失值的数据
data = data[data.brand.notnull()]
data.info()
2.4 存在重复值,但是换个角度去想,这些重复值就是同笔订单下了多个数量的订单,所以不删除重复值,进而增加一列购买数量的列和总价的列
data.duplicated().sum()
634
data.duplicated()
#添加新的列:购买数量
#data = data.value_counts().reset_index().rename(columns={0:'buy_cnt'})
#由于python版本问题,旧的版本没有上面的功能,所以要写以下3行代码
df = data.groupby(['order_id','product_id']).agg(buy_cnt=('user_id','count'))
data = pd.merge(data,df,on=['order_id','product_id'],how='inner')
data = data.drop_duplicates().reset_index(drop=True)
#添加新的列:购买总金额
data['amount'] = data['price'] * data['buy_cnt']
2.5 查看数据是否有异常
2.5.1 把几个id的格式转化为object格式
data.order_id = data.order_id.astype('object')
data.product_id = data.product_id.astype('object')
data.category_id = data.category_id.astype('object')
data.user_id = data.user_id.astype('object')
2.5.2 把hour和weekday转化为int
data['hour'] = data.loc[:,'hour'].astype('int')
data['weekday'] = data.loc[:,'weekday'].astype('int')
data.info()
2.5.3 查看价格和年龄是否存在异常值
data.describe(percentiles=[0.01,0.25,0.75,0.99]).T
data.describe(percentiles=[0.01,0.25,0.75,0.99]).T
以上7个字段均没有异常值
评论:
price和amount最小值为0,这类商品应该就是免费类的商品,所以也不属于异常值。
应该进一步分析,购买了0元商品的用户,后续是否还有购买了其他的商品
2.5.4 检查其他字段是否有异常值
data.describe(include='all').T
发现date日期有异常值,显示为1970-01-01,把这些异常值删除
data = data[data.date>'1970-01-01']
data.date.min()
Timestamp(‘2020-01-05 00:00:00’)
3. 数据清洗完毕后,对数据进行分析
data.head()
data.shape
(535065, 16)
data.reset_index(drop=True,inplace=True)
#保存清洗后的数据
data.to_csv('data_clean.csv',index=False)
3.1 总的指标
3.1.1 总GMV:约1.15亿元
round(data['amount'].sum(),0)
114986636.0
3.1.2 每月的GMV
- GMV8月之前都基本是处于上升状态,在7月8月的上升更是非常大,8月达到峰值,然后就开始下降了
GMV_month = data.groupby('month').agg(GMV=('amount','sum'))
GMV_month
plt.plot(GMV_month.index,GMV_month['GMV'])
plt.show()
3.1.3 客单价:1240元
#按客户数量
round(data['amount'].sum() / data['user_id'].nunique(),0)
1240.0
#按订单数量
round(data['amount'].sum() / data['order_id'].nunique(),0)
296.0
3.2 用户分析
3.2.1 结论先行
- 各地区用户最多的是广东(21382),然后是上海(16031)、北京(15928),其他8个城市比较平均(在5400上下)
- 广东人口基数是全国最高的,而且目前广东的用户仅占广东人口总数的0.017%,最高用户占比是北京:0.0728%,约为广东的4倍,所以在广东投入拉新活动性价比是比较高的
- 男性和女性用户各占一半
- 年龄分布也平均,总体上是在16-50岁之间
3.2.2 用户总数量
data = pd.read_csv('data_clean.csv')
#转换为日期格式
data['date'] = pd.to_datetime(data['date'])
data.user_id.nunique()
92755
3.2.3 各地区用户数量
local = data.groupby('local')['user_id'].nunique().reset_index()
local = local.rename(columns={'user_id':'用户数量'})
local = local.sort_values('用户数量').reset_index(drop=True)
local
plt.figure(figsize=(10,8))
plt.ylabel('用户数量')
plt.title('各地区用户数量')
plt.bar(local['local'],local['用户数量'])
for x,y in enumerate(local['用户数量']):
plt.text(x,y+200,y,ha='center')
plt.show()
- 广东的用户数量是最多的,然后就是北京和上海,其他八个城市用户比较平均,都是在5400左右。
根据2020年的全国人口普查,在网上得到了各省的人口数量数据,分析各省用户的占比,看看哪些省还可以进行用户拉新
population = pd.read_excel('2020年各省人口数量.xlsx')
population = population.iloc[:,:2]
population.head()
local = pd.merge(local,population,how='inner',left_on='local',right_on='地区')
local['占比'] = local['用户数量'] / local['人口数']
local = local.sort_values('占比',ascending=False).reset_index(drop=True)
local
从上表可以看到,用户数量占比前三是北京、上海、海南
,第四是天津
,广东
排第五,广东的占比仅为第一名北京的五分之一
,加上广东的人口数是最多的,所以在广东进行拉新活动的性价比是最高的
3.2.4 用户性别分布
sex = data.groupby('sex')['user_id'].nunique().reset_index()
sex.rename(columns={'user_id':'用户数量'},inplace=True)
sex
plt.pie(sex['用户数量'],labels=sex['sex'],autopct='%1.2f%%')
plt.show()
男性女性用户各占一半
3.2.5 年龄分布
data.age.min()
16.0
data.age.max()
50.0
bins = [15,20,25,30,35,40,45,50]
labels = ['(15-20]岁','(20-25]岁','(25-30]岁','(30-35]岁','(35-40]岁','(40-45]岁','(45-50]岁']
data_ = data.copy()
data_['age_bin'] = pd.cut(x=data.age,bins=bins,right=True,labels=labels)
data_
age = data_.groupby('age_bin')['user_id'].nunique().reset_index()
age.rename(columns={'user_id':'用户数量'},inplace=True)
age
plt.figure(figsize=(10,4))
plt.bar(age['age_bin'],age['用户数量'])
plt.show()
用户年龄分布较平均,在16-50
岁
data_.groupby('age')['user_id'].nunique().reset_index()
3.2.6 各年龄段的消费金额及下单数量
发现35-40岁的用户,贡献的消费金额与下订单的数量都是最低的,其他年龄段比较平均
age_bin_data = data_.groupby('age_bin').agg(消费金额=('amount','sum'),下单次数=('order_id','nunique'))
age_bin_data
fig,ax1 = plt.subplots(figsize=(16,8))
xticks = np.arange(len(age_bin_data.index))
ax1.bar(xticks,age_bin_data.消费金额,width=0.3,color='g')
ax1.set_ylabel('消费金额')
ax2 = ax1.twinx()
ax2.bar(xticks+0.3,age_bin_data.下单次数,width=0.3,color='b')
ax2.set_ylabel('下单次数')
plt.title('各年龄段的消费金额及下单次数')
ax1.set_xticks(xticks+0.15)
ax1.set_xticklabels(age_bin_data.index)
plt.show()
3.2.7 男性女性的消费金额及下单数量
发现男性女性的消费金额与下单次数均比较平均
sex_data = data_.groupby('sex').agg(消费金额=('amount','sum'),下单次数=('order_id','nunique'))
sex_data
3.2.8分析购买了0元产品的用户
从产品的类别可以知道,0元的商品应该是抽奖活动中奖的
- 中奖的30个用户中,仅有1个用户没有购买过其他商品,而且这30个用户的客单价高达34832元(总的客单价为1240元)
data[data['price']==0]
#提取该批用户出来
user_0 = data[data['price']==0]['user_id'].reset_index(drop=True)
user_0
user_0.shape
(30,)
#30个中奖的用户中,只有一个用户没有产生消费
user_0[~user_0.isin(data[data['price']>0]['user_id'])]
0 1515915625468531712
Name: user_id, dtype: int64
data_user_0 = pd.merge(data,user_0,on='user_id')
data_user_0_amount = data_user_0.groupby('user_id').agg(消费金额=('amount','sum')).sort_values('消费金额',ascending=False)
data_user_0_amount
#该批用户的客单价为34832元
data_user_0_amount['消费金额'].sum() / 30
34832.36466666667
3.2.9 二八定律-找出累计贡献销售额80%的那批用户
- 前27%的用户贡献了80%的销售收入,这批用户要做好跟进,一定要留住这批贡献大的客户
user_28 = data.groupby('user_id').agg(消费金额=('amount','sum')).sort_values('消费金额',ascending=False).reset_index()
user_28['累计销售额'] = user_28['消费金额'].cumsum()
user_28
#前27%的用户贡献了80%的销售收入
p = user_28['消费金额'].cumsum()/user_28['消费金额'].sum() # 创建累计占比,Series
key = p[p>0.8].index[0]
key
25408
key / user_28.shape[0]
0.2739259339119185
plt.plot(user_28.index,user_28['累计销售额'])
plt.show()
3.3.10 客户消费金额的分位数
用户平均消费金额大于75%分位数,即存在着高消费的客户
data.groupby('user_id').agg(消费金额=('amount','sum')).describe(percentiles=(0.01,0.1,0.25,0.75,0.9,0.99)).T
3.3.11 客户消费周期
- 消费了两次及以上的客户有50%的消费周期为7天内,用户的消费周期还是比较短的。75%为26天内,消费周期适中。
purchase_day = data[data['amount']>0].sort_values('date').groupby('user_id').apply(lambda x: x['date'] - x['date'].shift()).dt.days
purchase_day
purchase_day[purchase_day>0].describe(percentiles=[0.01,0.1,0.25,0.75,0.9,0.99])
3.2.12 每月新客户、不活跃客户、回流客户、活跃客户的情况
pivoted_amount =data[data['amount']>0].pivot_table(index='user_id'
,columns='month'
,values='buy_cnt'
,aggfunc='sum').fillna(0)
columns_month = pivoted_amount.columns.astype('str') #一定要把列名格式变为str不然后面就会报错
pivoted_amount.columns = columns_month
pivoted_purchase = pivoted_amount.applymap(lambda x:1 if x>0 else 0)
def active_status(data):
status =[]
for i in range(11):
#若本月没有消费
if data[i] ==0:
if len(status)>0: #如果不是第一个月,
if status[i-1]=='未注册': #如果上个月已经是未注册,那么本月也是未注册
status.append('未注册')
else: #如果上月已注册,则本月为不活跃
status.append('不活跃')
else: #如果是第一个月
status.append('未注册') #则未注册
#若本月消费
else:
if len(status)==0: #如果是第一个月,则为新注册用户
status.append('新客户')
else: #如果不是第一个月
if status[i-1]=='不活跃': #如果上月为不活跃,那么本月为回流
status.append('回流')
elif status[i-1]=='未注册': #如果上月为未注册,那么本月为新注册
status.append('新客户')
else: #如果上月为活跃,本月也为活跃
status.append('活跃')
return pd.Series(status,index=columns_month)
pivoted_purchase_status = pivoted_purchase.apply(lambda x:active_status(x),axis=1)
pivoted_purchase_status.head()
- unreg 未注册
- unactive 不活跃
- new 新注册
- active 活跃
- return 回流–不活跃后第一次活跃即为回流
purchase_cnt = pivoted_purchase_status.apply(lambda x:x.value_counts())
#去除未注册的数据行
purchase_cnt = purchase_cnt[purchase_cnt.index != '未注册']
purchase_cnt = purchase_cnt.fillna(0)
#排序 可排可不排
purchase_cnt = purchase_cnt.loc[['新客户','回流','活跃','不活跃'],:]
purchase_cnt
purchase_cnt.T.plot(figsize=(16,8))
- 回流人数
3.3 产品分析
data.head()
3.3.1 销量前十产品
对于热销产品,应该时刻关注他们的库存量,避免发生缺货情况
cnt = data.groupby('product_id').agg(销售总量=('buy_cnt','sum')).reset_index().sort_values('销售总量',ascending=False).reset_index(drop=True)
cnt.head(10)
3.3.2 销量少于10的产品
有12069个产品销量少于10,对于这批产品可以考虑促销活动对其进行清仓处理
cnt.describe(percentiles=(0.01,0.1,0.25,0.75,0.9,0.99))
cnt[cnt.销售总量<10]
3.3.3销售额前十产品
销售额并不代表利润,往往还需要结合产品的成本来看
amount = data.groupby('product_id').agg(销售总额=('amount','sum')).reset_index().sort_values('销售总额',ascending=False).reset_index(drop=True)
amount.head(10)
amount.describe(percentiles=(0.01,0.1,0.25,0.75,0.9,0.99))
3.3.4 销量前十的产品类别 category_code
最受欢迎的产品类别是smartphone-即手机,是第二名手提电脑的4倍
# 需要去除类别为R的,因为是缺失数据
cnt_category = data[data.category_code != 'R'].groupby('category_code').agg(销量=('buy_cnt','sum')).reset_index().sort_values('销量',ascending=False).reset_index(drop=True)
cnt_category.head(10)
3.3.5 对于手机,销量前五的品牌-brand
销量前五分别为三星、苹果、小米、华为、OPPO,其中三星占了一半以上的份额,苹果占据了四分之一
brand_5 = data[data.category_code=='electronics.smartphone'].groupby('brand').agg(销量=('buy_cnt','sum')).reset_index().sort_values('销量',ascending=False)
brand_5.reset_index(drop=True,inplace=True)
brand_5.head(5)
brand_5['销量'].sum()
102169
plt.pie(data=brand_5.head(5)
,x='销量'
,labels='brand'
,autopct='%.1f%%'
,textprops={'fontsize':12, 'color':'k'} # 设置文本标签的属性值
,radius=2
)
plt.show()
data[data.category_code=='electronics.smartphone'].groupby('brand').agg(销量=('amount','sum')).reset_index().sort_values('销量',ascending=False)
3.3.6 分析销量前五的手机品牌的人群特征
3.3.6.1 samsung品牌
- 年龄分布比较平均
- 男女比例也是平均
- 广东、上海、北京占据了50%以上的销量
3.3.6.1.1 年龄分布
user_samsung = data_.loc[(data_['brand']=='samsung') & (data_['category_code']=='electronics.smartphone')]
user_samsung.head()
user_samsung.groupby('age_bin').agg(销量=('buy_cnt','sum'))
3.3.6.1.2 性别分布
user_samsung.groupby('sex').agg(销量=('buy_cnt','sum'))
3.3.6.1.3 地域分布
local_brand = user_samsung.groupby('local').agg(销量=('buy_cnt','sum')).sort_values('销量',ascending=False)
local_brand
plt.pie(x=local_brand['销量'].values
,labels=local_brand.index
,autopct='%.1f%%'
,textprops={'fontsize':12, 'color':'k'} # 设置文本标签的属性值
,counterclock = False # 是否逆时针,这里设置为顺时针方向
,startangle = 90 # 设置饼图的初始角度
,radius=2
)
plt.show()
3.3.6.2 apple品牌
人群特征基本与samsung一致
3.3.6.2.1 年龄分布
user_apple = data_.loc[(data_['brand']=='apple') & (data_['category_code']=='electronics.smartphone')]
user_apple.head()
user_apple.groupby('age_bin').agg(销量=('buy_cnt','sum'))
3.3.6.2.2 性别分布
user_apple.groupby('sex').agg(销量=('buy_cnt','sum'))
3.3.6.2.3 地域分布
user_apple.groupby('sex').agg(销量=('buy_cnt','sum'))
plt.pie(x=local_brand_apple['销量'].values
,labels=local_brand_apple.index
,autopct='%.1f%%'
,textprops={'fontsize':12, 'color':'k'} # 设置文本标签的属性值
,counterclock = False # 是否逆时针,这里设置为顺时针方向
,startangle = 90 # 设置饼图的初始角度
,radius=2
)
plt.show()
3.4 销售情况分析
3.4.1 结论先行
广东、上海、北京的销售额、销量以及客户数量都是最高的
- 销售额与销量1-8月呈上升趋势,但是8月份之后就开始下降
- 618和双十一活动效果基本没有得到有效的回报,复盘一下618与双十一活动的内容。
- 销售额与销量在5月、7月、8月有均有上升,这三个月的新客户都有增长,幅度也不少,证明拉新活动效果不错;9月份下降,加上9月份的新客户下跌,还有9月份下订单的客户也减少了(其中新客下单的数量减少了四分之三,老客只是稍微下降一点点),是否因为9月份的拉新活动投入减少了呢?也可拆解是哪一渠道的新客减少了。
3.4.2 各省销量、销售额情况
local_situation = data.groupby('local').agg(销量=('buy_cnt','sum'),销售额=('amount','sum')).sort_values('销售额')
local_situation['销售额'] = local_situation['销售额'].astype('int')
plt.figure(figsize=(16,8))
plt.barh(local_situation.index,local_situation['销售额'])
for i,j in enumerate(local_situation['销售额']):
plt.text(j+200,local_situation['销售额'].index[i],j)
plt.title('销售额')
plt.show()
plt.figure(figsize=(16,8))
plt.barh(local_situation.index,local_situation['销量'])
for i,j in enumerate(local_situation['销量']):
plt.text(j+200,local_situation['销量'].index[i],j)
plt.title('销量')
plt.show()
3.4.3 按日期分析销售额与销量的总体走势
数据显示,618和双十一当天的销量和销售额并没有很高,反而处于低位(理论上不可能存在这种情况的-数据是否不够真实!!!)
- 假设数据真实,那么可以证实618和双十一的活动效果是非常差的,进行复盘
- 结合后面的分析,公司处于起步阶段,所以618与双十一的活动很难和其他已经开了好几年的公司拼。
data_date = data.groupby('date').agg(销售额=('amount','sum'),销量=('buy_cnt','sum'))
data_date
plt.figure(figsize=(20,5))
plt.plot(data_date.index,data_date['销售额'])
#plt.xticks([]) #隐藏坐标轴
#plt.xticks([data_date.index.min(),'2020-06-18','2020-11-11'])
plt.title('销售额')
plt.show()
plt.figure(figsize=(20,5))
plt.plot(data_date.index,data_date['销量'])
#plt.xticks([]) #隐藏坐标轴
#plt.xticks([data_date.index.min(),'2020-06-18','2020-11-11'])
plt.title('销量')
plt.show()
3.4.4 按月份对销售额和销量进行分析
- 销售额1-8月呈上升趋势,但是8月份之后就开始下降
- 销量基本与销售额呈一致的趋势
data_month = data.groupby('month').agg(销售额=('amount','sum'),销量=('buy_cnt','sum'))
plt.figure(figsize=(16,5))
plt.plot(data_month.index,data_month['销售额'])
plt.title('销售额')
plt.xticks(data_month.index)
plt.show()
plt.figure(figsize=(16,5))
plt.plot(data_month.index,data_month['销量'])
plt.title('销量')
plt.xticks(data_month.index)
plt.show()
3.4.4.1 新客老客的销售额与销量对比
#划分每个用户的首次购买月份(用来确认用户在几月份是属于新客户)
data_user = data.groupby('user_id').agg(首次购买月份=('month','min')).reset_index()
user_all = pd.merge(data,data_user,on='user_id')
user_all['新老客户'] = np.where(user_all['month']==user_all['首次购买月份'],'新客户','老客户')
user_all.head()
#每月新客的销售额和销量
user_all_new = user_all[user_all['新老客户']=='新客户'].groupby('month').agg(销售额=('amount','sum'))
user_all_new['销量'] = user_all[user_all['新老客户']=='新客户'].groupby('month').agg(销量=('buy_cnt','sum'))
user_all_new
user_all_old = user_all[user_all['新老客户']=='老客户'].groupby('month').agg(销售额=('amount','sum'))
user_all_old['销量'] = user_all[user_all['新老客户']=='老客户'].groupby('month').agg(销量=('buy_cnt','sum'))
user_all_old
3.4.4.2 新老客户销售额对比图
从数据中发现,基本每个月都是新客的贡献度都大于老客,证明公司处于起步阶段,老客户的黏性还不够高
plt.figure(figsize=(16,8))
line1, = plt.plot(user_all_new.index,user_all_new['销售额'],c='r')
line2, = plt.plot(user_all_old.index,user_all_old['销售额'],c='b')
plt.legend([line1,line2],['新客','老客'])
plt.xticks(user_all_new.index)
plt.title('新老客户销售额对比')
plt.show()
3.4.4.3 新老客户销量对比图
plt.figure(figsize=(16,8))
line1, = plt.plot(user_all_new.index,user_all_new['销量'],c='r')
line2, = plt.plot(user_all_old.index,user_all_old['销量'],c='b')
plt.legend([line1,line2],['新客','老客'])
plt.xticks(user_all_new.index)
plt.title('新老客户销量对比')
plt.show()
3.4.5 按周分析
- 0为周日,6为周六
- 从数据中得出:销售额与销量在周末会比工作日高,客户比较喜欢在周末进行购物,可以在周末多推送商品给用户,适当推销一两款要清仓的商品给用户
data_weekday = data.groupby('weekday').agg(销售额=('amount','sum'),销量=('buy_cnt','sum'))
plt.figure(figsize=(16,5))
plt.plot(data_weekday.index,data_weekday['销售额'])
plt.title('销售额')
plt.xticks(data_weekday.index)
plt.show()
plt.figure(figsize=(16,5))
plt.plot(data_weekday.index,data_weekday['销量'])
plt.title('销量')
plt.xticks(data_weekday.index)
plt.show()
3.4.6 按小时分析
- 销售额与销量在凌晨1点以后就开始上升,到达9点的时候达到峰值,然后又下降。
- 用户喜欢在早上6点至中午12点之间进行购物,可以在这个时间段发送推荐内容给用户
- 理论上,应该会是大概早上8点以后呈上升趋势,然后到达下午1点以后又下降,到达下午5点之后又会开始上升至凌晨12点的
data_hour = data.groupby('hour').agg(销售额=('amount','sum'),销量=('buy_cnt','sum'))
plt.figure(figsize=(16,5))
plt.plot(data_hour.index,data_hour['销售额'])
plt.title('销售额')
plt.xticks(data_hour.index)
plt.show()
plt.figure(figsize=(16,5))
plt.plot(data_hour.index,data_hour['销量'])
plt.title('销量')
plt.xticks(data_hour.index)
plt.show()
3.4.7 每月新增用户:首次购买即为新增用户
- 4月比3月新增用户多了差不多2倍,5月又比4月多了2.3倍,证明4月5月的拉新活动有比较好的效果;
- 但是6月份又下跌了35%,
- 7月份比6月份增多了1.5倍,8月份与7月份持平,也是证明7月8月的拉新效果不错;
- 9月-11月一致持下降趋势,9月更是比8月下跌了63%;
- 进一步分析是哪一个拉新渠道导致9月份新客的下跌?还是9月份开始就减少了拉新活动的投入?
new_user = data.groupby('user_id').agg({'month':'min'}).reset_index()
new_user.head()
new_user_month = new_user.groupby('month').agg(新用户数量=('user_id','nunique'))
new_user_month['环比'] = new_user_month.pct_change()
new_user_month
plt.figure(figsize=(16,5))
plt.plot(new_user_month.index,new_user_month['新用户数量'])
plt.title('新用户数量')
plt.xticks(new_user_month.index)
plt.show()
3.4.8 每月复购率 = 当月购买次数大于2的用户数/总购买人数 (同一天购买多次视为1次)
- 第一次复购人数增长较多是在5月份,贡献最多的是新客(新客是老客的2倍多);
- 第二次复购人数增长较多是在7月份,8月份也上涨了,贡献最多的依然是新客;
- 然后9月份开始下降,新客复购人数下降了63%,老客仅下降了13%,贡献率第一次被老客反超新客;
- 复购率1-3月在上升,然后4月份下降到比1月份还低,进一步分析,4月份进行了拉新活动,较多新客当月只购买了一次(有可能是因为拉新活动启动比较晚,新客的复购周期还没到;也有可能这品新客在4月的购买满意度较低,应该着重分析这批客户是否有投诉),所以造成复购率下降;
- 复购率5月份开始上升,直至11月份下降,11月份新客户的复购率大幅下降,而从9月开始,新客与老客的复购人数都在减少,加上新客数的骤降就导致复购率下降了。老客的复购率都在下降,那么很可能是老客对我们店铺的满意度不够,应该做好产品的优化与服务,维系好老客,还有加强拉新。
data_buy = data.groupby(['user_id','month','date']).agg(是否购买=('user_id','nunique')).reset_index()
data_buy_month = data_buy.groupby(['user_id','month']).agg(每月购买次数=('是否购买','sum')).reset_index()
data_repurchase = data_buy_month[data_buy_month['每月购买次数']>=2].groupby('month').agg(每月复购人数=('user_id','nunique'))
data_repurchase['每月购买人数'] = data.groupby('month').agg(每月购买人数=('user_id','nunique'))
data_repurchase['复购率'] = data_repurchase['每月复购人数'] / data_repurchase['每月购买人数']
data_repurchase
fig,ax1 = plt.subplots(figsize=(16,5))
line1, = ax1.plot(data_repurchase.index,data_repurchase['复购率'],c='r')
ax1.set_ylabel('复购率')
ax2 = ax1.twinx() #twinx将ax1的X轴共用与ax2
line2, = ax2.plot(data_repurchase.index,data_repurchase['每月复购人数'])
ax2.set_ylabel('每月复购人数')
plt.title('每月复购率及复购人数')
plt.xticks(data_repurchase.index)
plt.legend([line1,line2],['复购率','每月复购人数'])
plt.show()
plt.subplots(figsize=(16,5))
plt.plot(data_repurchase.index,data_repurchase['每月购买人数'],c='r')
plt.title('每月购买人数')
plt.xticks(data_repurchase.index)
plt.show()
#划分每个用户的首次购买月份(用来确认用户在几月份是属于新客户)
data_user = data.groupby('user_id').agg(首次购买月份=('month','min')).reset_index()
#data_buy_month 是上面求得的:每个用户每月的购买次数
user = pd.merge(data_buy_month,data_user)
user['新老客户'] = np.where(user['month']==user['首次购买月份'],'新客户','老客户')
#新客户购买人数
user_buy = user[user['新老客户']=='新客户'].groupby('month').agg(新客购买人数=('user_id','nunique'))
#老客户购买人数
user_buy['老客购买人数'] = user[user['新老客户']=='老客户'].groupby('month').agg(老客购买人数=('user_id','nunique'))
user_buy = user_buy.fillna(0)
user_buy
plt.subplots(figsize=(16,5))
line1, = plt.plot(user_buy.index,user_buy['新客购买人数'],c='r')
line2, = plt.plot(user_buy.index,user_buy['老客购买人数'])
plt.title('新老客购买人数')
plt.xticks(user_buy.index)
plt.legend([line1,line2],['新客','老客'])
plt.show()
3.4.9 每月回购率
- 回购率最高的是2月和5月,整体的回购率在下降,在5月份反弹上升了,然后就走向下坡了
- 回购人数在不断上升,直至到9月开始下降
- 虽然回购人数上升,但是整体回购率在下降,可见老客的回头率在不断下降,也就证明我们的产品留不住大部分的客户,应该要进一步分析到底是哪里出错了?
purchase_cnt = purchase_cnt.T
purchase_cnt['老客户'] = purchase_cnt.sum(axis=1) - purchase_cnt['新客户']
purchase_cnt['回购'] = purchase_cnt['回流'] + purchase_cnt['活跃']
purchase_cnt['回购率'] = purchase_cnt['回购'] / purchase_cnt['老客户']
purchase_cnt
plt.figure(figsize=(16,8))
plt.plot(purchase_cnt.index,purchase_cnt['回购率'])
plt.title('每月回购率')
plt.show()
plt.figure(figsize=(16,8))
plt.plot(purchase_cnt.index,purchase_cnt['回购'])
plt.title('每月回购人数')
plt.show()
3.4.10 每月的新客、老客复购人数分析(老客定义:首次购买次月即成为老客)
#划分每个用户的首次购买月份(用来确认用户在几月份是属于新客户)
data_user = data.groupby('user_id').agg(首次购买月份=('month','min')).reset_index()
data_user
# data_buy_month 是上面求得的:每个用户每月的购买次数
user = pd.merge(data_buy_month,data_user)
user['新老客户'] = np.where(user['month']==user['首次购买月份'],'新客户','老客户')
# 筛选出每月购买次数大于等于2的数据
user_2 = user[user['每月购买次数'] >= 2]
# 每月新客户的复购人数
user_repurchase = user_2[user_2['新老客户']=='新客户'].groupby('month').agg(新客户复购人数=('user_id','nunique'))
user_repurchase
#每月老客户的复购人数
#1月只有新客户 所以老客户复购人数为0
user_repurchase['老客户复购人数'] = user_2[user_2['新老客户']=='老客户'].groupby('month').agg(老客户复购人数=('user_id','nunique'))
user_repurchase = user_repurchase.fillna(0)
user_repurchase['老客户复购人数'] = user_repurchase['老客户复购人数'].astype('int')
user_repurchase
user_repurchase['每月购买人数'] = data_repurchase['每月购买人数']
user_repurchase['新客户复购率'] = user_repurchase['新客户复购人数'] / user_repurchase['每月购买人数']
user_repurchase['老客户复购率'] = user_repurchase['老客户复购人数'] / user_repurchase['每月购买人数']
user_repurchase
plt.subplots(figsize=(16,5))
line1, = plt.plot(user_repurchase.index,user_repurchase['新客户复购率'],c='r')
line2, = plt.plot(user_repurchase.index,user_repurchase['老客户复购率'])
plt.title('每月新老客户的复购率')
plt.xticks(user_repurchase.index)
plt.legend([line1,line2],['新客户复购率','老客户复购率'],loc=4)
plt.show()
4 RFM模型
4.1 结论
-
重要的客户共有44129人
-
其中重要价值客户有20012人,约占重要客户的50%,对于重要价值客户,要给与他们VIP式的服务,时刻留意他们的购买反馈
-
重要发展客户有4415人,这类客户消费频率不够高,要想办法提高他们的消费频率,例如发放满减卷等
-
重要保持客户有11368人,这类客户最后一次购买时间距离现在已经很远了,应该发送信息,或者电话联系他们,也可以发放满减卷给他们,提高他们的复购率
-
重要挽留客户有8334人,要想办法挽回这批将近流失的客户,也可以通过短信召回,发放优惠券.
-
一般的客户共有48626人
-
对于一般价值、一般发展、一般保持的客户,在处理好重要客户的情况下,可以酌情去发展维系这批客户。
-
其中一般挽留用户有26843人,这类客户是要流失的,所以在没有多余资源的情况下,就放弃这批客户吧。
用户同一天购买多次视为一次
- R:最近一次消费(Recency)
- F:消费频率(Frequency)
- M:消费金额(Monetary)
user_rfm = data.groupby(['user_id','date']).agg(消费金额=('amount','sum')).reset_index()
user_rfm.head()
user_rfm = user_rfm.groupby('user_id').agg(最后购买日期=('date','max')
,M=('消费金额','sum')
,F=('date','count')
).reset_index()
user_rfm
user_rfm['最后购买日期'] = pd.to_datetime(user_rfm['最后购买日期'])
user_rfm['R'] = user_rfm['最后购买日期'].apply(lambda x:user_rfm['最后购买日期'].max() - x)
user_rfm['R'] = user_rfm['R'].dt.days
user_rfm = user_rfm[['user_id','R','F','M']]
user_rfm
user_rfm['user_id'] = user_rfm['user_id'].astype('object')
user_rfm.describe(percentiles=(0.01,0.1,0.25,0.75,0.9,0.95,0.99)).T
R的打分:
- (30-60]:4分
- (60-90]:3分
- (90-120]:2分
- 120以上:1分
F的打分: - 1次:1分
- 2次:2分
- 3次:3分
- 4次:4分
- 5次及以上:5分
M的打分: - (200-500]:2分
- (500-1000]:3分
- (1000-2000]:4分
- 2000以上:5分
rfm_score = user_rfm.copy()
for i,j in enumerate(rfm_score['R']):
if j <= 30:
rfm_score['R'][i] = 5
elif j <= 60:
rfm_score['R'][i] = 4
elif j <= 90:
rfm_score['R'][i] = 3
elif j <= 120:
rfm_score['R'][i] = 2
else :
rfm_score['R'][i] = 1
for i,j in enumerate(rfm_score['F']):
if j <= 1:
rfm_score['F'][i] = 1
elif j <= 2:
rfm_score['F'][i] = 2
elif j <= 3:
rfm_score['F'][i] = 3
elif j <= 4:
rfm_score['F'][i] = 4
else :
rfm_score['F'][i] = 5
for i,j in enumerate(rfm_score['M']):
if j <= 200:
rfm_score['M'][i] = 1
elif j <= 500:
rfm_score['M'][i] = 2
elif j <= 1000:
rfm_score['M'][i] = 3
elif j <= 2000:
rfm_score['M'][i] = 4
else :
rfm_score['M'][i] = 5
rfm_score
rfm_score['R'].mean()
2.498830251738451
rfm_score['F'].mean()
1.996366772680718
rfm_score['M'].mean()
2.581930893213304
rfm = pd.DataFrame()
rfm['user_id'] = rfm_score['user_id']
rfm['R'] = rfm_score['R'].apply(lambda x: '1' if x >= rfm_score['R'].mean() else '0')
rfm['F'] = rfm_score['F'].apply(lambda x: '1' if x >= rfm_score['F'].mean() else '0')
rfm['M'] = rfm_score['M'].apply(lambda x: '1' if x >= rfm_score['M'].mean() else '0')
rfm
rfm['result'] = rfm['R'] + rfm['F'] + rfm['M']
rfm
for i,j in enumerate(rfm['result']):
if j == '111':
rfm['result'][i] = '重要价值客户'
elif j == '101':
rfm['result'][i] = '重要发展客户'
elif j == '011':
rfm['result'][i] = '重要保持客户'
elif j == '001':
rfm['result'][i] = '重要挽留客户'
elif j == '110':
rfm['result'][i] = '一般价值客户'
elif j == '100':
rfm['result'][i] = '一般发展客户'
elif j == '010':
rfm['result'][i] = '一般保持客户'
elif j == '000':
rfm['result'][i] = '一般挽留客户'
rfm = rfm[['user_id','result']]
rfm
rfm['result'].value_counts()
plt.figure(figsize=(16,8))
plt.bar(rfm['result'].value_counts().index,rfm['result'].value_counts().values)
for x,y in enumerate(rfm['result'].value_counts()):
plt.text(x,y+100,y,ha='center')
plt.show
关注公众号:『AI学习星球
』
回复:某电子产品销售数据分析报告及RFM模型
即可获取数据下载。
论文辅导
或算法学习
可以通过公众号滴滴我