一、项目背景
本次样本数据集来自“天池”的婴儿用品信息,包含从2012年起至2015年2月的两个表trade(商品交易记录)和babyinfo(婴儿信息)。根据以往的销售数据分析2015年(当年)整体的销量断崖式下降的原因,并提出改进方案。
二、字段说明
1. trade表(商品交易记录):
buy_mount (购买数量/销量)
user_id(用户id)
auction_id(购买行为编号)
cat1(商品所属的大类)
cat_id(cat1的子类,是更细分的类别)
property(商品属性)
day(购买时间)
2. babyinfo表(婴儿信息):
user_id(用户id)
birthday(出生日期)
gender:性别(0 男孩,1 女孩,2性别不明)
三、数据处理
1.导入库
import pandas as pd
import numpy as np
from pandas import DataFrame,Series
import os
from matplotlib import pyplot as plt
import seaborn as sns
plt.rcParams['font.sans-serif'] =['Microsoft YaHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
import warnings
warnings.filterwarnings('ignore')
2.读取数据
df_baby=pd.read_csv('(sample)sam_tianchi_mum_baby.csv')
df_trade=pd.read_csv('(sample)sam_tianchi_mum_baby_trade_history.csv')
3.查看数据
df_baby.head()
df_trade.head()
property字段存在null值
df_trade.describe()
buy_mount的方差有63.9,需要处理
df_trade.isna().sum()
property共有144个null值
对df_trande和df_baby进行连接,生成df
df=pd.merge(df_trade,df_baby,left_on='user_id',right_on='user_id',how='left')
df.head()
并对day进行时间格式处理
df['day']=pd.to_datetime(df['day'],format='%Y%m%d')
df.head()
4.异常值处理
df['buy_mount'].describe()
buy_mount销量数据有异常值,方差为63.9
查看buy_mount的数据分布
df.buy_mount.value_counts().sort_index()
print(df.buy_mount.value_counts().sort_index().index)
print(df.buy_mount.value_counts().sort_index().values)
根据三倍标准差计算异常值为2.5+3x63.9=194。从业务角度上,根据国双2018年本土婴幼儿奶粉电商消费研究的数据,在电商平台购买婴幼儿奶粉的消费者年均购买次数约为27次,向上取整后,以单笔销量超过30罐奶粉作异常值处理。
df=df[df['buy_mount']<=30]
df.describe()
5.销量分析
确认指标:2015年当月销量同比增速必须高于上年同期同比增速或上年整体同比增速。
分析如下:
- 1 观察各年度每月销量情况走势
- 2 2015年1-2月的销量走势对比13年和14年,判断销量的好或差?
- 3 如果销量差,问题出在什么地方
- 4 如果销量差,还有多少缺口,有多少时间挽救,重要的挽救时间节点是什么时候?
- 5 如果要冲销量,推广什么品类?
5.1.各年度销售情况
以购买日期为标准对数据进行分组聚合,并对时间进行降采样至月。分别提取每年各月的销售数据
df.set_index(df.day,inplace=True,drop=True)
df.drop(columns='day',inplace=True)
df_month=df.resample('m').sum()
df_month.drop(columns=['user_id','auction_id','cat_id','cat1','birthday','gender'],inplace=True)
df_month['year']=df_month.index.year
df_month['month']=df_month.index.month
df_month.head()
每年销量同比增长
df_month.groupby(by='year')['buy_mount'].sum()
df_month.groupby(by='year')['buy_mount'].sum().pct_change()
可知,2014年销量比2013年增长50.54%。而2015年至今销量同比增速低于目标的50.54%,需要提高至50.54%以上。
各年份的每月销量情况
df_month_2012=df_month[df_month['year']==2012]
df_month_2013=df_month[df_month['year']==2013]
df_month_2014=df_month[df_month['year']==2014]
df_month_2015=df_month[df_month['year']==2015]
plt.figure(figsize=[10,8])
plt.plot(df_month_2012['month'],df_month_2012['buy_mount'],label='2012')
plt.plot(df_month_2013['month'],df_month_2013['buy_mount'],label='2013')
plt.plot(df_month_2014['month'],df_month_2014['buy_mount'],label='2014')
plt.plot(df_month_2015['month'],df_month_2015['buy_mount'],label='2015')
plt.xlabel('月份')
plt.ylabel('销量')
plt.legend(loc='upper left')
plt.title('各年份的每月销量情况')
plt.show()
可知2013年和2014的走势相似,1-2月份销量均出现下降,但并没有持续下降,在三月份出现上升。因此需要聚焦到1-2月份的数据上进行分析。由于近三年都是在1-2月份销量下降,可能是由于春节假期。
- 2013年春节:2月9日-2月15日
- 2014年春节:1月30日-2月6日
- 2015年春节:2月19日-2月25日
2015年月2013年相似,春节都完整分布在2月份,所以2015年2月份的数据应该与2013年相似,不能和2014年一样把销量都定为同比增长50%。所以将目标时间都改为春节前三十天,分析该段时间的销量情况比较合理。
春节前30日销售情况走势
df_spring=df.groupby(by=df.index).sum().drop(columns=['user_id','auction_id','cat_id','cat1','birthday','gender'])
df_spring_2013=df_spring['2013-1-10':'2013-2-15']
df_spring_2014=df_spring['2014-1-1':'2014-2-6']
df_spring_2015=df_spring['2015-1':'2015-2'][:-17:-1][::-1]
x_values=[i for i in range(len(df_spring_2013))]
x_labels=['闰月初一', '闰月初二', '闰月初三', '闰月初四', '闰月初五', '闰月初六', '闰月初七', '闰月初八', '闰月初九', '闰月初十', '闰月十一', '闰月十二', '闰月十三', '闰月十四', '闰月十五', '闰月十六', '闰月十七', '闰月十八', '闰月十九', '闰月二十', '闰月廿一', '闰月廿二', '闰月廿三', '闰月廿四', '闰月廿五', '闰月廿六', '闰月廿七', '闰月廿八', '闰月廿九', '闰月三十', '正月初一', '正月初二', '正月初三', '正月初四', '正月初五', '正月初六', '正月初七']
plt.figure(figsize=(12,8))
plt.plot(x_values,df_spring_2013.buy_mount,label='2013')
plt.plot(x_values,df_spring_2014.buy_mount,label='2014')
plt.plot(x_values[:len(df_spring_2015)],df_spring_2015.buy_mount,label='2015')
plt.legend(loc='best')
plt.xticks(ticks=x_values,labels=x_labels,rotation=60)
plt.xlabel('日期',fontsize=16)
plt.ylabel('销量',fontsize=16)
plt.title('春节前30日每日销量情况比较')
plt.show()
春节前30日-前13日销量情况比较
df_spring=pd.DataFrame({'2013':df_spring_2013[:16].buy_mount.sum(),'2014':df_spring_2014[:16].buy_mount.sum(),'2015':df_spring_2015.buy_mount.sum()},index=['销量'])
df_spring_rate=df_spring.pct_change(axis=1)
df_spring_rate.index=['同比增速']
# df_spring,df_spring_rate
df_1=pd.concat([df_spring,round(df_spring_rate*100,2)])
df_1
# pd.concat(df_spring,df_spring_rate)
fig=plt.figure(figsize=[12,8])
ax1=fig.add_subplot(121)
plt.bar(df_1.columns,df_1.loc['销量'],label='销量')
for m,n in zip(df_1.columns,df_1.loc['销量']):
plt.text(m,n+20,n,ha='center')
ax1.set_xlabel('年份')
ax1.set_ylabel('销量')
plt.title('2013-2015年闰月初一到十五销量同比情况')
plt.legend(loc='upper left')
#plt.legend(loc='upper left')
ax2=ax1.twinx()
df_2=df_1.loc['同比增速']
plt.plot(df_2.index,df_2,'r',label='同比增速')
for i,j in zip(df_2[1:].index,df_2[1:].values):
plt.text(i,j,j,ha='left')
ax2.set_ylabel('同比增速')
plt.legend(bbox_to_anchor=(0,0.96),loc='upper left')
plt.show()
2014年春节前同比增速49.9%,而2015的同比增速是43.24%,小于目标值49.9%,所以2015的销量状况并不好。
原因拆解
进一步分析是哪款产品导致的2015年销量未达标。对2014、2015年的产品按商品大类、年份进行分组求和。
df_reason=df.groupby([df.index,df.cat1]).sum()['buy_mount']
df_reason_2014=df_reason.loc['2014-1-1':'2014-1-16'].unstack()
df_reason_2015=df_reason.loc['2015-1-21':].unstack()
df_reason=pd.DataFrame({'2014':df_reason_2014.sum(),'2015':df_reason_2015.sum()},index=df_reason_2014.columns)
df_reason
df_reason_rate=round(df_reason.pct_change(axis=1)*100,2)['2015']
df_reason_rate
width=0.35
fig=plt.figure(figsize=[16,6])
ax1=fig.add_subplot(121)
plt.bar(np.arange(df_reason.shape[0])-width/2,df_reason['2014'],width,data=df_reason,label='2014年销量(左轴)')
plt.bar(np.arange(df_reason.shape[0])+width/2,df_reason['2015'],width,data=df_reason,label='2015年销量(左轴)')
plt.xlabel('产品大类')
plt.ylabel('销量')
plt.title('2014-2015年春节前30-14日各分类销量情况')
plt.legend(loc='upper right')
ax2=ax1.twinx()
plt.plot(range(len(df_reason_rate)),df_reason_rate.values,'r',label='同比增速(右轴)')
for i,j in zip(range(len(df_reason_rate)),df_reason_rate.values):
plt.text(i,j+1,j,ha='left')
plt.legend(bbox_to_anchor=(1,0.89),loc='upper right')
plt.xticks(range(len(df_reason_rate)),df_reason.index,rotation=60)
plt.show()
可知,50008168大类、50014815大类都是增长幅度非常的低,是主要的问题点。而122650008大类的增速接近目标值,且销量占比低,是次要的问题点。
推广计划
由于没有更多的数据,针对以上问题,可以对今年2015年未来14天的走势做基本的预测,发现可提升销量的机会
确定推广时限
对比往年的销量情况,接下来的销量会逐渐降低,需要在接下来的一周做推广计划
确定资源分配
对不同类别的产品查看销售曲线
df_plan=df.groupby([df.index,df.cat1]).sum()['buy_mount']
df_plan_2013=df_plan.loc['2013-1-10':'2013-2-15'].unstack()
df_plan_2014=df_plan.loc['2014-1-1':'2014-2-6'].unstack()
df_plan_2015=df_plan.loc['2015-1-21':].unstack()
df_plan_2013
plt.figure(figsize=[16,12])
for i in range(df_plan_2015.shape[1]):
plt.subplot(3,2,i+1)
plt.plot(range(df_plan_2013.shape[0]),df_plan_2013.iloc[:,i],label='2013')
plt.plot(range(df_plan_2014.shape[0]),df_plan_2014.iloc[:,i],label='2014')
plt.plot(range(df_plan_2015.shape[0]),df_plan_2015.iloc[:,i],label='2015')
plt.legend(loc='best')
plt.grid()
plt.title(f'{df_plan_2013.columns[i]}大类销量情况')
plt.show()
建议:
28大类:2014年的数据当中,销量会有明显的波动周期,隔7-10天会出现一次陡增,需要对比推广计划,确定是活动推广周期还是自然增长。
- 如果为自然增长,未来还会出现1-2次的陡增,单日销量估计在30左右。
- 如果为活动推广所致,参考15年闰月初七至初九。在未来一周内进行一次同类推广活动,对数据监控后再作下一步推广计划
- 作为今年销量最高的大类,可以选择其为推广计划中的核心产品
38大类:2013-2015销量数据都不是很稳定,但2015年有个别日期销量猛增,同样结合今年的推广计划,反推推广是否能够促进38大类的销量。如果有效,可尝试增加一定推广的期限。
168大类和815大类:根据过往数据,直到春节结束,销量会一直下降。参考过往这段时间是否有采取过推广计划。如果经过推广后依旧没有销量的增长,那么有富余资源的情况下再考虑推广这两类。
520大类:除了520大类在14年的年初六有大幅的增长外,其余时间两大类的销量都是非常的低,日均销量为个位数。520大类在15年的销量几乎没有,可以考虑参考14年的年初六进行一次小规模推广。
08大类:虽然看上去曲线很陡峭,但实际销量基本没超过个位数。结合过往推广计划进行判断,如果已经进行过推广,但销量依旧不乐观,则今年可以放弃08大类,不作推广。
总结
- 现状:春节前30日-16日共销售1080罐,同比增速为43.2%,略低于目标的50.54%,销售状况有待提高
- 问题:距离春节还有14天,有504罐的销量缺口,未来一周日均需要销售57罐,年廿四到年三十日均销量需15罐才能达到目标。
- 原因:168、815两个大类增速远低于目标值是核心原因,需要收集渠道等数据才能进一步定位更具体的原因
- 做法:
- 一周内做1-2论推广计划,年廿四或廿五作最后一波冲刺
- 28大类可作为主推产品
- 其次优先推广38大类、520大类
- 在资源有剩余的情况下,再考虑推广168大类、815大类及08大类