(电商)唯品会双十一促销活动复盘——数据分析
项目背景:
唯品会是一个专门做特卖的网站,什么是特卖呢。特卖一般是指在特定的时间段里,以优惠的价格出售指定的商品,一般以商城或者专卖店为多。该模式在线下早已存在(比如商场促销、街边的尾货甩卖),在国外成熟的大商场内也有针对滞销商品的打折特卖,如奥特莱斯。特卖一般是商家清库存,不过也有一些专门生产商品做特卖的商家。
特卖行业也是有个真实存在的产业链,只是因为快速分销渠道,地理位置等关系,大多数都集中在一线城市,部分生活在一线城市的都基本或多或少去过几次各个品牌的特卖仓,但是二三线甚至四五线城市的就比较难接触到,后来就有一群人成了品牌搬运工,和各大品牌联系通过微信等渠道快速分销大牌库存,达到快速低价消除库存,加快周转回笼资金等目的。
在货源上,由于品牌尾货具备天然的清仓需求,是折扣零售最常见的货源,但实际上,只要成本足够低,新品首发、定制包销、自有品牌均可以成为折扣特卖零售的可持续货源。成立初期,唯品会货源以尾货为主,但随着唯品会在电商领域的不断发展,新品和专供品的占比不断提升,早在 2016 年 Q2 分析中,唯品会当季新品和平台特供品就已经占 37%了。
此次分析的目标:
评估每次促销活动的结果,并根据情况优化商品结构,以便让自己的商品卖的更好。
分析流程:
- 1、总体运营指标
- 2、从价格区间找出表现不好的产品,优化商品结构
- 3、从折扣区间来找出表现不好的产品,优化商品结构
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
〇、数据清洗与准备
1、读取数据,更改列名
import sqlalchemy
engine = sqlalchemy.create_engine('mysql+pymysql://ID:PASSWORD@localhost:3306/datdabase')
# 读取数据
# 商品信息表
sql_cmd = "select * from sales_info1"
# 执行sql语句,获取数据
dt= pd.read_sql(sql=sql_cmd, con=engine)
dt.rename(columns={
"sale_name":"商品名",
"sale_price":"售卖价",
"tag_price":"吊牌价",
"discout":"折扣率",
"stocks":"库存量",
"stocks_value":"货值",
"cost_price":"成本价",
"profit_rate":"利润率",
"skus":"SKU"},
inplace=True)
dt.head()
商品名 | 售卖价 | 吊牌价 | 折扣率 | 库存量 | 货值 | 成本价 | 利润率 | SKU | |
---|---|---|---|---|---|---|---|---|---|
0 | A001 | 15 | 70 | 0.214286 | 501 | 35070 | 14 | 0.066667 | 2 |
1 | A002 | 236 | 610 | 0.386885 | 423 | 258030 | 75 | 0.682203 | 1 |
2 | A003 | 473 | 1253 | 0.377494 | 415 | 519995 | 394 | 0.167019 | 1 |
3 | A004 | 320 | 835 | 0.383234 | 624 | 521040 | 279 | 0.128125 | 2 |
4 | A005 | 15 | 82 | 0.182927 | 179 | 14678 | 27 | -0.800000 | 1 |
# 读取数据
# 商品热度表
sql_cmd = "select * from sales_info2"
# 执行sql语句,获取数据
dt2 = pd.read_sql(sql=sql_cmd, con=engine)
dt2.rename(columns={
"sale_name":"商品名",
"uvs":"UV数",
"collections":"收藏数",
"carts":"加购物车数"},
inplace=True)
dt2.head()
商品名 | UV数 | 收藏数 | 加购物车数 | |
---|---|---|---|---|
0 | A001 | 10926 | 48 | 372 |
1 | A002 | 13124 | 84 | 193 |
2 | A003 | 25657 | 45 | 173 |
3 | A004 | 20833 | 5 | 273 |
4 | A005 | 19371 | 71 | 356 |
# 读取数据
# 用户销售明细表
sql_cmd = "select * from sales_info3"
# 执行sql语句,获取数据
dt3 = pd.read_sql(sql=sql_cmd, con=engine)
dt3.rename(columns={
"user_id":"用户id",
"buy_date":"购买日期",
"sale_name":"商品名",
"buy_cons":"购买数量",
"buy_price":"购买单价",
"cost_price":"购买金额",
"is_tui":"是否退货",
"tui_cons":"退货件数",
"tui_price":"退货金额"},
inplace=True)
dt3['是否退货']=dt3["是否退货"].map({
"是":1,"否":0})
dt3.head()
用户id | 购买日期 | 商品名 | 购买数量 | 购买单价 | 购买金额 | 是否退货 | 退货件数 | 退货金额 | |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 20191111 | F001 | 1 | 920.0 | 920.0 | 1 | 1 | 920.0 |
1 | 2 | 20191111 | B007 | 2 | 548.0 | 1096.0 | 0 | 0 | 0.0 |
2 | 2 | 20191111 | E007 | 1 | 930.0 | 930.0 | 1 | 1 | 930.0 |
3 | 3 | 20191111 | A004 | 2 | 320.0 | 640.0 | 1 | 2 | 640.0 |
4 | 3 | 20191111 | H007 | 2 | 750.0 | 1500.0 | 0 | 0 | 0.0 |
2、合并商品信息表和商品热度表数据
# 把商品信息加上该商品的热度信息
# 得到基础的商品信息,以及商品的一些热度信息:加购物车数量,收藏数量、uv数
dt_products=pd.merge(dt1,dt2,how='left',on='商品名')
dt_products.head()
商品名 | 售卖价 | 吊牌价 | 折扣率 | 库存量 | 货值 | 成本价 | 利润率 | SKU | UV数 | 收藏数 | 加购物车数 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | A001 | 15 | 70 | 0.214286 | 501 | 35070 | 14 | 0.066667 | 2 | 10926 | 48 | 372 |
1 | A002 | 236 | 610 | 0.386885 | 423 | 258030 | 75 | 0.682203 | 1 | 13124 | 84 | 193 |
2 | A003 | 473 | 1253 | 0.377494 | 415 | 519995 | 394 | 0.167019 | 1 | 25657 | 45 | 173 |
3 | A004 | 320 | 835 | 0.383234 | 624 | 521040 | 279 | 0.128125 | 2 | 20833 | 5 | 273 |
4 | A005 | 15 | 82 | 0.182927 | 179 | 14678 | 27 | -0.800000 | 1 | 19371 | 71 | 356 |
3、合并商品信息表和商品热度表数据和用户销售明细表
dt3.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8036 entries, 0 to 8035
Data columns (total 9 columns):
用户id 8036 non-null object
购买日期 8036 non-null int64
商品名 8036 non-null object
购买数量 8036 non-null int64
购买单价 8036 non-null float64
购买金额 8036 non-null float64
是否退货 8036 non-null int64
退货件数 8036 non-null int64
退货金额 8036 non-null float64
dtypes: float64(3), int64(4), object(2)
memory usage: 565.2+ KB
# 统计每个商品的一个销售情况
product_sales=dt3.groupby('商品名').agg({
'购买数量':sum,
'购买金额':sum,
'退货件数':sum,
'退货金额':sum,
'购买单价':np.mean,
'用户id':pd.Series.nunique}).reset_index()
product_sales.rename(columns={
'购买数量':'商品销售数量',
'购买金额':'商品销售金额',
'是否退货':'商品退货数量',
'退货金额':'商品退货金额',
'购买单价':'商品销售单价',
'用户id':'购买用户数量'},inplace=True)
product_sales.head()
商品名 | 商品销售数量 | 商品销售金额 | 退货件数 | 商品退货金额 | 商品销售单价 | 购买用户数量 | |
---|---|---|---|---|---|---|---|
0 | A001 | 185 | 2775.0 | 59 | 885.0 | 15.0 | 116 |
1 | A002 | 146 | 34456.0 | 31 | 7316.0 | 236.0 | 87 |
2 | A003 | 144 | 68112.0 | 31 | 14663.0 | 473.0 | 94 |
3 | A004 | 172 | 55040.0 | 56 | 17920.0 | 320.0 | 111 |
4 | A005 | 122 | 1830.0 | 32 | 480.0 | 15.0 | 81 |
# 合并商品信息
dt_product_sales=pd.merge(dt_products,product_sales,how='left',on='商品名')
dt_product_sales.head()
商品名 | 售卖价 | 吊牌价 | 折扣率 | 库存量 | 货值 | 成本价 | 利润率 | SKU | UV数 | 收藏数 | 加购物车数 | 商品销售数量 | 商品销售金额 | 退货件数 | 商品退货金额 | 商品销售单价 | 购买用户数量 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | A001 | 15 | 70 | 0.214286 | 501 | 35070 | 14 | 0.066667 | 2 | 10926 | 48 | 372 | 185 | 2775.0 | 59 | 885.0 | 15.0 | 116 |
1 | A002 | 236 | 610 | 0.386885 | 423 | 258030 | 75 | 0.682203 | 1 | 13124 | 84 | 193 | 146 | 34456.0 | 31 | 7316.0 | 236.0 | 87 |
2 | A003 | 473 | 1253 | 0.377494 | 415 | 519995 | 394 | 0.167019 | 1 | 25657 | 45 | 173 | 144 | 68112.0 | 31 | 14663.0 | 473.0 | 94 |
3 | A004 | 320 | 835 | 0.383234 | 624 | 521040 | 279 | 0.128125 | 2 | 20833 | 5 | 273 | 172 | 55040.0 | 56 | 17920.0 | 320.0 | 111 |
4 | A005 | 15 | 82 | 0.182927 | 179 | 14678 | 27 | -0.800000 | 1 | 19371 | 71 | 356 | 122 | 1830.0 | 32 | 480.0 | 15.0 | 81 |
一、总体运营情况评价
总体运营部分,主要关注销售额、售卖比、UV、转化率等指标,其他指标作为辅助指标。销售额用来和预期目标做对比,售卖比用来看商品流转情况。
- GMV:销售额,在唯品会里称为到手价。
- 实销:GMV – 拒退金额。
- 销量:累计销售量(含拒退)。
- 客单价:GMV / 客户数,客单价与毛利率息息相关,一般客单价越高,毛利率越高。
- UV:商品所在页面的独立访问数。
- 转化率:客户数 / UV。
- 折扣率:GMV / 吊牌总额(吊牌总额 = 吊牌价 * 销量),在日常工作中,吊牌额是必不可少的。
- 备货值:吊牌价 * 库存数。
- 售卖比:又称售罄率,GMV / 备货值。
- 收藏数:收藏某款商品的用户数量。
- 加购数:加购物车人数。
- SKU数:促销活动中的SKU计数(一般指货号)。
- SPU数:促销活动中的SPU计数(一般指款号)。
- 拒退量:拒收和退货的总数量。
- 拒退额:拒收和退货的总金额