数据分析学习,印度超市销售数据(一)数据分析

309 阅读13分钟

摘要与总结

学习过程的整理。 该数据集较为简单全面,可从多维度进行思考处理,适合练习。
该次内容主要从不同角度,相对全面的挖掘了各个方面的信息; 可视化仅包含一些观察图,在后面会整理出一些简单的pyecharts可视化内容。

数据集来自阿里云天池 tianchi.aliyun.com/dataset/dat…
需要可以自取。

(备注:由于该数据集并不大,在实际分析上,可能统计意义不高。但该次练习中,依然将其视为一个数量足够大的样本来看待,主要抓取其趋势上来进行一定的业务判断,锻炼数据思维)

基于印度超市销售数据的简要分析及可视化

分析思路

查阅资料,寻找有关超市销售的各种模型指标;或根据实际,确定分析方向,自行决定指标内容。

在此我选择较为普遍但较适用的“人货场”模型,因笔者为初学者,选择其中比较常用的指标数据自行构建下图。

在一般分析流程中,可以采取这个思路。但实际分析上,我选择使用了一个整体到部分的趋势,中心思想围绕着为什么会出现这种销售情况,如何获得更高的利润来思考处理数据,因而下图仅作为我的参考方向

超市销售分析.png

分析工具

在该数据集中,数据量并不大,推荐采用excel处理,也较为方便。
但此次分析,为了熟悉使用Python的pandas库和其他可视化工具,且本身数据集格式较为规整,处理内容不多,数据清洗难度不高,所以采用了Python jupyter

分析内容

0.库的导入

import numpy as np 
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.rcParams['font.sans-serif']=['SimHei'] #画图字体为黑体
plt.rcParams['axes.unicode_minus']=False   #显示负数
%matplotlib inline

1.数据的读取和清洗

orderinfo = pd.read_csv('C:/Users/86131/Desktop/List of Orders.csv')
orderinfo.info()
#导入订单数据并查看基本信息

WY6WUF}3C`@YL50HIXF~X{S.png

orderinfo.isnull().any()  #确定是否具有空值
orderinfo.duplicated()  #确定有无重复

_CKNE77TPMKRK$B}ZK%X(`I.png

1)_(EQ5YV2%GQX2OD2Q(}2Q.png

可见数据较为规范,但是注意到基本信息处,时间并不是相对应的时间格式,将其变为标准的时间格式

orderinfo['OrderDate'] = pd.to_datetime(orderinfo['OrderDate'],format = '%Y/%m/%d')
orderinfo.head()
orderinfo.info()

图片.png

图片.png

同理,导入销售数据进行类似的处理,此处省略。
数据集中还有每类商品的销售目标,需要联立使用可以导入分析,此文仅针对销售数据进行分析,故忽略

2.数据分析

注:详细的订单数据我导入时命名为priceinfo

- 首先查看整体情况,商品与地区的整体销售趋势,利润情况是怎样的
- 对于商品
priceinfo.head()

图片.png

#以商品大类为索引,查看销售额,利润,销量三个指标情况
pricept = priceinfo.drop(columns=['OrderID'])
pricept = pricept.pivot_table( index = 'Category',   
              values = ['Amount','Profit','Quantity'],
              aggfunc = {'Profit' : 'sum',
                        'Amount':'sum',
                         'Quantity':'sum'
                        })
pricept

图片.png

  • 进行简单的可视化

作出利润饼图:

price_pie = pricept.apply(lambda x : x/sum(x))
price_pie.Profit.plot.pie(autopct="%0.2f%%") #标签保留两位小数

图片.png

做出聚合数据的柱状图:

price_pg.plot.bar(title = '各类总体销售占比')

图片.png

由聚合情况不难看出,衣物,电子,家具三大类的总销售额具有一定的差距,电子类最高,较衣物类和家具类销售额高17.8% - 29.8%左右。 总体来看,衣物作为每个人都有的生活物品,销量较高,约为电子类3倍,家具类4倍。其销售利润比(利润/销售额)0.080也为最高。电子类虽销售额领先,但其销售利润比0.063低于衣物类,家具类所有指标都为最低,并且销售利润比为0.018。
对于以上现象,我们可以初步猜测:
1.在印度,各类家具可能不太常用,或者大多数的家庭对某种家具的消费能力较低
2.电子类消费额高于日常物品衣物类,说明其大概率属于增量市场,可以作为重点商品,提升其销量。但数据而言其利润较低,需要结合业务找出其原因
3.衣物类商品销量已经较高,思考其市场总体情况饱和与否。因而提高利润要降低单件商品平均成本,或者提升品质提高售价来竞争市场

  • 具体细化每个商品大类的数据
# 按衣物类商品利润降序进行排列,按商品的大类和二类进行聚合
price = priceinfo.drop(columns=['OrderID'])
priceclo = price.groupby(['Category','SubCategory']).sum().drop(['Electronics','Furniture']).sort_values('Profit',ascending=False)
priceclo

图片.png

priceclo.describe().apply(lambda x : round(x,2))

图片.png 从利润和销售额方面来说,中位值均小于平均值,标准偏差较大。对于销售额,可以看出预估处于一个偏态分布的情况。利润中位值和平均值相对接近,这也说明可能出现了一些销售额特别高,但利润却不高的商品。

此处,定义 销售利润比=利润/销售额,单件商品平均利润 = 利润/件数

#查看具体最高销售利润比,最高单件利润的商品
priceclo['SalesP'] =priceclo['Profit']/priceclo['Amount']
priceclo['QP'] =priceclo['Profit']/priceclo['Quantity']
priceclo.sort_values('SalesP',ascending=False).head() #按销售利润比降序排列前五

图片.png

priceclo.sort_values('QP',ascending=False).head() #按销售单件利润降序排列前五

图片.png

  • 简易可视化
pricesq=priceclo.drop(columns = ['Amount','Profit','Quantity'])
pricesq.head().plot.barh(title='销售利润比',y="SalesP")
pricesq.head().plot.barh(title='件商品平均利润',y="QP")

图片.png

分析认为:

  1. T-shirt、shirt、披肩stole,手帕Hankerchief类在衣物类商品可以认为是相对有较高收益商品。并且前两者在销量上(305,271)存在进步空间。

  2. 披肩与手帕更偏向于地区特点的衣饰,因而在销量上已经较高(671,754),提高利润需要考虑成本的降低,降低其边际成本。

  3. legging虽然销售利润比在前五,但其销量和利润在数值上偏低,可以根据实际业务考虑是否作为发展商品

  4. Trousers属于单件利润(21.09)和销售额(30039)很高的商品,但其销售利润比较低(0.094)。提升销量,可作为营销运营推广的商品。另外,从平均销售单价而言,该类商品远高于其他同类型,因而作为一个相对针对高消费用户的商品,能否进行品牌运营,提高品质和销售单价,从而提升其销售利润比

  • 电子大类 代码过程略,与衣物类类似。

图片.png 提取信息:销量大体一致,打印机是最高平均利润商品,电子游戏属于亏损。

  • 家具大类 代码过程略,与衣物类类似。

图片.png

提取信息:销量除了桌子外大体一致,利润集中在书柜的销售,桌子属于重大亏损,约占利润的2/3,相应商品业务属于重点关注的地方。

- 对于时间地区
#对两表进行合并,并且查重查空
df = pd.merge(priceinfo,orderinfo,on = 'OrderID')
df.isnull().sum()
df.duplicated()
#无异常后观察表
df.head(10)

图片.png

发现在订单内容里,订单ID、用户的数据有重复,进行处理

df1 = df.groupby('OrderID').sum('Amount') #将订单数据进行整合
dfinfopre = pd.merge(df,df1,on = 'OrderID',how = 'right')#整合内容添加至df
#联立方式产生重复行数据,去重
dfinfo=dfinfopre.drop(columns=['Amount_x','Profit_x','Quantity_x','CustomerName','SubCategory']).drop_duplicates('OrderID').reset_index(drop='true')
dfin = dfinfo.rename(columns={'Amount_y':'Amount','Profit_y':'Profit','Quantity_y':'Quantity'})#更名
dfin.head()

图片.png

得到表格,再次查重和查空中无异常值

dfin.info()

图片.png

  • 观察时间上的销售趋势
dfin['month'] = dfin.OrderDate.values.astype('datetime64[M]')#处理为月份,方便观察总体趋势
grouped_month = dfin.groupby('month').sum()
y1=grouped_month['Profit']
y2=grouped_month['Amount']
plt.subplots()
l1=y1.plot(label = '利润')
l2=y2.plot(label = '销售额')
plt.legend()

图片.png

1.在10月之前,是处于亏损的状态,可能属于一个发展期,用于积累用户。
2.七月份销售额出现了约50%的下降,但亏损降低,可能是降低了成本。
3.9月后,利润趋势和销售额趋势保持一致,无明显异常。

- 查看具体地区情况
dfinarea = dfin.groupby(['State','City']).sum().drop(columns = ['OrderID'])#按州和城市聚合
plt.figure(figsize=(25,15))
dfinarea.loc[dfinarea.Profit < 0,'color'] = 'g' #亏损地区标为绿色
dfinarea.loc[dfinarea.Profit > 0,'color'] = 'r'
dfinarea.Profit.plot.barh(color=dfinarea.color)
plt.title('各地门店的利润',fontdict={'weight':'normal','size':30},)
plt.tick_params(labelsize=25)

图片.png

  • 筛选出亏损地区,查看亏损地区的情况
dfinarea.drop(columns = 'color').sort_values('Profit').head(6)

图片.png

查看统计数据

round(dfinarea.describe(),2)

图片.png

最高亏损地区负利润已经接近平均值的两个标准差范围,猜测认为,该地区门店存在销售存在问题

  • 查看各地区所占总利润比例
groupbu_pro = dfinarea.groupby('State')
df_Profit = groupbu_pro.sum().query('Profit > 0').sort_values('Profit').Profit
df_Profit
labels = df_Profit.index  #注意利用索引作图
plt.figure(figsize=(20, 10))
plt.title('各地区门店利润占比')
#patches, l_texts, p_texts,为了得到饼图的返回值,p_texts饼图内部文本的,l_texts饼图外label的文本
patches,l_text,p_text = plt.pie(df_Profit, labels=labels, autopct='%.2f%%',pctdistance = 0.5, explode=[0.1 if i in ['Maharashtra', 'Madhya Pradesh', 'Uttar Pradesh','Delhi']
else 0 for i in labels],labeldistance = 1)
for t in l_text:
    t.set_size(15)
for t in p_text:
    t.set_size(12)
plt.show()

图片.png

- Tamil Nadu 地区的销售利润比地区异常具体原因探究

筛选聚合

dfChe = dfin[dfin['City'] == "Chennai"]
dfChe =pd.merge(priceinfo,dfChe,on = 'OrderID')
dfc = dfChe.drop(columns=['State','Category_y','City','Amount_y','Profit_y','Quantity_y']).rename(columns={'Category_x':'Category','Amount_x':'Amount','Profit_x':'Profit','Quantity_x':'Quantity'})
dfc.head()

图片.png

进行透视操作,按商品大类为索引,查看整体情况

dfc1 = dfc.pivot_table( index = 'Category',
              values = ['Amount','Profit','Quantity'],
              aggfunc = {'Profit' : 'sum',
                        'Amount':'sum',
                         'Quantity':'sum'
                        })
dfc1

图片.png

dfc1.drop(columns = 'Quantity').plot.barh(title = '销售利润图')

图片.png

可以看出亏损集中在家具类,有上文商品整体销售情况来看,家具类的亏损约40%来自该地区的亏损。
亏损可能来自于折扣率,或者退货率等各种原因过高,需要结合具体业务场景进行解决。

对于用户方面

用户方面,作为销售行业相对更加重要的数据,某种程度能够反映出整个业务的好坏情况,
在这里,我们简单作以下方面分析:
1.对于新用户,简单查看其增长情况。
2.对于老用户,查看消费间隔,回购率,复购率,生命周期等指标。
3.简单的RFM模型建立
(再次强调:由于该数据集并不大,在实际分析上,可能统计意义不高。但该次练习中,依然将其视为一个数量足够大的样本来看待,主要抓取其趋势上来进行一定的业务判断,锻炼数据思维)

- 查看整体情况
dfinfopre = pd.merge(df,df1,on = 'OrderID',how = 'right')
dfpeo=dfinfopre.drop(columns=['Amount_x','Profit_x','Quantity_x','City','Profit_y']).drop_duplicates('OrderID').reset_index(drop='true').rename(columns = {'Amount_y':'Amount','Quantity_y':'Quantity'})
round(dfpeo.describe().drop(columns = ['OrderID']),2)

图片.png

消费额Amount中位数高于中位数约100%,即可以认为存在少量的高消费用户;

dfpeo['OrderDate'] = pd.to_datetime(dfpeo.OrderDate,format = '%Y-%m-01')
dfpeo['month'] = dfpeo.OrderDate.values.astype('datetime64[M]') #将时间转化为月份,好进行统计
dfpeo.groupby('CustomerName').sum().plot.scatter(x = 'Quantity',y = 'Amount')#绘制用户散点图

图片.png

消费集中在2K以下,销量与销售额呈正相关。

- 观察用户消费间隔的情况
timediff = dfpeo.groupby('CustomerName').apply(lambda x:x.OrderDate - x.OrderDate.shift()) #观察多次消费的顾客,消费相差时间
plt.figure()
plt.title('消费间隔')
(timediff/np.timedelta64(1,'D')).hist(bins=10)

图片.png (Y轴为消费间隔天数为X轴对应值的人数,X轴为消费天数的间隔)

可以看到,大部分用户集中在前75天内会有多次消费的情况,可以将此划为是否为活跃用户的一个简易的标准。

timediff.describe()

图片.png

先观察用户第一次消费和最后一次消费的情况

fig = plt.figure(figsize=(20, 5))
plt.subplot(1,2,1)
dfpeo.groupby('CustomerName').min().month.value_counts().plot(title='顾客第一次消费图')
plt.ylabel('人数')
plt.subplot(1,2,2)
dfpeo.groupby('CustomerName').max().month.value_counts().plot(title='顾客最后一次消费图')
plt.ylabel('人数')

图片.png

把第一次消费时间作为新增顾客的一个指标。
从月份来看,顾客第一次消费时间大多集中在4-5月份,之后总体呈下降趋势,到了9月份出现了上升
下降原因:由内而言
1.可能为商店商品的质量有所下降
2.或者服务体验不够良好,导致了顾客的减少
由外部而言:
1.下降时间属于夏天,存在可能为顾客的出门消费意愿降低
2.外部竞争变强,存在新开的商店
3.具体的市场调整

而在9月份,由图一出现上升,可能存在有营销活动使新顾客有明显提升,并且吸引了老顾客的回流

  • 最大消费间隔
dfpeolife = dfpeo.groupby('CustomerName').OrderDate.agg(['max','min'])
dfpeolife['life'] = (dfpeolife['max'] - dfpeolife['min'])/np.timedelta64(1,'D')
dfpeolife.loc[dfpeolife['life'] != 0].sort_values('life').describe()

图片.png

平均数大于中位数,说明了大多数用户的最大消费间隔较小。

dfpeolife.drop(columns = ['max','min']).plot.hist()

图片.png

证明了上述趋势。

- 消费水平分布
dfpeo.groupby('CustomerName').sum().Amount.plot.hist(bins = 20) #消费水平
plt.title('消费水平分布')

图片.png

大部分用户消费在最低区间

统计用户数量

user_cumsum = dfpeo.groupby('CustomerName')
user_cumsum.count()

图片.png

进行用户累计消费水平查看

plt.figure
user_cumsum = dfpeo.groupby('CustomerName').sum().sort_values('Amount').apply(lambda x:x.cumsum()/x.sum())
user_cumsum.reset_index().Amount.plot() #观察用户累计消费整体水平的分布
plt.title('用户累计消费整体水平')
plt.xlabel('累计用户')
plt.ylabel('累计用户消费占总销售额比例')

图片.png

该图说明,

  1. 在总消费到0.4比例时,已经占到约 270/332=0.81 左右的用户,
  2. 剩下0.2左右的用户消费已经达到总消费比例的0.6
- RFM模型的简单建立

进行较为简单的RFM模型建立,观察用户情况
要素构成:
最近一次消费 (Recency)
消费频率 (Frequency)
消费金额 (Monetary)
备注:影响复购的核心因素是商品,因此复购不适合做跨类目比较。比如食品类目和美妆类目:食品是属于“半标品”,产品的标品化程度越高,客户背叛的难度就越小,越难形成忠实用户;但是相对美妆,食品又属于易耗品,消耗周期短,购买频率高,相对容易产生重复购买,因此跨类目复购并不具有可比性。

进行相应的透视聚合

dfnew = dfpeo.pivot_table(index = 'CustomerName',
              values = ['Amount','OrderDate','month'],
              aggfunc = {'Amount' : 'sum',   #消费金额 M
                        'OrderDate':'max',  #取最后的消费时间为所有顾客最后一次消费时间的基准
                         'month':'count'  #作为消费次数 F
                        })
dfnew['R']= -(dfnew.OrderDate - dfnew.OrderDate.max())/np.timedelta64(1,'D') #算出每位用户离基准最后消费时间的差值,并且处理单位
rfm = dfnew.rename(columns = {'month':'F','Amount' : 'M'}).drop(columns = ['OrderDate'])
rfm

图片.png

定义一个RFM简单模型的函数(此处以平均值为每个维度的基准)

def torfm(x):
    info = x.apply(lambda x:'1' if x>=0 else '0')
    label = info.R+info.F+info.M
    l= {
        '111':'重要价值',
        '011':'重要保持',
        '101':'重要挽留',
        '001':'重要发展',
        '110':'一般价值',
        '010':'一般保持',
        '100':'一般挽留',
        '000':'一般发展'
    }
    result = l[label]
    return result
rfm['label'] = rfm[['R','F','M']].apply(lambda x:x-x.mean()).apply(torfm,axis=1)
# axis=1限制以行进行循环,以平均值作为基准点

观察数量

rfm.groupby('label').count()

图片.png

可以看到,重要价值和重要保持用户占总人数的(9+55)/332 = 20%,总体上满足二八定律。
作出散点图

rfm.loc[rfm.label == '重要价值','color'] = 'g' #重要价值用户以绿色代表
rfm.loc[rfm.label == '重要保持','color'] = 'y' #重要保持用户以黄色代表
rfm.loc[~((rfm.label == '重要价值')|(rfm.label == '重要保持')),'color'] = 'r'
rfm.plot.scatter('R','M',color=rfm.color)

图片.png

可以看到存在部分重要价值用户处于较高消费间隔的区间,可以尝试对这部分用户进行唤回, 从而提高销售额度。

- 复购率的观察

最后,进行一个复购率和回购率的观。将数据处理为以月份时间为列标签的表格,显示每个用户当月的购买次数。 复购率 = 当月多次购买人数/总购买人数
再进行一次数据处理,对于当月购买次数超过一次的记为1,购买过商品记为0,没有购买过几位NaN

df_counts = dfpeo.pivot_table(index = 'CustomerName',
                               columns = 'month',
                               values = 'OrderDate',
                               aggfunc = 'count').fillna(0) #将空值变为0
df_purchase = df_counts.applymap(lambda x:1 if x>1 else np.NaN if x==0 else 0) #applymap对每个单元格执行指定函数的操作
df_purchase

图片.png

count不记录NaN

plt.figure()
(df_purchase.sum()/df_purchase.count()).plot(figsize = (10,4)) #每个月的复购率
plt.title('复购率')

图片.png

图片.png

可以看到复购率和销售额有明显的正相关关系