乐趣区

关于python:关于超市零售数据的Python数据分析实战

一、明确需要和目标

  • 对一家寰球超市的四年(2011-2014)销售数据进行“人、货、场”剖析,并给出晋升销量的针对性倡议。
  • 场:整体经营状况剖析,包含销售额、销量、利润、客单价、市场布局等具体情况剖析。
  • 货:商品构造、劣势 / 爆款商品、劣势 / 待优化商品等状况剖析。
  • 人:客户数量、新老客户、RFM 模型、复购率、回购率等用户行为剖析。

二、数据介绍

  • 数据来源于 Kaggle 平台,这是一份寰球大型超市五年的批发数据集,数据详尽。
  • 数据集为 superstore_dataset2011-2015.csv,共有 51290 条数据,每条数据共 24 个特色。

三、数据预处理

3.1 加载相干库和数据集

# 加载数据分析须要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings

plt.rcParams['font.sans-serif'] = ['SimHei']  # 替换 sans-serif 字体
plt.rcParams['axes.unicode_minus'] = False  # 解决坐标轴正数的负号显示问题
warnings.filterwarnings('ignore')  # 克制第三方正告

# 应用 "unicode-escape" 编码方式加载数据集
df = pd.read_csv('./superstore_dataset2011-2015.csv', encoding='unicode-escape')
df.head()


首先看一下数据集的大小:

df.shape
> (51290, 24)

再看一下数据的散布详情:

df.describe()

3.2 列名重命名
从下面展现的数据能够发现其列名不合乎 Python 的命名标准,因而采纳下划线命名法对列名进行重命名:

df.rename(columns=lambda x: x.replace('','_').replace('-','_'), inplace=True)
df.columns

3.3 数据类型解决
首先查看各列的数据类型:

df.dtypes


能够看到大部分列的数据类型是 object,其中销量 (Quantity)、销售额(Sales)、利润(Profit) 等为数值型,不须要进行数据类型转换。另外,订单日期 (Order_Date) 的类型应为 datetime,因而须要对其进行转换。

df['Order_Date'] = pd.to_datetime(df['Order_Date'])
df['Order_Date'].sample(5)


为了便于剖析每年和每月的销售状况,减少“Year”列和“Month”列:

df['Year'] = df['Order_Date'].dt.year
df['Month'] = df['Order_Date'].values.astype('datetime64[M]')
df[['Year', 'Month']].sample(5)

3.4 缺失值解决
先查看一下数据的缺失状况:

df.isnull().sum(axis=0)


发现“Postal_Code”列的缺失值十分多,该列示意的是邮编信息,显然对咱们的剖析没有太大作用,因而可间接删除该列。

df.drop(['Postal_Code'], axis=1, inplace=True)

3.5 异样值解决

df.describe()


从下面展现的后果能够确定没有显著的异样值,因而不须要进行解决。

3.6 反复值解决
先查看一下是否有反复值:

df.duplicated().sum()
> 0

阐明没有反复值,因而不须要进行解决。

四、数据分析

4.1 整体销售状况剖析
首先结构整体销售状况的子数据集:

# 蕴含:订单日期、年份、月份、销售额、销量、利润
sales_data = df[['Order_Date', 'Year', 'Month', 'Sales', 'Quantity', 'Profit']]
sales_data.sample(5)


而后依照年份、月份对销售子数据集进行分组求和:

sales_year = sales_data.groupby(['Year', 'Month']).sum()
sales_year


接着对以上进行分组求和后的数据进行拆分,将每一年的数据作为独立的表:

year_2011 = sales_year.loc[2011:2011,:].reset_index()
year_2012 = sales_year.loc[2012:2012,:].reset_index()
year_2013 = sales_year.loc[2013:2013,:].reset_index()
year_2014 = sales_year.loc[2014:2014,:].reset_index()
year_2014

4.1.1 销售额剖析

# 构建销售额表
sales = pd.concat([year_2011['Sales'],year_2012['Sales'],
                  year_2013['Sales'],year_2014['Sales']], axis=1)

# 对行名和列名进行重命名
sales.columns = ['Sales_2011', 'Sales_2012', 'Sales_2013', 'Sales_2014']
sales.index = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']

# 增加色彩:色彩越深示意销售额越高
sales.style.background_gradient()

  • 从上图能够看出基本上每一年下半年的销售额都比上半年要高,而且随着年份的增大,销售额也在逐年的减少,能够阐明其销售业绩增长较快,倒退还是比拟好的。

肉眼看到的是每一年的销售额都比前一年要好,那么当初来理论计算一下每年的销售总额和具体的增长率:

# 计算每年销售额
sales_sum = sales.sum()

# 计算销售额增长率
rise_12 = (sales_sum[1]-sales_sum[0]) / sales_sum[0]
rise_23 = (sales_sum[2]-sales_sum[1]) / sales_sum[1]
rise_34 = (sales_sum[3]-sales_sum[2]) / sales_sum[2]
rise_rate = [0, rise_12, rise_23, rise_34]

sales_sum = pd.DataFrame({'sales_sum': sales_sum})
sales_sum['rise_rate'] = rise_rate
sales_sum

# 展现每年销售额
sales_sum['sales_sum'].plot(kind='bar', alpha=0.5, figsize=(8,6))
plt.grid()
plt.title('2011-2014 每年销售额')
plt.show()

# 展现增长率
sales_sum['rise_rate'].plot(color='red', figsize=(8,6))
plt.title('2011-2014 每年销售额增长率变化趋势')
plt.show()

  • 从下面能够看出,后两年的销售额增长率达到 26%,2014 年的销售额将近是 2011 年的两倍,发展势头良好,经营在逐渐稳固。联合年度销售额及其增长率进行战略规划,能够布局或制订下一年度总销售额业绩指标。

理解超市整体销售额后,再对每年每月的销售额进行剖析。理解不同月份的销售状况,找出是否有淡旺季之分,找出重点销售月份,以便制订经营策略与业绩月度及季度指标拆分。
首先看一下每年每月销售额的面积沉积图:

sales.plot.area(stacked=False, figsize=(8,6))
plt.title('每年每月销售额变动')
plt.show()

  • 从沉积图能够大抵看出该超市的销售季节性显著,总体状况是上半年为旺季,下半年为淡季。其中上半年中 6 月份的销售额比拟高,下半年中 7 月份的销售额偏低。对此可采取的措施有:

    1. 对于淡季的月份,经营推广等策略要持续维持,还能够加大投入,进步整体销售额。
    2. 对于旺季的月份,能够联合产品特点进行新产品扩大,举办一些促销流动等来吸引用户。

4.1.2 销量剖析

# 构建销量表
quantity = pd.concat([year_2011['Quantity'],year_2012['Quantity'],
                  year_2013['Quantity'],year_2014['Quantity']], axis=1)

# 对行名和列名进行重命名
quantity.columns = ['Quantity_2011', 'Quantity_2012', 'Quantity_2013', 'Quantity_2014']
quantity.index = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']

# 增加色彩:色彩越深示意销量越高
quantity.style.background_gradient()


计算一下每年销量和销量增长率:

# 计算每年销量
quantity_sum = quantity.sum()

# 计算销量增长率
rise_12 = (quantity_sum[1]-quantity_sum[0]) / quantity_sum[0]
rise_23 = (quantity_sum[2]-quantity_sum[1]) / quantity_sum[1]
rise_34 = (quantity_sum[3]-quantity_sum[2]) / quantity_sum[2]
rise_rate = [0, rise_12, rise_23, rise_34]

quantity_sum = pd.DataFrame({'quantity_sum': quantity_sum})
quantity_sum['rise_rate'] = rise_rate
quantity_sum

# 展现每年销量
quantity_sum['quantity_sum'].plot(kind='bar',alpha=0.5, figsize=(8,6))
plt.grid()
plt.title('2011-2014 每年销量')
plt.show()

# 展现销量增长率
quantity_sum['rise_rate'].plot(color='red', figsize=(8,6))
plt.title('2011-2014 每年销量增长率变化趋势')
plt.show()

quantity.plot.area(stacked=False, figsize=(8,6))
plt.title('每年每月销量变动')
plt.show()

  • 能够显著看出 2011-2014 年销量的变化趋势与销售额是一样的,下半年销量整体高于上半年,同时销量同比上一年均在进步。

4.1.3 利润剖析

# 构建利润表
profit = pd.concat([year_2011['Profit'],year_2012['Profit'],
                 year_2013['Profit'],year_2014['Profit']], axis=1)

# 对行名和列名进行重命名
profit.columns = ['Profit_2011','Profit_2012','Profit_2013','Profit_2014']
profit.index = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']

# 增加色彩:色彩越深示意利润越高
profit.style.background_gradient()

# 计算每年总利润
profit_sum = profit.sum()
profit_sum=pd.DataFrame({'profit_sum':profit_sum})
profit_sum['sales_sum'] = sales.sum().values

# 计算利润率
profit_sum['profit_rate'] = profit_sum['profit_sum'] / profit_sum['sales_sum']

profit_sum

# 展现每年总利润
profit_sum['profit_sum'].plot(kind='bar',alpha=0.5, figsize=(8,6))
plt.grid()
plt.title('2011-2014 每年总利润')
plt.show()

# 展现利润率
profit_sum['profit_rate'].plot(color='green', figsize=(8,6))
plt.ylim(0.08,0.15)
plt.title('2011-2014 每年利润率变化趋势')
plt.show()

  • 从下面的后果能够看出,每年总利润变化趋势和销售额一样,都是在逐年减少的,阐明企业经营还是比拟妥善的。然而利润率总体安稳,稳固在 11%-12% 之间,总体也还是不错的。

4.1.4 客单价剖析
客单价是指超市每一个顾客均匀购买商品的金额,即均匀交易金额。从某种程度上反映了企业的生产群体的许多特点以及企业的销售类目标盈利状态是否衰弱。其中,须要留神的有:(1)计算总生产次数要明确同一天内同一个客户产生的所有生产算作一次生产;(2)客单价等于总生产金额除以总生产次数。

# 2011-2014 年客单价
total_num_list = []
unit_price_list = []
for i in range(2011,2015):
    data = df[df['Year']==i]
    price = data[['Order_Date','Customer_ID','Sales']]
    
    # 计算每年总生产次数
    price_dr = price.drop_duplicates(subset=['Order_Date','Customer_ID'])
    
    # 总生产次数 list
    total_num_list.append(price_dr.shape[0])
    # 客单价 list
    unit_price_list.append(price['Sales'].sum()/price_dr.shape[0])
    
unit_price = pd.DataFrame({'total_num': total_num_list, 'unit_price': unit_price_list})
unit_price.index = [str(i) for i in range(2011,2015)]
unit_price

# 展现每年生产次数
# 展现每年生产次数
unit_price['total_num'].plot(kind='bar',alpha=0.5, figsize=(8,6))
plt.grid()
plt.title('2011-2014 每年生产次数')
plt.show()

# 展现每年客单价
unit_price['unit_price'].plot(color='green', figsize=(8,6))
plt.ylim(450,550)
plt.grid()
plt.title('2011-2014 每年客单价变化趋势')
plt.show()

  • 从以上图表来看,每年生产次数呈一直回升趋势,然而客单价总体浮动范畴不是很大,根本稳固在 500 左右。

4.1.5 市场布局剖析
因为这是一家寰球超市,在不同地区都会有市场,因而来看一下不同地区之间的销售状况:

Market_Year_Sales = df.groupby(['Market','Year']).agg({'Sales':'sum'}).reset_index().rename(columns={'Sales':'Sales_amounts'})
Market_Year_Sales.head(10)

plt.figure(figsize=(8,6))
sns.barplot(data=Market_Year_Sales, x='Market', y='Sales_amounts', hue='Year')
plt.title('2011-2014 每年各地区销售状况')
plt.show()


再来看一下这四年各地区销售额占总销售额的百分比:

Market_Sales = df.groupby(['Market']).agg({'Sales':'sum'})
Market_Sales['percent'] = Market_Sales['Sales'] / df['Sales'].sum()
Market_Sales.sort_values('percent',inplace=True,ascending=False)
Market_Sales.style.background_gradient()

Market_Sales['percent'].plot.pie(autopct='%.2f%%', figsize=(8,8))
plt.title('四年各地区销售额占总销售额的百分比')
plt.show()

  • 从以上图表能够看出,每个地区每年销售额总体处于回升趋势,其中 APAC(亚太地区)、EU(欧盟)、US(美国)、LATAM(拉丁美洲)的销售额超过了总销售额的 85%,总体也与地区的经济倒退相匹配。然而 Canada(加拿大)的销售额微不足道,能够联合公司整体策略布局进行取舍。

4.2 商品状况剖析
首先统计销量前 10 的商品:

productID_count = df.groupby('Product_ID').count()['Customer_ID'].sort_values(ascending=False)
productID_count.head(10)


统计销售额前 10 的商品:

productID_amount = df.groupby('Product_ID').sum()['Sales'].sort_values(ascending=False)
productID_amount.head(10)


统计利润前 10 的商品:

productID_profit= df.groupby('Product_ID').sum()['Profit'].sort_values(ascending=False)
productID_profit.head(10)

  • 从以上后果能够看出,销量靠前的大部分是办公用品;销售额靠前的大部分是电子产品以及家具这些单价较高的商品;利润前 10 的商品中有一半是电子产品,能够重点思考晋升这部分产品的销量,来减少整体的利润。

接着看一下具体商品种类的销售状况:

# 依照品种和子品种进行分组,统计销售额和利润
df_Category_Sub_Category = df.groupby(['Category','Sub_Category']).sum()[['Sales','Profit']]
# 依照销售额倒序排序
df_Category_Sub_Category.sort_values('Sales', ascending=False, inplace=True)
# 计算每个品种商品的销售额累计占比
df_Category_Sub_Category['cum_percent'] = df_Category_Sub_Category['Sales'].cumsum() / df_Category_Sub_Category['Sales'].sum()
df_Category_Sub_Category.reset_index()


  • 从以上图表能够清晰的看到不同品种商品的销售额奉献比照,有将近一半品种的商品的总销售额占比达到 84%,这部分商品应该是自家劣势主营产品,后续经营过程中应持续放弃,可联合整体策略倒退适当加大投入,逐步造成本人的品牌。
  • 同时也发现,开端销售额累计占比 16% 的产品中大部分是办公用品中的小物件。能够思考与其余主营产品联合,连带销售来晋升销量,或者思考对这些商品进行优化。
  • 最值得关注的是,Tables(桌子)的利润是正数,表明这个产品目前处于亏损状态,其起因可能是促销让利太多。通过查看原数据,发现 Tabels 大部分都在打折,打折的销量高达 76%。如果是在清库存,这个成果还是不错的,但如果不是,阐明这个产品在市场推广上遇到了瓶颈,或者是遇到强竞争对手,须要结合实际业务进行剖析,适当改善经营策略。

4.3 用户状况剖析
4.3.1 不同类型的客户占比
首先统计四年所有不同类型的客户占比:

df['Segment'].value_counts().plot(kind='pie', autopct='%.2f%%', figsize=(8,8))
plt.title('四年所有不同类型客户占比')
plt.show()

  • 从上面饼图能够看出,这四年来,一般消费者的客户占比最多,达到 51.7%。

接着看一下每一年不同类别的客户数状况:

Segment_Year = df.groupby(['Segment','Year']).agg({'Customer_ID':'count'}).reset_index()
plt.figure(figsize=(8,6))
sns.barplot(x='Segment', y='Customer_ID', hue='Year', data=Segment_Year)
plt.title('2011-2014 年不同类别的客户数量')
plt.show()

  • 从柱形图能够看出每一类客户每年均放弃增长趋势,阐明客户构造十分不错。

再来看一下每一年不同类别客户奉献的销售额:

Segment_sales = df.groupby(['Segment', 'Year']).agg({'Sales':'sum'}).reset_index()
plt.figure(figsize=(8,6))
sns.barplot(x='Segment', y='Sales', hue='Year', data=Segment_sales)
plt.title('2011-2014 年不同类型客户奉献的销售额')
plt.show()

  • 各个类型的客户每年奉献的销售额都在稳步晋升,一般消费者奉献的销售额最多,当然这和客户占比是有肯定关系的。

4.3.2 客户下单行为剖析
截取 Customer_ID,Order_Date,Quantity,Sales,Month 为新的子集,并对 Order_Date 进行排序,不便后续剖析:

grouped_Customer = df[['Customer_ID','Order_Date', 
                       'Quantity', 'Sales', 'Month']].sort_values(['Order_Date']).groupby('Customer_ID')

首先看一下用户的第一次购买日期散布和最初一次购买日期散布:

grouped_Customer.min()['Order_Date'].value_counts().plot(figsize=(8,6))
plt.title('用户第一次购买日期散布')
plt.show()

grouped_Customer.max().Order_Date.value_counts().plot(figsize=(8,6))
plt.title('用户第最初一次购买日期散布')
plt.show()

  • 从下面能够看出,在 13 年初当前新用户增长的趋势迟缓,商家能够通过广告等推广策略排汇更多的新用户。通过观察最近一次购买日期散布,能够发现用户根本没有散失,也验证了每年销售额的增长趋势。
  • 总体来说新客户数量是在逐年递加的,阐明该企业对老客户的维系不错,但新客获取率较低。如果可能在新客户获取上失去冲破,将会给企业带来很大的增长空间。

接着来看一下只购买过一次的客户数量:

# 统计每一个客户第一次购买和最初一次购买日期
Customer_life = grouped_Customer['Order_Date'].agg(['min','max'])
# 查看只有一次购买记录的顾客数量,第一次和最初一次购买日期雷同的话则阐明购买只有一次
(Customer_life['min']==Customer_life['max']).value_counts()

  • 从后果来看,只购买一次的用户只有 10 个,大部分用户都会购买屡次,阐明复购率很高,也验证了下面对于该企业对老客户的维系不错的论断。

4.3.3 RFM 模型剖析
RFM 的含意:

  • R(Recency):客户最近一次交易工夫的距离。R 值越大,示意客户交易产生的日期越久,反之则示意客户交易产生的日期越近。
  • F(Frequency):客户在最近一段时间内交易的次数。F 值越大,示意客户交易越频繁,反之则示意客户交易不够沉闷。
  • M(Monetary):客户在最近一段时间内交易的金额。M 值越大,示意客户价值越高,反之则示意客户价值越低。

RFM 模型剖析就是依据客户沉闷水平和交易金额的奉献,进行客户价值细分的一种办法。

首先构建 RFM 表:

rfm = df.pivot_table(index='Customer_ID', 
                     values = ['Order_ID','Sales','Order_Date'], 
                     aggfunc={'Order_ID':'count','Sales':'sum','Order_Date':'max'})

# 以所有用户中最大的交易日期为规范,求每笔交易的工夫距离即为 R
rfm['R'] = (rfm.Order_Date.max()-rfm.Order_Date) / np.timedelta64(1,'D')

# 每个客户的订单数即为 F,总销售额即为 M
rfm.rename(columns={'Order_ID':'F','Sales':'M'},inplace = True)
rfm = rfm[['R','F','M']]
rfm.head()


当初对客户价值进行标注,将客户分为 8 个等级(2x2x2):

# 基于平均值作比拟,超过均值则为 1,反之为 0
def rfm_func(x):
    level = x.apply(lambda x: '1' if x>0 else '0')
    level = level.R + level.F + level.M
    d = {"111": "重要价值客户",
        "011": "重要放弃客户",
        "101": "重要挽留客户",
        "001": "重要倒退客户",
        "110": "个别价值客户",
        "010": "个别放弃客户",
        "100": "个别挽留客户",
        "000": "个别倒退客户"}
    return d[level]

rfm['Label'] = rfm.apply(lambda x: x-x.mean()).apply(rfm_func, axis=1)
rfm.head()


接着对重要价值客户和非重要价值客户进行可视化展现:

rfm.loc[rfm.Label=='重要价值客户','Color']='g'
rfm.loc[rfm.Label!='重要价值客户','Color']='r'
rfm.plot.scatter('F', 'R',c=rfm.Color, figsize=(8,6))
plt.title('重要价值与非重要价值客户的散布')
plt.show()

  • 通过 RFM 辨认不同的客户群体,可能掂量客户价值和客户利润创收能力,能够制订个性化的沟通和营销服务,为更多的营销决策提供无力反对,为企业发明更大的利益。

4.3.4 新用户、沉闷用户、不沉闷用户和回归用户剖析
设置 Customer_ID 为索引,Month 为列名,统计每个用户每个月的购买次数:

pivoted_counts = df.pivot_table(index='Customer_ID', 
                               columns='Month', 
                               values='Order_Date', 
                               aggfunc='count').fillna(0)

# 将大于 1 次的全副设为 1,反之设为 0
df_purchase = pivoted_counts.applymap(lambda x: 1 if x>0 else 0)
df_purchase.head()


定义状态函数并进行状态标记:

def active_status(data):
    status = []
    for i in range(48):
        # 本月未生产
        if data[i] == 0:
            if len(status)>0:
                if status[i-1] == 'unreg':
                    status.append('unreg')  # 未注册客户
                else:
                    status.append('unactive')  # 不沉闷用户
            else:
                status.append('unreg')
        # 若生产了
        else:
            if len(status) == 0:
                status.append('new')  # 新用户
            else:
                if status[i-1] == 'unactive':
                    status.append('return')  # 回归用户
                elif status[i-1] == 'unreg':
                    status.append('new')
                else:
                    status.append('active')
                    
    return pd.Series(status)

purchase_status = df_purchase.apply(active_status, axis=1)
purchase_status.head()


用 NaN 代替“unreg”,并统计每月各状态的客户数量:

purchase_status_ct = purchase_status.replace('unreg',np.NaN).apply(lambda x: pd.value_counts(x))
# 用 0 填充 NaN
purchase_status_ct.fillna(0).T.plot.area(figsize=(8,6))
plt.title('每月各状态的用户数量')
plt.show()

  • 从以上后果能够发现沉闷客户、新客户和回归客户,每年呈肯定的法则起伏,这可能和年初年末大促无关,须要更多数据进行佐证。同时能够发现新客数量每年均在缩小,阐明该商家新客获取率较低,如果能在新客户获取上获得冲破,会给商家带来很大的增长空间。

4.3.5 复购率和回购率剖析

复购率计算指标:用户在本月购买过一次以上算作复购

purchase_r = pivoted_counts.applymap(lambda x: 1 if x>1 else np.NaN if x==0 else 0)
(purchase_r.sum()/purchase_r.count()).plot(figsize=(10,6))
plt.title('每月复购率变化趋势')
plt.show()


回购率计算指标:在本月购买过,且在下月也购买时计入回购

def purchase_back(data):
    status = []
    for i in range(47):
        if data[i] == 1:
            if data[i+1] == 1:
                status.append(1)
            if data[i+1] == 0:
                status.append(0)
        else:
            status.append(np.NaN)
    status.append(np.NaN)
    return status

purchase_b = df_purchase.apply(purchase_back, axis=1, result_type='expand')
(purchase_b.sum()/purchase_b.count()).plot(figsize=(10,6))
plt.title('每月回购率变化趋势')
plt.show()

  • 从上能够发现复购率根本大于 52.5%,且总体呈上升趋势,阐明客户忠诚度高,回购率在年中年末呈峰状态,可能与商家折扣流动或节日无关。

五、总结

本我的项目通过“场、货、人”三个不同的角度去剖析一家寰球超市的销售、商品、用户状况,并依据剖析后果给出一些有利于拓展用户、晋升销量的方法。当然,这份数据集蕴含信息很多,还能够进行其它一些方面的剖析,来给出更好的倡议和业务决策反对。

退出移动版