文章目录

  • 背景
  • 明确目标
  • 定义元问题
  • 用公式思维拆解问题
  • 得到分析思路
  • 数据处理
  • 清理数据
  • 处理缺失值
  • 处理重复值
  • 处理异常值
  • 清洗情况
  • 数据分析
  • 数据展现
  • 报告撰写
  • 提出对策



 


背景

xx公司,运营了一家小型的口罩工厂,口罩厂经营状态过往处于不稳定的状态,在今年6月底,经营开始处于亏损状态。通过本次对口罩厂历史经营数据的分析,帮助公司推断口罩厂的发展,并对口罩厂的亏损问题处理提供建议。
 


明确目标

数据分析任务需要解决怎样的问题,以及该如何解决。

口罩厂从 2020 年初开始售卖口罩,到 7 月初的时候,总监发现账目上已经开始出现亏损。

总监十分焦虑,想要在短时间内快速找到亏损的原因,以及对应的解决策略。

  • 找到亏损原因
  • 找出对应的解决策略

具体怎么办呢?

数据分析一般来说会遵循五个步骤,分别是:

  • 明确目标
  • 数据处理
  • 数据分析
  • 数据展现
  • 报告撰写

我们一步步走,你到底知不知道自己在解决什么问题。

请注意, 遇到具体问题时,你一定要问自己“我遇到的问题本质到底是什么?”

 


定义元问题

很多问题之所以难搞就是因为它特别的宏大而复杂,我们解决起来也不知道该从哪里入手。

为什么复杂问题我们总觉得解决起来就是千条万绪,找不到思路呢?这个答案很简单,也很重要。因为,我们生活当中遇到的大多问题都是所谓的复杂问题,而不是元问题。

什么是复杂问题呢?

  • 就是掺杂了多个维度和变量的问题。

那什么是元问题?

  • 就是那些最本质、最细小的待解决的问题。

复杂问题是不可直接解决的。

通过第一步【明确目标】的分析,已经知道了这次任务首先要做的就是找到口罩厂亏损背后的原因。

而亏损意味着利润为负,所以本质上我们要研究的是口罩厂的利润情况。

会影响口罩厂利润的因素有这些:

  • 订单量
  • 单价
  • 成本
  • 销售额

虽然知道了要素,但要素之间的连接关系彼此之间又存在一些关联,互相影响… …订单量要先影响销售额,然后销售额可能再进一步影响利润;但是销售额可能又会受到口罩单价等因素的影响。

这个时候你可能就会感到各种因素之间的关系非常乱,如果贸然地就对数据进行分析,很可能会有一种剪不断理还乱的困扰。

知道了这四个因素会影响口罩厂的利润,那接下来该如何动手去分析呢?

要想解决这种思路凌乱的问题,就需要借助分析框架,帮助我们系统地构建分析思路。

一般来说,我们会借助一些分析方法来搭建框架,比如逻辑树法、对比法、象限法、漏斗法等。

不过上面这些我都不用,因为有一种通用的分析框架:公式拆解法。
 


用公式思维拆解问题

其实,把一件事情公式化就是最好的拆解一件事情的方式。

有了这个公式以后,就相当于一个问题变成了元问题,现在大家就都知道了。

由于口罩厂的亏损本质上就是利润为负,那么利润就是最终想要研究的对象。

  • 利润

最直接影响利润的因素是哪些?

  • 利润 = 销售额(收入) - 成本

继续按照公式思维拆解【销售额】、【成本】。

销售额又会受到哪些因素直接影响?

  • 销售额 = 总订单量 * 单价

总订单量还可以拆分为各个地方的订单量

  • 总订单量 = 订单量(省份1) + 订单量(省份2) + 订单量(省份3)+ …

对销售额拆解后,我们应该思考成本会和哪些因素有关。

但总监说,2020 年 1 月到 6 月期间,各月成本几乎没有变化。

所以利润这个因素可以看作只受到销售额的影响,之后没有必要对成本继续拆解了。

P.S. 成本 = 人力成本 + 仓储物流成本 + 原材料成本;

业务亏损分析_缺失值


接下来想问你一个问题,这四层信息足够支持我们找到口罩厂亏损背后的原因嘛?

不可以,总监是在 7 月初的时候发现了口罩厂出现了亏损,但是口罩厂是从 1 月初开始经营的。

我们需要通过这 6 个月来的数据变化情况,推演出口罩厂亏损的原因,所以,还需要一个时间维度(订单日期)的信息。

 


得到分析思路

说明:利润=销售额-成本,现已知 1-6 月份口罩成本是变动微小,故本次分析主要从“销售额(收入)”进行分析。

总方法:通过销售额及其影响因子的分析,对过往销售额变化的规律、变化原因进行剖析。

  • 销售额 = 订单量 * 单价
  • 订单量 = 省份1 + 省份2 … + 省份n

分析框架:




  • 分析各月各省份“订单量”的变化趋势,分析变化规律及原因。
     

数据处理

在明确完目标以后,需要对数据进行清洗、整合等操作,把数据变成干净、规整的样子,这样后续才能对数据进行分析。

数据下载地址:javascript:void(0)

下载好后,我们就先从导入数据开始。

import pandas as pd

my_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

从公司数据库中导入营销数据mask_data.csv,原始数据一共包含 10,1942 行,数据的结构如下:

业务亏损分析_python_02


 


清理数据

我们仔细观察这份数据的前 业务亏损分析_python_03 行和后 业务亏损分析_python_03

业务亏损分析_数据分析_05


问题所在:

  1. 1、3、4 行完全重复
  2. 后 5 行数据的部分值是 NaN(NaN 代表缺失值,意思是这里的数据是空的)
  3. 数据中存在部分 0999999999.0 的数值(业务亏损分析_数据_06 接近十个亿,2019 年全国的口罩产能也才 业务亏损分析_python_07 亿,业务亏损分析_缺失值_08

业务亏损分析_缺失值_09

综上所述,这份数据同时存在缺失值、重复值以及异常值的问题,在进行数据处理时我们需要先将这些数据问题处理掉,这一过程也叫做【数据清洗】。

处理缺失值

在 pandas 库中,我们可以使用 isna() 方法来查找缺失值。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

print( mask_data.isna() )
# 查看 mask_data 的缺失值

isna() 返回都是布尔值:

  • 缺失数据会用 业务亏损分析_缺失值_10
  • 业务亏损分析_数据_11

运行结果:

业务亏损分析_数据分析_12


从结果中我们可以直接看到数据前后 业务亏损分析_python_03 行的缺失情况,其中 业务亏损分析_数据分析_14

这里可能是数据采集时出了一些问题,对于缺失值,最简单的方法就是将含有缺失值的行直接删除。

当然除了删除之外还有其它处理方法,比如给缺失值填充数据。

由于我们总体的数据量比较大,缺失值占总数据量的比重也比较低,将含有缺失值的行删除后并不会妨碍后续的分析。

因此我们选择较为简易的处理方法,直接将含有缺失值的数据整行删除。

在 pandas 库中,我们可以使用 dropna() 方法删除含有缺失值的数据。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

print( mask_data.isna() )
# 查找缺失值

mask_data = mask_data.dropna()
# 删除缺失值,但dropna()并没有真正删除数据,只是返回一个删除了值的对象,所以重新赋值

print( mask_data.isna() )
# 查看 mask_data 的缺失值

业务亏损分析_缺失值_15


到此,缺失值已经被处理干净了。

我们通常 info() 方法看一下这次删除了多少行数据,对总体数据影响大不大。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

print( mask_data.info() )
# 查看数据基本信息总结

mask_data.isna() 
# 查找缺失值
mask_data = mask_data.dropna()
# 删除缺失值

print( mask_data.info() )
# 查看数据基本信息总结

处理缺失值的数据:

业务亏损分析_python_16


处理缺失值的数据:

业务亏损分析_数据分析_17


这些输出是什么意思呢?

业务亏损分析_数据分析_18

可以看到,与之前的数据的总量:业务亏损分析_缺失值_19业务亏损分析_数据_20 对比,我们删除了差不多 业务亏损分析_数据_21 行数据,不到总数据的 业务亏损分析_缺失值_22% ,并不会对后续的分析产生太大影响。
 


处理重复值

重复值很好理解,就跟字面意思一样,指的是表格中重复出现的数据。在多数情况中,重复值都是完全相同的数据。

重复值处理的第一步和缺失值一样,还是要先查找重复值。

在 pandas 库中,我们可以直接使用 duplicated()(中文意思:重复的)方法来查找重复数据,返回的是布尔值,重复为 业务亏损分析_数据分析_14,不重复为 业务亏损分析_数据分析_24

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

print( mask_data[mask_data.duplicated()] )
# 查找重复行

业务亏损分析_缺失值_25


可以看到,这份数据中含有大量完全重复的行,这种重复数据并不具备分析的意义,而且会影响数据分析的结果,因此我们需要直接删除。

我们可以使用 pandas 库的 drop_duplicates() 方法删除重复出现的整行数据。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

print( mask_data[mask_data.duplicated()] )
# 查找重复行

业务亏损分析_缺失值_26


在运行结果中我们能看见 mask_data 的表头下面是空的,说明到这里已经没有重复数据了。

最后我们再运行下方代码查看一下 mask_data 的基本信息总结。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

print( mask_data.info() )
# 查看数据基本信息总结

业务亏损分析_数据_27


现在,数据的总量又变成了:业务亏损分析_数据_28,相比于之前删完缺失值的数据:业务亏损分析_缺失值_19,这次又删除了四百多行重复数据,大概占总数据的 业务亏损分析_数据分析_30%,依然不会对分析结果造成太大影响。

 


处理异常值

首先我来快速跟你介绍一个检查数据是否存在异常的方法 —— describe()

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

print( mask_data.describe() )
# 查看 mask_data 的描述性统计信息

业务亏损分析_数据分析_31


describe() 方法返回出来的统计信息分别代表数值型数据的:

  • 频数统计:业务亏损分析_数据_32
  • 平均值:业务亏损分析_数据_33
  • 标准差:业务亏损分析_缺失值_34
  • 最小值:业务亏损分析_数据分析_35
  • 第一四分位数:业务亏损分析_数据_36%
  • 中位数:业务亏损分析_python_07%
  • 第三四分位数:业务亏损分析_数据_38%
  • 最大值:业务亏损分析_数据分析_39

这里我们只需要观察最大最小值、平均数、中位数就好。

业务亏损分析_python_40


业务亏损分析_数据分析_41 代表的就是 业务亏损分析_数据分析_42 乘以 业务亏损分析_缺失值_43业务亏损分析_python_44 次方,也就是 业务亏损分析_python_45

数据中存在部分 0999999999.0 的数值,这就是异常值:

  • 图中第1处:在实际业务中,不可能出现订单量为 业务亏损分析_数据分析_46 的情况,所以订单量等于 业务亏损分析_数据分析_46
  • 图中第2处:业务亏损分析_数据_06 接近十个亿,2019 年全国的口罩产能也才 业务亏损分析_python_07 亿,业务亏损分析_缺失值_08
  • 图中第3处:单价的最大值也几乎是 10 的九次方。我们公司每盒口罩的单价最高 200 元,大于 200 的数据都属于异常值。

这些异常值会严重影响分析结果的准确性,所以我们需要在分析前抽取合理范围的数据,将异常值过滤掉。

在 pandas 库中,有一种筛选数据的方法叫做布尔索引。

比如我们想选择 mask_data 数据中,单价一列小于等于 业务亏损分析_python_51 的数据,就可以设置条件表达式 mask_data['单价'] <= 200

把它加入括号中索引:mask_data[mask_data['单价'] <= 200],就可以选取 mask_data 数据中单价这一列小于等于 业务亏损分析_python_51

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

print( mask_data[mask_data['单价'] <= 200] )
# 查看单价小于等于 200 的数据

业务亏损分析_python_53


怎么样?单价小于等于 200 的数据是不是显示了出来?这一操作就叫做布尔索引。

筛选单价小于等于 200 的数据后,再查看筛选后 mask_data 的描述性统计信息。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

print( mask_data.describe() )
# 查看 mask_data 的描述性统计信息

业务亏损分析_缺失值_54


清除异常值后,现在单价的最小值已经变成了 30,最大值变成了 200,在一个比较合理的范围内。平均值和中位数与之前比,也都变小、变合理了。

至此,这份数据中单价的异常值已经处理完毕,接下来我们再来看一下订单量一列的异常值。

仔细观察我们发现,订单量最大值变成了 业务亏损分析_python_55

说明上一步清除单价异常值的时候,订单量一列的最大值的异常也一并被处理掉了。

但订单量的最小值依然是 业务亏损分析_缺失值_56

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

mask_data = mask_data[mask_data['订单量'] > 0]
# 筛选订单量大于0的数据

print( mask_data.describe() )
# 查看 mask_data 的描述性统计信息

业务亏损分析_python_57


可以看到订单量目前的范围在 业务亏损分析_缺失值_22业务亏损分析_python_55

到这里,我们的这份数据就已经清洗完毕了。不过这并不意味着这份数据可以直接拿去分析。

在【明确目标】中分析,要先研究各个的利润变化情况,所以我们需要将日期的统计数据由年-月-日调整为月,而后作为新的月份字段保存到数据中。

业务亏损分析_缺失值_60


那么我们又该如何从日期这一列之中提取月份信息呢?

Python 中有一种专门储存时间日期的数据类型 —— datetime,我们可以直接从中提取想要的时间单位。

但 mask_data 中的日期列数据并不是 datetime 类型,而在 pandas 库中我们可以使用 to_datetime(arg, format) 方法转化为 datetime 类型。

  • 其中的 arg 参数为我们要转换的数据,我们这次要转换的数据为 mask_data 中的’日期’字段,所以对应的 arg 参数就是 mask_data[‘日期’]
  • format 参数为 datetime 类型的日期格式,比如说这份数据,它是以年-月-日的形式出现的,那么它对应的 format 就是 ‘%Y-%m-%d’
date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 转换日期数据,并设置对应的日期格式

将’日期’字段转换成 datetime 类型后,我们就可以直接从中提取月份信息。

month_data = date_data.dt.month
# 提取日期数据中的月份信息

现在我们已经得到了所有的月份信息,最后一步,我们就可以将月份数据添加到原数据,方便之后使用。

在 pandas 库中,我们可以直接用 df[‘colname’] = Series 的方式来为原数据添加新的一列。

colname 指的是要添加的新列的列名。在这个案例中,colname 就是月份。

mask_data['月份'] = month_data
# 将月份数据添加到原数据中

完整代码:

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

mask_data = mask_data[mask_data['订单量'] > 0]
# 筛选订单量大于0的数据


date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 转换日期数据,并设置对应的日期格式

month_data = date_data.dt.month
# 提取日期数据中的月份信息

mask_data['月份'] = month_data
# 将月份数据添加到原数据中

print(mask_data)

业务亏损分析_数据分析_61

 


清洗情况

清洗结果:我们对以上三种数据均进行了删除处理,其中清洗前数据一共有 业务亏损分析_数据_20,清洗后数据一共有 业务亏损分析_python_63

清洗数据占比:占比业务亏损分析_缺失值_64%

清洗前后数据量变化极小,因此可以忽略对结果的影响。
 


数据分析

在数据经过处理以后,就可以对数据进行分析,最终得到解决问题的策略。

数据分析,核心是分析思维。

分析思维不是要证明你智商很高,而是考察你面对问题展开的分析能力和分析思维。

业务亏损分析_python_65

书上的 5w2h 这样的常用分析方法,其实根本用不在数据分析工作里。

最常用的分析方法其实是:

  • 多维度拆解分析方法;
  • 对比分析方法;
  • 假设检验分析方法;
  • 相关分析方法;
  • 群组分析方法;
  • AARRR漏斗模型分析方法;
  • RFM模型分析方法;
  • … …

业务亏损分析_数据_66


本次分析的影响因素是销售额、订单量、单价、各省订单量,而且是以一个月为颗粒度,进行时间维度上的变化趋势和原因分析。

以销售额为例,如果要分别查看 1 月到 6 月以来的销售额,可以根据月份对数据mask_data 进行分组。

如果每条记录都包含了一个销售额数值,那什么数值能够代表一整个月的销售额情况,能够用来与其他月进行比较呢?

业务亏损分析_python_67


我们可以计算出销售额的总数,因为它代表着这个月销售额的总体情况,可以通过对比它的上升或下降,查看其每月变化。

那如何得到每个月销售额总数呢?

首先要根据月份对数据集进行分组(groupby),同时还需要对销售额列进行一次聚合计算,计算出销售额的总和(sum)。

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据

mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

mask_data = mask_data[mask_data['订单量'] > 0]
# 筛选订单量大于0的数据


date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 转换日期数据,并设置对应的日期格式

month_data = date_data.dt.month
# 提取日期数据中的月份信息

mask_data['月份'] = month_data
# 将月份数据添加到原数据中


sales_income = mask_data.groupby('月份')['销售额'].sum()
# 获取每月销售额总数

print( sales_income )

业务亏损分析_数据分析_68


根据数值已经可以大致看出,1-3 月总销售额增加, 3-6 月则在下降。基于这样的每月销售额统计数据,才能方便地进入后续趋势分析步骤中。

业务亏损分析_数据分析_69

同理,依次按照此法,求出订单量、单价、各省订单量的变化规律。

order_number = mask_data.groupby('月份')['订单量'].sum()
# 获取各月总订单量

print( order_number )

业务亏损分析_缺失值_70


不错,它同样能够体现出这个月所有订单量的变化情况,1-3 月数量上升,3-6 月数量下降。

month_price = mask_data.groupby('月份')['单价'].mean()
# 获取每月平均单价

print(month_price)

业务亏损分析_数据_71


可以看到,终端打印出了每个月平均单价的不同数值,大致可以看出 1-3 月平均单价上升,3-6 月平均单价在下降。

month_order1 = mask_data.groupby(['省', '月份'])['订单量'].sum()
# 获取各月各省总订单量

print(month_order1)
省   月份
其他 1         452.0
    2       10668.0
    3       16871.0
    4        2713.0
    5        1250.0
    6         113.0
    
广东 1        2628.0
    2       77427.0
    3      125618.0
    4       46708.0
    5       22364.0
    6        4038.0
    
江苏 1         263.0
    2        2530.0
    3        4004.0
    4         106.0
    5         118.0
    6         116.0
    
河南 1         434.0
    2       10058.0
    3       18293.0
    4        2917.0
    5        1239.0
    6         107.0
    
湖北 1        9055.0
    2      259511.0
    3     1731060.0
    4      476558.0
    5      126342.0
    6       17182.0
    
湖南 1        1020.0
    2       29125.0
    3       65634.0
    4       17274.0
    5        6875.0
    6         666.0

完整代码:

import pandas as pd

mask_data = pd.read_csv('./mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据



'''数据处理'''
mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

mask_data = mask_data[mask_data['订单量'] > 0]
# 筛选订单量大于0的数据


date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 转换日期数据,并设置对应的日期格式

month_data = date_data.dt.month
# 提取日期数据中的月份信息

mask_data['月份'] = month_data
# 将月份数据添加到原数据中



'''数据分析'''
sales_income = mask_data.groupby('月份')['销售额'].sum()
# 获取每月销售额总数

order_number = mask_data.groupby('月份')['订单量'].sum()
# 获取各月总订单量

month_price = mask_data.groupby('月份')['单价'].mean()
# 获取每月平均单价

month_order1 = mask_data.groupby(['省', '月份'])['订单量'].sum()
# 获取各月各省总订单量

根据数据结果,我们其实可以大致看出数据变化的趋势。

但是其效果很不直观,需要人工比较数据、分析变化。所以下面正式进入变化趋势的分析,通过数据可视化来辅助我们的分析。
 


数据展现

在分析完数据以后,需要通过表格或者图表的形式,直观、有效地传递出数据分析师所要表达的观点。

import pandas as pd

mask_data = pd.read_csv('/Users/debroon/Desktop/mask_data.csv', encoding = 'utf-8')
# 导入 csv 数据



'''数据处理'''
mask_data = mask_data.dropna()
# 删除缺失值

mask_data = mask_data.drop_duplicates()
# 直接删除所有重复值

mask_data = mask_data[mask_data['单价'] <= 200]
# 筛选单价小于等于 200 的数据

mask_data = mask_data[mask_data['订单量'] > 0]
# 筛选订单量大于0的数据


date_data = pd.to_datetime(mask_data['日期'], format = '%Y-%m-%d')
# 转换日期数据,并设置对应的日期格式

month_data = date_data.dt.month
# 提取日期数据中的月份信息

mask_data['月份'] = month_data
# 将月份数据添加到原数据中



'''数据分析'''
sales_income = mask_data.groupby('月份')['销售额'].sum()
# 获取每月销售额总数

order_number = mask_data.groupby('月份')['订单量'].sum()
# 获取各月总订单量

month_price = mask_data.groupby('月份')['单价'].mean()
# 获取每月平均单价

month_order1 = mask_data.groupby(['省', '月份'])['订单量'].sum()
# 获取各月各省总订单量



'''数据展示'''
from matplotlib import pyplot as plt
# 导入matplotlib库的pyplot模块

plt.rcParams['font.family'] = ['Source Han Sans CN']
# 设置中文字体

sales_income.plot(kind = 'line', figsize = (7, 7), title = '各月总销售额趋势图')
# 画出各月总销售额的折线图

order_number.plot(kind = 'line', figsize = (7, 7), title = '各月总订单量趋势图')
# 画出各月总订单量的折线图

month_price.plot(kind = 'line', figsize = (7, 7), title = '各月平均单价趋势图')
# 画出各月平均单价的折线图

month_order1_df.plot(title='各月各省总订单量趋势图')
# 根据month_order1_df绘制多条折线图,标题为'各月各省总订单量趋势图'

业务亏损分析_数据分析_72


可以发现无论各月的总销售额、总订单量、平均单价,以及各省在各月的总订单量,变化趋势有很大的相似性:

  • 在 1 月到 3 月的折线是随着时间增长而增加,整体呈增加趋势,其中 2 月到 3 月增长快速;3 月之后整体呈下降趋势,3 月到 4 月出现了“急跌”。
  • 目前的折线中未出现多个类似形状的起伏波动,未体现出任何规律性。
  • 而且图中只有一处波动起伏,在这个起伏的最高点,对应着 3 月的数据,此处为最大值。

这种完全一致的变化趋势说明,此处总销售额的下降是由于订单量、单价双双下滑带来的,而非某一因素单方面的影响。

最后,我们来分析口罩厂各项影响因素的统计值都在“走下坡路”的原因,给总监一个“交代”。
 


报告撰写

在对数据分析完并且展现完毕后,需要对整个过程进行一个梳理与总结。

这里趋势变化非常明显,最大值(转折点)为 3 月,因此我们重点关注这个值,以其特殊性作为突破口。

根据订单量、单价的变化,我们可以知道,3 月之前,工厂的口罩需求量和价格都增长猛烈,3 月之后却又出现大幅下降。

按理来说,口罩属于医用防护品和日用生活品,不受季节、时间的影响。一般其需求源头比较稳定,物价也很稳定。

但是对比 1-3 月份总订单量的变化,其纵坐标的值是 0 到 2,但是单位是百万。因此是从 1 月份的万余盒,增长到 2 月份的 30 余万盒,突然涨到 3 月份 196 万余盒,变化迅猛。

另外通过折线图也能够发现趋势变化的强烈程度,比如 3 月到 4 月的订单量,就是一个“急跌”,订单量一下子缩水了 100 多万!

因此认为,要想在两三个月内迅速改变口罩的供需关系,应该是属于社会性问题的外部冲击。

与口罩使用的历史一致,几乎每次大的普及推广都是受传染性疾病或空气污染等外部影响。

因此这里排除业务内部原因,比如偶发性降价促销活动带来的客流量上涨与衰退,以及工厂竞争能力降低带来的市场占有率从高到低等等内部问题。

因为这些情况都属于常规运营问题,造成的影响远不会如此剧烈。

实际上,从 2020 年 1 月底开始,国内就蔓延出了新型冠状病毒疫情的传染性疾病,到了 3 月份,疫情感染人数才逐渐得到控制。

可以看到疫情出现与改善的时间,与以上所分析四个描述性统计数值的变化时间,整体上是非常吻合的。

业务亏损分析_python_73


疫情最严重的时期为 2 月份,2 月初电视中还经常出现药店口罩断供的新闻,3 月中下旬开始,全国陆续开始复工,人群流动性增加的情况下,口罩的需求也是达到了最高点。

而且我们也可以看到,各省的订单量变化中,以湖北省的变化最为强烈,在 2020 年的新冠疫情中,湖北省是重灾区,全省 5927 万人口,是非常大的需求缺口。

据说我们的工厂整个 2-3 月都在加班加点进行生产,但是口罩的价格与销量都还是只增不降,说明需求根本无法及时得到满足。

而到 3 月之后,疫情好转,全国各地陆续放松,尤其是不再要求必须在公众场合佩戴口罩,人们对于口罩的需求迅速回落,一直到 6 月销量和价格都仍在下落的过程中。

可以说,2020 年 1 月到 6 月,由于新冠疫情的影响,口罩的供需关系发生了很大的波动变化,导致工厂口罩的订单量、价格以及整体销售额也自然随之发生变化。

 


提出对策

说到问题根源,其实还是一个供需关系,如果口罩厂商在供需矛盾中不占优势,想要继续维持原来高价格高销量的情况几乎是不可能。

因为人们的购买标准,将从“能用”升级为性价比高、质量好、服务好等更复杂的行业竞争力问题上。

因此当疫情减少,刚性需求减少后,口罩行业的市场竞争将会越发激烈。

因疫情产生的刚性需求将回落为日常性需求,因此未来一段时间疫情稳定,订单量和单价不会同时上升。

如果这种状态下,6 月份口罩厂是处于亏损,那么未来这段时间,口罩厂将继续处于亏损状态。

基于这样的分析,我会跟总监提出两种不同的建议:

  • 1)公司可以对口罩厂进行改革,不过对于工厂的优势,改革的重点及规划,还有待进一步对产品、营销体系等情况的分析。
  • 2)如果公司不对口罩厂进行新的投入和改革,口罩厂将会持续亏损,建议尽早对口罩厂做转让处理。
    当然,还有第三种方案,那就是基于国际局势考虑,目前南半球及欧美国家,由于气候及政策等原因,新冠疫情的危机是越演愈烈。
    预计对于疫情为加重趋势的国家,口罩的需求量仍会比较高涨。
  • 3)如果可以及时打开国际销售渠道,并维持相对较低的成本,工厂将仍能保持供需矛盾中的优势地位,维持高单价高销量的营收情况。