一、学习目标
- 读取文件,预处理数据
- 将2张商品销量数据合并成一张表
- 将合并后的销量数据按每个月每个商品进行聚合 联合浏览和销量数据,计算转化率
- 可视化,得出结论
二、数据分析步骤
STEP1.整理问题
整理问题,也是明确需求和解决方案的开始。从便利店来看,则需要找出影响销量的因素,然后给出合适的优化方案。
这里先预测影响销量的三个因素:1.销售额变化趋势;2.商品结构;3.用户群体
2.收集数据
从数据库里获取了2018年1月1日到2021年12月30日,这四年内「便利店的销售数据」,包含下单日期、商品类别、销售额等信息。
pycharm参考代码:
输入代码(本次练习使用 jypter notebook 编程):
# jupyter notebook
import pandas as pd
data = pd.read_csv("store.csv") # 相对路径
data
显示如下:
该数据集是一个CSV文件。 图中展示了该数据集的一小部分,每一行就是一条订单数据。
3.清洗数据
获取并读取数据集后,在开展进一步分析前,我们还需要对数据的质量进行检查和处理。也就是先识别并清洗数据集中的脏数据,然后再看是否需要对数据进行处理,比如转换数据类型等操作。
3.1 数据清洗
识别并清洗数据的流程是:
1.识别并处理缺失值
在pandas中,检查缺失值可以通过 info() 函数。 我们只用关注该函数返回的数据总行数和各列非空数据的数量即可。
观察输出: 这份数据集的总行数为9935; 订单量和销售额这两列的非空数据都小于数据总行数; 说明存在少量缺失值。 由于缺失值数量较少,我们选择直接删除掉包含缺失值的行数据。
参考代码:
输入代码:
data.info()
显示如下:
2.识别并处理异常值
接下来进行数据清洗的第二步:识别并处理异常值。
有一个快速识别数据集中是否存在异常值的方法,就是使用描述函数 —— describe()。 输出结果中,很多数字后面都有 e+01、e+09、... 之类的后缀,这是一种科学计数法,代表10的n次方。
参考代码:
输入代码:
# 1.识别并处理缺失值
quanNull = data[data["订单量"].isnull()]
data.drop(index=quanNull.index, inplace=True)
# 2.识别并处理异常值
# 查看 data 的描述性统计信息
data.describe()
显示如下:
2.1异常处理
现在重点观察下这两列的最小值(min)和最大值(max),我们可以发现以下2个异常情况:
1)订单量的最小值几乎是 0
在实际业务中,不可能出现订单量为 0 的情况。
2)订单量的最大值几乎是 10 的九次方
便利店明显无法处理10 个亿的订单量。
这两种数据均属于异常值,会影响分析结果的准确性,所以我们抽取合理范围内的数据,将异常值过滤掉。
处理异常值非常简单,我们只需通过布尔索引,将订单量这一列值里大于0,以及小于100000000(1.000000e+08)的数据筛选出来,重新赋值给data即可。
完成后,再次通过describe()函数查看筛选后的描述性信息。
可以看到,订单量目前的范围在1至2000之间,是一个较合理的订单量范围,不需要再进行数据筛选。
参考代码:
输入代码:
# 1.识别并处理缺失值
quanNull = data[data["订单量"].isnull()]
data.drop(index=quanNull.index, inplace=True)
# 2.识别并处理异常值
#使用布尔索引筛选出“订单量”大于0且小于100000000的数据
data = data[(data["订单量"]>0) & (data["订单量"]<100000000)]
# 查看 data 的描述性统计信息
data.describe()
显示如下:
3.识别并处理重复值
最后,脏数据中还有一种情况,属于重复值。
pandas模块中的duplicated()函数专门用于判断DataFrame对象中是否存在重复数据。
我们可以将布尔索引和该函数结合起来,查看 data 中重复的行。
最后使用print()输出的是一个Empty DataFrame,代表是一个空DataFrame,说明没有重复的行,因此不需要处理
参考代码:
输入代码:
# 3.识别并处理重复制
# 使用布尔索引和duplicated()函数,将 data中的重复行筛选出来
dup = data[data.duplicated()]
dup
显示如下:
3.2数据处理
将脏数据清理完毕后,接下来还需要进行数据预处理。
我们见过最多的处理,就是把数据集里与日期有关的某列数据从字符串转为时间类型。
这是因为存放在CSV文件中的时间大都是字符串类型,不利于后续对其进行时间类型的处理。
1. 字符串转时间
使用pd.to_datetime()函数,批量将DataFrame中的数据转换为datetime类型。
数据集中,只有"订单日期"这一列是需要进行转换的。
我们将这一列转换后的结果输出查看,可以看到已经成功转换为datetime类型。
参考代码:
输入代码:
# 4.数据类型转换
# 使用 pd.to_datetime()函数,将“订单日期”列的数据转换为时间格式
data["订单日期"] = pd.to_datetime(data["订单日期"])
data['订单日期']
显示如下:
4.分析数据
从销售情况出发,从时间和地区这两个维度,依次分析销售额的变化趋势,及其变化原因。
时间维度,就是便利店每年每月的销售额变化,这会分别帮助我们了解不同月份的销售情况,找出是否有淡旺季之分。
地区维度,则是看不同地区销售额的占比,以便对不同区域采取对应的运营策略。
为了获取每年的每月销售额,我们可以先将便利店销售额按照年份进行分组,再在各个年份的分组中按月重新采样聚合。
相当于是先分组,然后在各个分组里进行重采样,最后聚合。
可是现在数据中并没有年份信息,所以需要先从"订单日期"里进行提取。 我们可以使用data["订单日期"].dt.year获取 "订单日期" 这列数据的年份信息, 并将其作为新列添加到data中。 通过输出可以看到,已在data变量中成功添加了一列年份信息数据。
参考代码:
输入代码:
data["年份"] = data["订单日期"].dt.year
data
显示如下:
重采样
还有一点很重要:重采样需要根据时间格式的行索引来进行操作,否则会出现报错。
因此,为了在处理过程中更加方便,我们通常会把数据中时间格式的列作为行索引index后,再进行重采样。
本例中,我们可以使用 set_index() 函数将 data["订单日期"] 设置成data变量的行索引index。
参考代码:
输入代码:
data = data.set_index("订单日期")
data
显示如下:
计算每年每个月的销售额总和
接下来,就可以对销售额完成分组、重采样和聚合操作。 先对data["销售额"]使用groupby()函数,按照data["年份"]进行分组,这样最后的结果里就只会有销售额,不包含其它无关信息,比如订单量等。 再按月("M")进行重采样,最后求和。具体代码如下:
参考代码:
输入代码:
groupByMonth = data["销售额"].groupby(data["年份"]).resample("M").sum()
groupByMonth
显示如下:
groupByMonth画出对应的折线图
获取到2018-2021这四年内每个月的销售额后,将每年对应的销售额绘制成一个折线图,并将四年的折线图都放在一个画布里来展示。 那我们怎么能通过groupByMonth画出对应的折线图呢?
我们来观察一下groupByMonth,可以看到,现在的销售额按照年份进行了分组,每年都有每月对应的销售额。 并且,出现了两层行索引: 第一层索引是使用groupby()函数时产生的,对应各年份; 第二层索引是使用resample()函数时产生的,对应各月份。
我们可以通过.loc属性,访问groupByMonth的多层索引,将每年的数据依次提取出来。 这样就可以依次根据year_2018、year_2019、year_2020和year_2021变量,绘制出对应年份的每月销售额变化趋势。
参考代码:
输入代码:
groupByMonth = data["销售额"].groupby(data["年份"]).resample('M').sum()
year_2018 = groupByMonth.loc[2018]
year_2019 = groupByMonth.loc[2019]
year_2020 = groupByMonth.loc[2020]
year_2021 = groupByMonth.loc[2021]
year_2021
显示如下:
5.可视化
绘图的准备
为了让图像更利于理解,我们使用 strftime()函数,依次将year_2018、year_2019、year_2020和year_2021的index转换为"月"的格式。 然后,就可以依次调用4次plt.plot()函数绘制折线图,并传入label参数,设置对应的图例。 在使用plt.show()展示图像前,通过plt.legend()方法显示图例即可。
参考代码:
输入代码:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "Times New Roman"
year_2018.index = year_2018.index.strftime("%m")
year_2019.index = year_2019.index.strftime("%m")
year_2020.index = year_2020.index.strftime("%m")
year_2021.index = year_2021.index.strftime("%m")
plt.plot(year_2018.index, year_2018.values, label="2018")
plt.plot(year_2019.index, year_2019.values, label="2019")
plt.plot(year_2020.index, year_2020.values, label="2020")
plt.plot(year_2021.index, year_2021.values, label="2021")
plt.legend()
plt.show()
显示如下:
月度销售额分析
通过不同年份月度销售额我们可以看出:
该便利店2018年-2021年每一年的销售额同比上一年都是上升趋势,销售季节性明显,总体上半年是淡季,下半年是旺季。 上半年中5月份销售额比较高,下半年中7月份的销售额偏低。 对于旺季的月份,可以继续维持运营推广等策略,还可以加大投入,提高整体销售额;而对于淡季的月份,可以结合产品特点进行新产品拓展,举办一些促销活动等吸引客户。
市场布局分析
完成时间维度销售额的分析,便利店订单共来自全国6个地区,因此可以看一下不同地区之间的销售情况,以便对不同地区采取对应的经营策略。
可以通过簇形柱状图,来直观展示2018-2021这四年各地区的销售情况。 那么我们就需要获取每个地区的每年销售总额。 按照之前的思路,其实就是先将便利店销售额按照每个地区进行分组,再在各个地区的分组中按年份重新采样聚合。
和之前一样,需要先使用set_index()函数将data["订单日期"]设置成data变量的行索引index。 然后,就可以对data["销售额"]使用groupby()函数,按照data["地区"]进行分组。 再按年("Y")进行重采样,最后求和。具体代码如下:
参考代码:
输入代码:
groupByArea = data["销售额"].groupby(data["地区"]).resample("Y").sum()
groupByArea
显示如下:
现在我们又得到了一个包含双层索引的DataFrame。 接下来就可以根据这个groupByArea,绘制簇形柱状图。 簇形柱状图的x轴是地区,y轴数据则是各地区对应的每年销售额。
绘制簇形柱状图只需要对一个DataFrame对象,使用pandas模块中的plot.bar()函数即可。 在不指定x轴和y轴数据的情况下,行索引index会作为x轴,每一行数值类型的值会被分组到并排的柱子中作为y轴。 也就是说,如果要绘制出所设想的簇形柱状图,需要重塑groupByArea的多层索引,让地区作为行索引index,各地区每年的销售总额作为每一行数值类型的值。
我们可以使用unstack()函数,将groupByArea里,名称为"订单日期"的行索引转变为列索引即可。 转变后,groupByArea的行索引就变成了地区,列索引则变成了订单日期。
参考代码:
输入代码:
groupByArea = groupByArea.unstack("订单日期")
groupByArea
显示如下:
现在就可以根据groupByArea,绘制簇形柱状图。 为了让图像更利于理解,我们使用 strftime()函数,先将groupByArea的columns转换为"年"的格式。 然后直接对groupByArea调用plot.bar()函数即可。
参考代码:
输入代码:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = "KaiTi"
# groupByArea.columns = groupByArea.columns.strftime("%Y")
groupByArea.plot.bar()
plt.show()
显示如下:
通过不同地区年度销售额我们可以看出: 每个地区总体每年销售额均处于上升趋势,尤其在东北、中南和华东三个地区的增长速度较快,可以看出市场占有能力在不断增加,企业市场前景比较好,下一年可以适当加大运营成本。 其他地区可以根据自身地区消费特点,借鉴东北、中南和华东地区的运营模式。
扩展练习
继续探索商品情况对销售额的影响,于是打算看取销售额前十的商品类别和子类别。
接下来,想要继续分析用户情况,先找到总消费金额在前10的用户,然后计算他们各自消费金额占总销售额的比例,最后用柱状图展示结果。