引言
笔者在商场购物时,无意听见某潮牌店员工们的对话 “上一个月拼得太猛了,现在月初我想放松一下…” “对,是该好好放松了,上个月底前的一段时间我的分店就完成了销售任务,就提前放松啦,达标了还那么拼的话,本月销售做得太多下月目标就会被老板拔高”。
上面这两种情况在零售行业被称为 “月初放松 & 月末踩刹车(零售业术语)”,这两种现象对销售最大化都是一种伤害,零售业的销售额是靠大家每天不断的努力和辛苦付出换来的(怎么感觉自己像个传销…),总之出现了类似心态的化管理者也有原因,根据利他思维,一个不懂得为员工们考虑的老板只希望员工疯狂干活,在薪酬和福利上又拼命剥削,员工出现这样的心理也很正常。另外,以下几种幼稚但人之常情的原因也可导致销售额数据异常
- 发现月底根本完不成目标,于是提前给自己判刑,不再努力了。
- 店铺员工藏销售,本该本月录入系统的销售,人为转移到下月初才录入系统
- 亲自采访笔者堂哥的妻子,在老家珠宝店做销售,发现月末完不成了,店铺内的员工便合伙用自己的钱购买,下月初才将虚增部分做退货处理
- 店员赚黑心钱,顾客没有会员卡,店员用自己开的会员卡进行结账,然后把差价收入自己的囊中,我恨
上面这几段引出了神奇的黄氏曲线——单位权重(销售)值曲线的其中一个经典作用:追踪销售过程,当然这神奇的曲线还可用到促销活动的分析及评估,特殊事件量化处理,新品上市的分析及评估,评估对手等。单位权重这种概念属于零售业中比较精细化的管理概念,要想使用好这些概念,首要的条件是必须精通业务,所以需要分析师们多去业务现场。事不宜迟,快开始!(文末有精彩延伸)
当然,在正式开始本博文的阅读前,我们最好已经熟悉以下密码的获得方式,当然,直接开始也没问题。寻找零售密码 | Python 实现周权重指数计算&可视化
单位权重(销售)值曲线
直接拿周一的销售值与周六对比显然不合理,毕竟周六大家下班了,休息了,有时间出来玩了,销售额比周一要高也在情理之中,除非你是周末休息的写字楼和一些特殊的奢侈品店。但只要我们在比较时将两者都除以它对应的日权重,效果就会好很多:高销售额÷高权重 VS 中销售额÷中权重 VS 低销售额÷低权重 ,这么一看合理多了。
把每一天的销售额分别除以当日的权重指数,就变成了单位权重(销售)值曲线了
所以销售额曲线可以是一条波动幅度比较大的曲线,而权重曲线则显得相对平稳。如果一个零售店铺的每日销售额是绝对服从周权重指数的规律,那对应的权重曲线则将是一条绝对的水平直线,而这种情况是根本不可能出现的,正常的权重曲线是一条围绕某个值变化的曲线,正是这种变化给我们提供了去东西某些营运现象的可能,比如某月的某个 周六销售额÷周六权重 < 周一销售额÷周一权重,那证明周一的销售情况相对优于周六,如果后者比前者大很多,那可能该月的这个周一有些事情发生了,这种变化说明有某种业务逻辑暗含其中,相面我们将他们拆开看,放入一些业务场景
销售追踪
下面这幅图从左至右来看,展示了引言中说到的疑似月末踩刹车,月末虚增销售,月初放松等现象
怎么预防??
销售首先是追踪出来的,其次才是分析出来的。目前还没有办法预测这种现象的发生(想想也正常,预测个销量还有得说,这么复杂的人类行为怎么预测),管理人员可以再发现权重曲线数据向下突变的第一天就应该去找原因,连续两天突变就应该请这个店长喝茶和谈心了。所以我们可以这么来看,偶尔1~2天的这种突变是店长的问题,超过两天还出现这种情况就是管理者的问题了,因为管理者没有去中止这种损害销售额的事情。
特殊事件的量化处理
上图是某店铺三八妇女节促销活动的销售权重曲线情况(整个3月),不过单看销售曲线和单位权重曲线还是会有些不够直观,如果把权重曲线换一种表达方式——黄氏曲线(黄氏曲线为权重曲线同时间段的平均值,以后会再详细讲计算,python 没有这样的函数,可以自己按需构造),这样就很明显了,并且这条曲线只是和相邻的时期对比,所以时效性和可对比性都很强。促销爆发度和促销衰减度
促销活动的分析与评估
促销活动的结果通常有几种情况,下图的黄氏曲线类型会一一展示,但要注意的是,无论是线上还是线下的促销活动,大多存在以下一些数据分析方面的误区:
- 只和促销目标对比,完成目标的促销活动就算成功
- 只进行促销期的数据对比,且对比误差较大
- 只关注促销前和促销中的数据,从来不关注促销后的数据
这也不难理解,比如一些软文促销,9.9 训练营,标题党等,短时间内吸引了大量用户购买,促销期结束后因为质量原因导致口碑下降,一下子把品牌搞坏了,呈现比促销钱还差劲的销量也就不足为奇了(相当于透支了后期销售的促销活动)。
我卸载网易和今日头条并不是没有有原因的。
通过这种分析可以发现什么类型的促销更适合什么类型的店铺或品类。
新品上市
在使用这种方法时,不能简单地将爆发或衰减看成促销的影响,有可能在促销的同时还有诸如新品上市等其他因素的作用,虽然黄氏曲线的处理方法已经尽量降低了其他因素的影响度,但还是需要综合分析。另外促销前和促销后的周期一般取7天,特殊情况例外。(一句话:使用黄氏曲线时一定要注意是“突发”状态,要有非常清晰的时间节点、非常明确的时间信息状态才可以使用;建议同时画出权重曲线和黄氏曲线)
项目实战
流程拆解
生成周权重指数
拼接两表,创建权重曲线
可视化
数据是笔者自己伪造的,只为展示思路,真实情况大多也这么用
自写库快捷实现
源码呈现
源码是笔者自己写的,技术不精,还有许多值得优化的地方,用到的都是 Python 数据分析中的基本操作,关键是看思路和一些处理的细节,加油!
import pandas as pd
import numpy as np
# 提高输出效率库
from IPython.core.interactiveshell import InteractiveShell # 实现 notebook 的多行输出
InteractiveShell.ast_node_interactivity = 'all' #默认为'last'
# 动态可视化库们的使用
# cufflinks 实现子图布局需要付出的努力
import cufflinks as cf
""" 下面几行为示例,想不起来的时候看,都是为了布局 """
# from plotly import tools
# import chart_studio.plotly as cp
# import plotly.graph_objs as go
# fig = tools.make_subplots(rows=3, cols=1)
# # 子图布局
# fig.add_traces(p1); fig.add_traces(p2); fig.add_traces(p3)
# # Decorations
# fig['layout'].update(hight=900, width=600)
# cy.plot(fig)
# ---------------------- 周权重指数 -----------------------------
def weightCreated(data, standard=None):
""" 传入指定格式的数据,返回该分店的周权重表,可自定义标准 """
daily_mean = {'Mon': np.mean(data['Mon']), 'Tue': np.mean(data['Tue'])
, 'Wed': np.mean(data['Wed']), 'Thu': np.mean(data['Thu'])
, 'Fri': np.mean(data['Fri']), 'Sau': np.mean(data['Sau'])
, 'Sun': np.mean(data['Sun'])}
weight_index = ['平均日销售额', '日销售权重指数']
weight_columns = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sau', 'Sun']
weight = pd.DataFrame(data=daily_mean,
index=weight_index, columns=weight_columns)
weight['Total'] = np.sum(weight.loc['平均日销售额', :])
if standard:
weight.loc['日销售权重指数', :] = \
weight.loc['平均日销售额', :]*standard / weight.loc['平均日销售额', 'Total']
return weight
# 若没有标准将销售中销售额最低一天的数据的日权重指数设置为 1.0,其余参照
base = weight.iloc[0, :].min()
weight.loc['日销售权重指数', :] = weight.loc['平均日销售额', :] / base
return weight
# ------------------------- 单位权重(销售)值曲线 ----------------------
def weight_curve_Created(month_data, weight_table):
""" 传入指定格式的某分店的 月销售数据 与 该分店周权重指数表
return 黄氏曲线表,用于后续可视化 """
# 添加周次
month_data['时间'] = pd.to_datetime(month_data['时间'])
month_data['weekday'] = month_data['时间'].dt.weekday # 匹配出来后还应该加1(中国文化)
month_data['weekday'] = month_data['weekday'] + 1
# week_label
week_label = {1:'Mon', 2:'Tue', 3:'Wed', 4:'Thu', 5:'Fri', 6:'Sau', 7:'Sun'}
month_data.weekday = month_data.weekday.map(week_label)
# daily_weight
daily_weight = weight_table.T['日销售权重指数'][:-1].reset_index()
daily_weight.rename(columns = {'index': 'weekday'}, inplace=True)
weight_curve = pd.merge(month_data, daily_weight, on='weekday', how='inner')
# 排序,因 merge 后时间列顺序自行被分组了,有点乱,所以需要重新排序
weight_curve = weight_curve.sort_values(by='时间')
# 单位权重(销售)值
weight_curve['权重值曲线'] = weight_curve['日销售额'] / weight_curve['日销售权重指数']
# 构造更好的 x 轴坐标
weight_curve['month_day'] = \
weight_curve['时间'].apply(lambda x : x.strftime('%m-%d'))
# 字符串化一行后再拼接
weight_curve['month_day_week'] = \
weight_curve['month_day'].str.cat(weight_curve['weekday'], sep='-')
return weight_curve