数据初览
我们的原始数据是2016年双11美妆产品的活动及销售数据,原始数据为.xlsx,格式包括update_time/id/title/price/店名,共5个字段,其中id为商品的唯一标识,店名为品牌名。
项目说明本次数据分析主要供学习使用,目的在于熟悉Python及相关库,同时练习数据分析的思维方式。
本次分析主要基于描述性统计,暂不包含预测模型。
文字说明会涉及实现的具体细节,而不仅是分析结果的展示。
项目实操
导入模块提前导入Python可能用到的相关模块。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import warnings
warnings.filterwarnings('ignore')
from bokeh.plotting import figure,show,output_file
from bokeh.models import ColumnDataSource
加载数据和数据预处理设置工作路径,pandas导入数据
检查数据是否有缺失值
检查数据类型
对数据进行基本预处理
查看数据
# 1.读取数据
import os
os.chdir(r'E:\打折套路分析')
df = pd.read_excel('双十一淘宝美妆数据.xlsx',sheet_name = 0)
df.fillna(0,inplace = True)
我们对原始数据做一些处理,将其变为易于观察处理的形式,将日期数据提取出来并改为索引。再次查看更改后的中间表data1 。
#加载日期,提取销售日期
df.index = df['update_time']
df['date'] = df.index.daydata1 = df[['id','title','店名','date']]
我们要了解双11活动中这些商品有哪些促销活动,共活动了几天。
#统计不同商品的销售开始和截止日期
d1 = data1[['id','date']].groupby(by = 'id').agg(['min','max'])['date']
双十一当天在售的商品占比情况,找出在双11当天在售的商品明细。
#统计双十一当天售卖的商品id
id_11 = data1[data1['date'] == 11]['id']
d2 = pd.DataFrame({'id':id_11,
'双十一当天是否售卖':True})
计算双11当天参加活动商品占比并得出结论
#合并数据
id_data = pd.merge(d1,d2,left_index=True,right_on='id',how = 'left')
id_data.fillna(False,inplace=True)
n = len(d1)
n_11 = len(d2)
n_pre = n_11 / n
print('双十一当天参与活动的商品有%i个,占比为%.2f%%'%(n_11,n_pre*100))
双十一当天参与活动的商品有2336个,占比为66.70%。约1/3的商品在双11当天并没有上架销售,只是在利用双11的热度在进行商品促销。
为了分析没参加双十一的产品去向,我们首先对所有商品id依据其上架时间进行分类。
A. 11.11前后及当天都在售 → 一直在售
B. 11.11之后停止销售 → 双十一后停止销售
C. 11.11开始销售并当天不停止 → 双十一当天上架并持续在售
D. 11.11开始销售且当天停止 → 仅双十一当天有售
E. 11.5 - 11.9 → 双十一前停止销售
F. 仅11.11当天停止销售 → 仅双十一当天停止销售
G. 11.12开始销售 → 双十一后上架
H. 11.10下架 → 可能11.11售罄 以后单独分析
#3.商品销售节奏分类
id_data['type'] = '待分类'
id_data['type'][(id_data['min']<11) & (id_data['max']>11)] = 'A'
id_data['type'][(id_data['min']<11) & (id_data['max']==11)] = 'B'
id_data['type'][(id_data['min']==11) & (id_data['max']>11)] = 'C'
id_data['type'][(id_data['min']==11) & (id_data['max']==11)] = 'D'
id_data['type'][id_data['双十一当天是否售卖'] == False] = 'F'
id_data['type'][id_data['max']<11] = 'E'
id_data['type'][id_data['min']>11] = 'G'
然后再观察一下这个中间表(尽量每做一个中间表都先去看看是否有问题)。
分类可视化,做饼图。
# 计算不同类别的商品数量
result1 = id_data['type'].value_counts()
#result1 = result1.loc[['A','B','C','D','E','F','G']]
from bokeh.palettes import brewer
colori = brewer['Greens'][7]
plt.axis('equal')
plt.pie(result1,labels = result1.index,
autopct = '%.2f%%',colors = colori,
startangle=90,radius=2,counterclock=False)
从饼图可以看出来,正常一直在售并参与双11活动的商品占比只有50%左右,而且有24%的商品在双十一前停止了销售,为什么会的出这么奇怪的比例呢,我们继续分析。
我们来分析没参加双十一当天活动的这些商品去向如何,经过思考也对其做个分类。
con1 → 暂时下架(F)
con2 → 重新上架(同一个id可能有不同title,也就是换个名头重新上架)
con3 → 预售(预售商品的title中包含“预售”二字)
con4 → 彻底下架,可忽略
#4.未参与双十一当天活动商品去向如何
id_not11 = id_data[id_data['双十一当天是否售卖'] == False]
# 找到双十一当天未参与活动的商品的对应的原始数据
df_not11 = id_not11[['id','type']]
data_not11 = pd.merge(df_not11,df,on = 'id',how = 'left')
#筛选出暂时下架的商品
id_con1 = id_data['id'][id_data['type'] == 'F'].values
#筛选出重新上架商品
data_con2 = data_not11[['id','title','date']].groupby(by=['id','title']).count()
title_count = data_con2.reset_index()['id'].value_counts()
id_con2 = title_count[title_count > 1].index
#筛选出预售商品
data_con3 = data_not11[data_not11['title'].str.contains('预售')]
id_con3= data_con3['id'].value_counts().index
print('未参与双十一当天活动的商品中,有%i个为暂时下架商品,
有%i个为重新上架商品,有%i个为预售商品'
%(len(id_con1),len(id_con2),len(id_con3)))
未参与双十一当天活动的商品中,有242个为暂时下架商品,有110个为重新上架商品,有453个为预售商品
参加双十一活动的品牌及其商品数量的分布
真正参加活动的商品 = 双十一当天在售的商品 + 预售商品,我们按不同品牌分类,找到各个品牌真正参加活动的商品的数量和所占比例。
#5.真正参与双十一当天活动的商品及品牌情况
#真正参与双十一当天活动的商品 = 双十一当天在售商品 + 预售商品 (可以尝试接过去重)
id_11sale_final = np.hstack((id_11,id_con3))
result2_i = pd.DataFrame({'id':id_11sale_final})
#不同品牌参与双十一当天活动的数量
x1 = pd.DataFrame({'id':id_11})
x1_df = pd.merge(x1,df,on = 'id',how = 'left')
brand_11sale = x1_df.groupby('店名')['id'].count()
#不同品牌预售的数量
x2 = pd.DataFrame({'id':id_con3})
x2_df = pd.merge(x2,df,on='id',how = 'left')
brand_ys = x2_df.groupby('店名')['id'].count()
#最终结果
result2_data = pd.DataFrame({'当天参与活动商品数量':brand_11sale,
'预售商品数量':brand_ys})
result2_data['总量'] = result2_data['当天参与活动商品数量'] + result2_data['预售商品数量']
result2_data.sort_values('总量',inplace = True,ascending = False)
拿到数据后绘图。
from bokeh.models import HoverTool
from bokeh.core.properties import value
lst_brand =result2_data.index.tolist()
lst_type = result2_data.columns.tolist()[:2]
colors = ['red','green']
result2_data.index.name = 'brand'
result2_data.columns = ['sale_on11','presell','sum']
source = ColumnDataSource(result2_data)
hover = HoverTool(tooltips = [('品牌','@brand'),
('双十一当天参与活动商品数量','@sale_on11'),
('预售商品数量','@presell'),
('真正参与双十一商品总数','@sum')
])
output_file('参与双十一活动品牌数量.html')
p = figure(plot_width = 800,plot_height = 300,
title = '各商品参与双十一说动情况',
#tools = [hover,'reset,xwheel_zoom,pan,crosshair']
)
p.vbar_stack(lst_type,x = 'presell',source = source,
width = 0.8,color = colors,alpha = 0.7,
legend = [value(x) for x in lst_type],
muted_color = 'black',muted_alpha = 0.2
)
show(p)
针对每个商品,评估其打折情况
筛选并加工数据。
data2 = df[['id','title','店名','date','price']]
#pd.cut() 左开右闭区间
data2['period'] = pd.cut(data2['date'],[4,10,11,14],
labels = ['双十一前','双十一当天','双十一后'])
# 2。针对每个商品,评估打折情况
#筛选出是否打折数量
price = data2[['id','price','period']].groupby(['id','price']).min()
price.reset_index(inplace=True)
id_count = price['id'].value_counts()
id_type1 = id_count[id_count == 1].index
id_type2 = id_count[id_count != 1].index
#3.针对打折商品,折扣率是多少
result3_data1 = data2[['id','price','period','店名']].groupby(['id','period']).min()
result3_data1.reset_index(inplace=True)
result3_data1.dropna(axis = 0,inplace = True)
result3_before11 = result3_data1[result3_data1['period'] == '双十一前']
result3_at11 = result3_data1[result3_data1['period'] == '双十一当天']
#折扣率
result3_data2 = pd.merge(result3_before11,result3_at11,on = 'id',how = 'inner')
result3_data2['zkl'] = result3_data2['price_y'] / result3_data2['price_x']
用bokeh绘制折线图:x轴为折扣率,y轴为商品数量占比
商品折扣率统计,每个折扣区间与总打折商品占比
bokeh_data = result3_data2[['id','zkl']]
bokeh_data['zkl_range'] = pd.cut(bokeh_data['zkl'],bins=np.linspace(0,1,21))
bokeh_data2 = bokeh_data.groupby('zkl_range').count()
bokeh_data2 = bokeh_data.groupby('zkl_range').count().iloc[:-1]
#计算折扣区间占比情况
bokeh_data2['zkl_pre'] = bokeh_data2['zkl'] / bokeh_data2['zkl'].sum()
bokeh_data2.index = bokeh_data2.index.astype('str')
source1 = ColumnDataSource(bokeh_data2)
lst_zkl = bokeh_data2.index.tolist()
hover1 = HoverTool(tooltips = [('折扣率','@zkl_pre')])
p1 = figure(x_range = lst_zkl,plot_width = 900,plot_height = 350,
title = '商品折扣率统计',
tools = [hover1,'pan,reset,xwheel_zoom,crosshair'],
)
p1.line(x = 'zkl_range',y = 'zkl_pre',source = source1,line_color = 'black',line_dash = 'dotted')
p1.circle(x = 'zkl_range',y = 'zkl_pre',source = source1,size = 8,color = 'red',alpha = 0.8,
legend = '折扣率')
#p1.legend.click_policy = 'mute'
show(p1)
分析上图,我们可以看到商品折扣率主要集中在五折和九折,二者共占50%以上,而且打折商品数量占比很小。
#4.按照品牌分析打折力度
from bokeh.transfrom import jitter
brand = result3_data2['店名_x'].unique().tolist()
bokeh_data3 = result3_data2[['id','zkl','店名_x']]
bokeh_data3['zkl'] = bokeh_data3['zkl'][bokeh_data3['zkl'] < 0.96]
source2 = ColumnDataSource(bokeh_data3)
output_file('不同品牌打折力度.html')
hover2 = HoverTool(tooltips = [('折扣率','@zkl')])
p2 = figure(y_range = brand,plot_width = 900,plot_height = 750,
title = '不同品牌的折扣情况',
tools = [hover2,'pan,reset,xwheel_zoom,crosshair'])
p2.circle(x = 'zkl',
y = jitter('店名_x',width = 0.7,range = p2.y_range),
source = source2,alpha = 0.3)
show(p2)
分析上图,我们可以看到实际打折率高的商品主要集中在国产几个品牌,相本宜草和佰草集打折商品最多。而国外一些大牌例如兰芝,SKII等几乎不打折。所以想要在双11捡大牌的童鞋可以打消这个念头了。
分析商家打折套路
筛选各品牌的折扣商品比例和平均折扣率
#3.套路分析
# 筛选不同品牌的折扣情况
data_zk = result3_data2[result3_data2['zkl']<0.95]
result4_zkld = data_zk.groupby('店名_y').mean()['zkl']
n_dz = data_zk['店名_y'].value_counts()
n_zs = result3_data2['店名_y'].value_counts()
result4_dzspbl = pd.DataFrame({'打折商品数':n_dz,
'商品总数':n_zs})
result4_dzspbl['参与打折商品比例'] = result4_dzspbl['打折商品数'] / result4_dzspbl['商品总数']
result4_dzspbl.dropna(inplace=True)
result4_sum = result2_data.copy()
#合并数据
result4_data = pd.merge(pd.DataFrame(result4_zkld),result4_dzspbl,left_index = True,right_index = True)
result4_data = pd.merge(result4_data,result4_sum,left_index = True,right_index = True)
划分四个象限,利用bokeh制图
#bokeh,制图
from bokeh.models.annotations import Span,Label,BoxAnnotation
bokeh_data4 = result4_data[['zkl','sum','参与打折商品比例']]
bokeh_data4.columns = ['zkl','amount','pre']
bokeh_data4['size'] = bokeh_data4['amount'] * 0.03
source4 = ColumnDataSource(bokeh_data4)
output_file('各品牌双十一打折情况.html')
x_mean = bokeh_data4['pre'].mean()
y_mean = bokeh_data4['zkl'].mean()
hover4 = HoverTool(tooltips = [
('品牌','@index'),
('折扣率','@zkl'),
('商品总数','@amount'),
('参与打折商品比例','@pre')
])
p4 = figure(plot_width = 600,plot_height = 600,
title = '各品牌双十一打折情况',
tools = [hover4,'pan,reset,wheel_zoom,crosshair,box_select'])
p4.circle(x = 'pre',y = 'zkl',source = source4,size = 'size',
fill_color = 'red',line_color = 'black',fill_alpha = 0.6,line_dash = 'dotted'
)
p4.xgrid.grid_line_dash = [6,4]
p4.ygrid.grid_line_dash = [6,4]
# 绘制辅助线
x = Span(location = x_mean,dimension = 'height',line_color = 'red',line_alpha = 0.8,line_width = 3)
y = Span(location = y_mean,dimension = 'width',line_color = 'red',line_alpha = 0.8,line_width = 3)
p4.add_layout(x)
p4.add_layout(y)
#第一象限
bg1 = BoxAnnotation(bottom = y_mean,left = x_mean,fill_alpha = 0.1,fill_color = 'olive')
label1 = Label(x = 0.7,y = 0.78,text = '大量少打折',text_font_size = '10pt')
p4.add_layout(bg1)
p4.add_layout(label1)
#第二象限
bg2 = BoxAnnotation(bottom = y_mean,right = x_mean,fill_alpha = 0.1,fill_color = 'olive')
label2 = Label(x = 0.2,y = 0.78,text = '少量少打折',text_font_size = '10pt')
p4.add_layout(bg2)
p4.add_layout(label2)
#第三象限
bg3 = BoxAnnotation(top = y_mean,right = x_mean,fill_alpha = 0.1,fill_color = 'olive')
label3 = Label(x = 0.2,y = 0.55,text = '少量大打折',text_font_size = '10pt')
p4.add_layout(bg3)
p4.add_layout(label3)
#第四象限
bg4 = BoxAnnotation(top = y_mean,left = x_mean,fill_alpha = 0.1,fill_color = 'olive')
label4 = Label(x = 0.7,y = 0.55,text = '大量大打折',text_font_size = '10pt')
p4.add_layout(bg4)
p4.add_layout(label4)
show(p4)
通过上图我们可以分析出
少量大打折:雅诗兰黛、兰蔻、薇姿、悦诗风吟、欧珀莱
大量少打折:欧莱雅、玉兰油、美宝莲、妮维雅、蜜丝佛陀、美加净
大量大打折:自然堂、相宜本草、佰草集
不打折:SKII、倩碧、兰芝、娇兰、植村秀、资生堂、雅漾、雪花秀
通过这个项目,我们可以看到双11商家套路多多,我们要理性消费,管好自己的钱包哦。