点击标题下「蓝色微信名」可快速关注

本文所有代码都通过运行!

数据分析的基本过程一般分为以下几个部分: 提出问题、理解数据、数据清洗、构建模型、数据可视化

本项目带你根据以上过程详细分析朝阳医院药品销售数据!欢迎Fork后自主运行学习~

每日一句

是谁说伟大才值得被歌颂,乘风破浪后也不一定成功。生命只能向前,坚定信念的人都是英雄。

1.提出问题

在数据分析之前,我们先要明确分析目标是什么,这样可以避免我们像无头苍蝇一样拿着数据无从下手,也可以帮助我们更高效的选取数据,进行分析研究。

本次的分析目标是从销售数据中分析出以下业务指标:

1)月均消费次数

2)月均消费金额

3)客单价

4)消费趋势

有了分析目标,我们再来关注一下数据情况。

2.理解数据

1)导入数据包,提取数据文件

In [1]:

#导入numpy、pandas包import numpy as npimport pandas as pd#导入数据salesDf = pd.read_excel('/home/kesci/input/medical9242/朝阳医院2018年销售数据.xlsx')

2)查看导入数据的基本状况

In [2]:

#查看导入数据的类型type(salesDf)

Out[2]:

pandas.core.frame.DataFrame

In [3]:

salesDf.dtypes

Out[3]:

购药时间     object
社保卡号    float64
商品编码    float64
商品名称     object
销售数量    float64
应收金额    float64
实收金额    float64
dtype: object

In [4]:

salesDf.shape

Out[4]:

(6578, 7)

In [5]:

#查看列名salesDf.columns

Out[5]:

Index(['购药时间', '社保卡号', '商品编码', '商品名称', '销售数量', '应收金额', '实收金额'], dtype='object')

In [6]:

#查看每列数据的统计数目salesDf.count()

Out[6]:

购药时间    6576
社保卡号    6576
商品编码    6577
商品名称    6577
销售数量    6577
应收金额    6577
实收金额    6577
dtype: int64

In [7]:

#查看前五列salesDf.head()

Out[7]:

购药时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
0 2018-01-01 星期五 1.616528e+06 236701.0 强力VC银翘片 6.0 82.8 69.00
1 2018-01-02 星期六 1.616528e+06 236701.0 清热解毒口服液 1.0 28.0 24.64
2 2018-01-06 星期三 1.260283e+07 236701.0 感康 2.0 16.8 15.00
3 2018-01-11 星期一 1.007034e+10 236701.0 三九感冒灵 1.0 28.0 28.00
4 2018-01-15 星期五 1.015543e+08 236701.0 三九感冒灵 8.0 224.0 208.00

3.数据清洗

取得了数据,并不能马上就开始进行数据分析。我们得到的数据通常并不是完全符合我们分析要求的,而且可能存在缺失值、异常值,这些数据都会使我们的分析结果产生偏差。所以在分析之前,需要进行子集选择、缺失数据补充、异常值处理、数据类型转换等多个步骤。这些都属于数据清理的范畴。 在数据分析中,通常有多达60%的时间是花在数据清洗中的。通常的清洗步骤有以下几步: • 选择子集

• 列名重命名

• 缺失数据处理

• 数据类型转换

• 数据排序

• 异常值处理

这些步骤有些不是一步就能完成的,可能需要重复操作。

现在开始对药店销售数据进行数据清洗。

1)选择子集

药店销售数据中,项目较少,选择子集可以忽略,我们从列名重命名开始。

2)列名重命名

销售数据集,购药时间显示为销售时间更为合理,我们先把这个项目名称做一下变更。

In [8]:

#购药时间->销售时间nameChangeDict = {'购药时间':'销售时间'}#参数inplace=True表示覆盖元数据集salesDf.rename(columns = nameChangeDict,inplace=True)

3)缺失数据处理

对于缺失数据,我们可以有几种处理方法:

▪ 删除

当缺失数据占总数据量的比例很小的时候,我们通常采用删除的处理方法。

▪ 合理值填充

在某些不适合删除的场合,我们有时候也会对缺失数据进行合理值填充,如平均值,中位数,相邻数据等等。

In [9]:

#首先查看一下哪些项目存在缺失值salesDf.isnull().any()

Out[9]:

销售时间    True
社保卡号    True
商品编码    True
商品名称    True
销售数量    True
应收金额    True
实收金额    True
dtype: bool

好吧,每个项目都存在缺失值。在这个销售数据中,销售时间和社保卡号是必须项目,不可或缺。所以我们在这里只把销售时间和社保卡号有缺失的数据做删除处理。我们来查看一下销售时间和社保卡缺失的数据大小,然后做删除处理。

In [10]:

#查看一下缺失值的数量#通常可以用isnull函数来查找缺失值salesDf[salesDf[['销售时间','社保卡号']].isnull().values == True]

Out[10]:

销售时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
6570 NaN 11778628.0 2367011.0 高特灵 10.0 56.0 56.00
6571 2018-04-25 星期二 NaN 2367011.0 高特灵 2.0 11.2 9.86
6574 NaN NaN NaN NaN NaN NaN NaN
6574 NaN NaN NaN NaN NaN NaN NaN

In [11]:

#序号6574因为销售时间和社保卡号都缺失,所以会出现两次。所以我们要去掉一下重复数据。naDf = salesDf[salesDf[['销售时间','社保卡号']].isnull().values == True].drop_duplicates()naDf

Out[11]:

销售时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
6570 NaN 11778628.0 2367011.0 高特灵 10.0 56.0 56.00
6571 2018-04-25 星期二 NaN 2367011.0 高特灵 2.0 11.2 9.86
6574 NaN NaN NaN NaN NaN NaN NaN

从上面可以清楚看出销售时间和社保卡号缺失的数据一共有三条,当数据量大的时候我们可以只显示条数,不显示数据内容

In [12]:

#缺失数据行数naDf.shape[0]

Out[12]:

3

现在把这些缺失数据进行删除

In [13]:

#含有销售时间和社保卡号的缺失数据删除salesDf = salesDf.dropna(subset=['销售时间','社保卡号'],how = 'any')#删除后数据集规模显示salesDf.shape

Out[13]:

(6575, 7)

在数据删除后要及时更新一下最新的序号,不然可能会产生问题。

In [14]:

#重命名行名(index):排序后的列索引值是之前的行号,需要修改成从0到N按顺序的索引值salesDf=salesDf.reset_index(drop=True)

4)数据类型转换

▪ 数量、金额项目:从字符串类型转换为数值(浮点型)类型

In [15]:

salesDf['销售数量'] = salesDf['销售数量'].astype('float')salesDf['应收金额'] = salesDf['应收金额'].astype('float')salesDf['实收金额'] = salesDf['实收金额'].astype('float')print('转换后的数据类型:\n',salesDf.dtypes)
转换后的数据类型:销售时间     object
社保卡号    float64
商品编码    float64
商品名称     object
销售数量    float64
应收金额    float64
实收金额    float64
dtype: object

▪ 日期项目:从字符串类型转换为日期类型 销售日期中包含了日期和星期,我们只要保留日期内容即可。这里用一个自定义的函数dateChange来实现这个功能。

In [16]:

#日期转换def dateChange(dateSer):dateList = []for i in dateSer:#例如2018-01-01 星期五,分割后为:2018-01-01str = i.split(' ')[0]dateList.append(str)dateChangeSer = pd.Series(dateList)return dateChangeSerdateChangeSer = dateChange(salesDf['销售时间'])dateChangeSer

Out[16]:

0       2018-01-01
1       2018-01-02
2       2018-01-06
3       2018-01-11
4       2018-01-15
5       2018-01-20
6       2018-01-31
7       2018-02-17
8       2018-02-22
9       2018-02-24
10      2018-03-05
11      2018-03-05
12      2018-03-05
13      2018-03-07
14      2018-03-09
15      2018-03-15
16      2018-03-15
17      2018-03-15
18      2018-03-20
19      2018-03-22
20      2018-03-23
21      2018-03-24
22      2018-03-24
23      2018-03-28
24      2018-03-29
25      2018-04-05
26      2018-04-07
27      2018-04-13
28      2018-04-22
29      2018-05-01...
6545    2018-04-05
6546    2018-04-05
6547    2018-04-09
6548    2018-04-10
6549    2018-04-10
6550    2018-04-10
6551    2018-04-12
6552    2018-04-13
6553    2018-04-13
6554    2018-04-14
6555    2018-04-15
6556    2018-04-15
6557    2018-04-15
6558    2018-04-15
6559    2018-04-16
6560    2018-04-17
6561    2018-04-18
6562    2018-04-21
6563    2018-04-22
6564    2018-04-24
6565    2018-04-25
6566    2018-04-25
6567    2018-04-25
6568    2018-04-26
6569    2018-04-26
6570    2018-04-27
6571    2018-04-27
6572    2018-04-27
6573    2018-04-27
6574    2018-04-28
Length: 6575, dtype: object

In [17]:

salesDf['销售时间'] = dateChangeSersalesDf.head()

Out[17]:

销售时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
0 2018-01-01 1.616528e+06 236701.0 强力VC银翘片 6.0 82.8 69.00
1 2018-01-02 1.616528e+06 236701.0 清热解毒口服液 1.0 28.0 24.64
2 2018-01-06 1.260283e+07 236701.0 感康 2.0 16.8 15.00
3 2018-01-11 1.007034e+10 236701.0 三九感冒灵 1.0 28.0 28.00
4 2018-01-15 1.015543e+08 236701.0 三九感冒灵 8.0 224.0 208.00

在做完转化后再观察一下有没有产生新的缺失值

In [18]:

salesDf['销售时间'].isnull().any()

Out[18]:

False

In [19]:

salesDf.dtypes

Out[19]:

销售时间     object
社保卡号    float64
商品编码    float64
商品名称     object
销售数量    float64
应收金额    float64
实收金额    float64
dtype: object

数据没有产生新的缺失,我们继续向下,把销售时间的数据类型转为日期型。

In [20]:

dateSer=pd.to_datetime(salesDf['销售时间'], format = '%Y-%m-%d', errors='coerce')dateSer

Out[20]:

0      2018-01-01
1      2018-01-02
2      2018-01-06
3      2018-01-11
4      2018-01-15
5      2018-01-20
6      2018-01-31
7      2018-02-17
8      2018-02-22
9      2018-02-24
10     2018-03-05
11     2018-03-05
12     2018-03-05
13     2018-03-07
14     2018-03-09
15     2018-03-15
16     2018-03-15
17     2018-03-15
18     2018-03-20
19     2018-03-22
20     2018-03-23
21     2018-03-24
22     2018-03-24
23     2018-03-28
24     2018-03-29
25     2018-04-05
26     2018-04-07
27     2018-04-13
28     2018-04-22
29     2018-05-01...
6545   2018-04-05
6546   2018-04-05
6547   2018-04-09
6548   2018-04-10
6549   2018-04-10
6550   2018-04-10
6551   2018-04-12
6552   2018-04-13
6553   2018-04-13
6554   2018-04-14
6555   2018-04-15
6556   2018-04-15
6557   2018-04-15
6558   2018-04-15
6559   2018-04-16
6560   2018-04-17
6561   2018-04-18
6562   2018-04-21
6563   2018-04-22
6564   2018-04-24
6565   2018-04-25
6566   2018-04-25
6567   2018-04-25
6568   2018-04-26
6569   2018-04-26
6570   2018-04-27
6571   2018-04-27
6572   2018-04-27
6573   2018-04-27
6574   2018-04-28
Name: 销售时间, Length: 6575, dtype: datetime64[ns]

In [21]:

dateSer.isnull().any()

Out[21]:

True

In [22]:

compareDf = pd.DataFrame(dateSer[dateSer.isnull()],salesDf[dateSer.isnull()]['销售时间'])compareDf

Out[22]:

销售时间
销售时间
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT
2018-02-29 NaT

查看了下数据,产生空值的原因是因为数据中出现了'2018-02-29'这样实际不存在的日期。在实际应用中,最好能向业务部门询问一下产生的原因,看下是不是因为日期推算不正确导致了这样原因的产生,需不需要将这样的数据进行一下必要的修正。这里就简单的把数据进行删除。

In [23]:

salesDf['销售时间'] = dateSersalesDf.dtypes

Out[23]:

销售时间    datetime64[ns]
社保卡号           float64
商品编码           float64
商品名称            object
销售数量           float64
应收金额           float64
实收金额           float64
dtype: object

In [24]:

salesDf=salesDf.dropna(subset=['销售时间','社保卡号'],how='any')salesDf.shape

Out[24]:

(6552, 7)

In [25]:

salesDf=salesDf.reset_index(drop=True)

5)数据排序 销售记录一般是以销售时间为顺序排列的,所以我们对数据进行一下排序

In [26]:

#按销售时间排序salesDf = salesDf.sort_values(by='销售时间')#再次更新一下序号salesDf = salesDf.reset_index(drop = True)

6)异常值处理

在下面数据集的描述指标中可以看出,存在销售数量为负的数据,这明显是不合理的,我们把这部分数据也进行删除

In [27]:

salesDf.describe()

Out[27]:

社保卡号 商品编码 销售数量 应收金额 实收金额
count 6.552000e+03 6.552000e+03 6552.000000 6552.00000 6552.000000
mean 6.095150e+09 1.015031e+06 2.384158 50.43025 46.266972
std 4.888430e+09 5.119572e+05 2.374754 87.68075 81.043956
min 1.616528e+06 2.367010e+05 -10.000000 -374.00000 -374.000000
25% 1.014290e+08 8.614560e+05 1.000000 14.00000 12.320000
50% 1.001650e+10 8.615070e+05 2.000000 28.00000 26.500000
75% 1.004898e+10 8.687840e+05 2.000000 59.60000 53.000000
max 1.283612e+10 2.367012e+06 50.000000 2950.00000 2650.000000

In [28]:

#删除异常值:通过条件判断筛选出数据#查询条件querySer=salesDf.loc[:,'销售数量']>0#应用查询条件print('删除异常值前:',salesDf.shape)salesDf=salesDf.loc[querySer,:]print('删除异常值后:',salesDf.shape)
删除异常值前: (6552, 7)
删除异常值后: (6509, 7)

数据清洗完了之后,我们终于可以来搭建我们的模型啦。当然如果在模型搭建过程中再次发现数据异常情况,我们还是要对数据进行进一步的清洗。

4.构建模型

1)业务指标1:月均消费次数=总消费次数 / 月份数

总消费次数:同一天内,同一个人发生的所有消费算作一次消费。这里我们根据列名(销售时间,社区卡号)结合,如果这两个列值同时相同,只保留1条,将重复的数据删除

月份数:数据已经按照销售时间进行排序,只需将最后的数据与第一条数据相减就可换算出月份数

In [29]:

#总消费次数计算kpDf = salesDf.drop_duplicates(subset=['销售时间','社保卡号'])total = kpDf.shape[0]print('总消费次数为:',total)
总消费次数为: 5345

In [30]:

#月份数计算startDay = salesDf.loc[0,'销售时间']print('开始日期:',startDay)endDay = salesDf.loc[salesDf.shape[0]-1,'销售时间']print('结束日期:',endDay)monthCount = (endDay - startDay).days//30print('月份数:',monthCount)
开始日期: 2018-01-01 00:00:00
结束日期: 2018-07-18 00:00:00
月份数: 6

In [31]:

#业务指标1:月均消费次数=总消费次数 / 月份数kpi1 = total / monthCountprint('业务指标1:月均消费次数=',kpi1)
业务指标1:月均消费次数= 890.8333333333334

2)指标2:月均消费金额 = 总消费金额 / 月份数

In [32]:

totalMoney = salesDf['实收金额'].sum()kpi2 = totalMoney / monthCountprint('业务指标2:月平均消费金额=',kpi2)
业务指标2:月平均消费金额= 50672.494999999995

3)指标3:客单价=总消费金额 / 总消费次数

In [33]:

kpi3 = kpi2 / kpi1print('业务指标3:客单价=',kpi3)
业务指标3:客单价= 56.88212722170252

4)指标4:消费趋势,画图:折线图

In [34]:

#在进行操作之前,先把数据复制到另一个数据框中,防止对之前清洗后的数据框造成影响groupDf=salesDf#第1步:重命名行名(index)为销售时间所在列的值groupDf.index=groupDf['销售时间']groupDf.head()

Out[34]:

销售时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
销售时间
2018-01-01 2018-01-01 1.616528e+06 236701.0 强力VC银翘片 6.0 82.8 69.0
2018-01-01 2018-01-01 1.078916e+08 861456.0 酒石酸美托洛尔片(倍他乐克) 2.0 14.0 12.6
2018-01-01 2018-01-01 1.616528e+06 861417.0 雷米普利片(瑞素坦) 1.0 28.5 28.5
2018-01-01 2018-01-01 1.007397e+10 866634.0 硝苯地平控释片(欣然) 6.0 111.0 92.5
2018-01-01 2018-01-01 1.001429e+10 866851.0 缬沙坦分散片(易达乐) 1.0 26.0 23.0

In [35]:

#第2步:分组gb=groupDf.groupby(groupDf.index.month)#第3步:应用函数,计算每个月的消费总额mounthDf=gb.sum()mounthDf

Out[35]:

社保卡号 商品编码 销售数量 应收金额 实收金额
销售时间
1 6.257155e+12 1.073329e+09 2527.0 53561.6 49461.19
2 4.702493e+12 7.438598e+08 1858.0 42028.8 38790.38
3 6.124761e+12 1.007946e+09 2225.0 45318.0 41597.51
4 7.620230e+12 1.226705e+09 3010.0 54324.3 48812.70
5 5.898556e+12 1.004573e+09 2225.0 51263.4 46925.27
6 5.421001e+12 9.289637e+08 2328.0 52300.8 48327.70
7 3.608900e+12 6.259256e+08 1483.0 32568.0 30120.22

In [36]:

import matplotlib.pyplot as pltimport seaborn as snsimport matplotlib as mplmpl.rcParams['font.sans-serif'] = ['SimHei']mpl.rcParams['font.serif'] = ['SimHei']sns.set_style("darkgrid",{"font.sans-serif":['simhei', 'Arial']})import matplotlib.pyplot as plt%matplotlib inline#绘制销售数量图plt.plot(mounthDf['销售数量'],color = 'b')

Out[36]:

[<matplotlib.lines.Line2D at 0x7fa6e872e550>]
findfont: Font family ['sans-serif'] not found. Falling back to DejaVu Sans.

四月份为最高点,二月份为前期一个最低点,而且在四月份以后销售一直处于向下的趋势,在记录的日期中,七月份达到了历史最低水平。

数据集获取方式

关注公众号,后台回复“医院”关键字获取哦~

实战|朝阳医院药品销售分析案例相关推荐

  1. python销售数据分析方法_Python数据分析之药品销售案例分析(上)

    一.一维数组 1.1 一维数据可以由numpy中的Array函数或者Pandas包中的Series函数创建,series函数是建立在array函数基础上,功能更加强大一些. 1.2Array 函数 利 ...

  2. 数据分析案例1.0——药品销售分析

    药品销售分析 前言 数据获取 数据清洗 选择子集 列名重命名 缺失数据处理 数据类型转换 数据排序 异常值处理 构建模型 业务指标1:月均消费次数 业务指标2:月均消费金额 业务指标3:客单价 数据可 ...

  3. 【阅读笔记】联邦学习实战——联邦个性化推荐案例

    联邦学习实战--联邦个性化推荐案例 前言 1. 引言 2. 传统的集中式个性化推荐 2.1 矩阵分解 2.2 因子分解机 3. 联邦矩阵分解 3.1 算法详解 3.2 详细实现 4 联邦因子分解机 4 ...

  4. c++实战(OpenCV C++案例实战九《对象计数》)

    c++实战(OpenCV C++案例实战九<对象计数>) 一.OpenCV C++案例实战九<对象计数> 一.OpenCV C++案例实战九<对象计数> OpenC ...

  5. DDD系列 实战一 应用设计案例 (golang)

    DDD系列 实战一 应用设计案例 (golang) 基于 ddd 的设计思想, 核心领域需要由纯内存对象+基础设施的抽象的接口组成 独立于外部框架: 比如 web 框架可以是 gin, 也可以是 be ...

  6. 案例学习|Python实现某医院药品销售分析

    数据分析的基本过程一般分为以下几个部分: 提出问题 获取并理解数据 数据清洗 构建模型 数据可视化 1.提出问题 在数据分析之前,我们先要明确分析目标,可以帮助我们更高效的选取数据,进行分析研究. 本 ...

  7. 【Flutter从入门到实战】⑪、豆瓣案例-1、星星评分Widget、虚线Widget、TabbarWidget、BottomNavigationBarItem的封装、初始化配置抽取

    Flutter从入门到实战 一共分为23个系列 ①(Flutter.Dart环境搭建篇) 共3个内容 已更新 ②(Dart语法1 篇) 共4个内容 已更新 ③(Dart语法2 篇) 共2个内容 已更新 ...

  8. python医药数据分析_Python数据分析案例-药品数据分析案例

    最近学习了python数据分析的一些基础知识,有numpy,pandas,matplotlib等,找了一个药品数据分析的小项目练一下手. 数据分析的步骤一般可以分为6个: 1,明确分析的目的 2,数据 ...

  9. python预测药_Python数据分析实例-药品销售分析

    学习了Python的各种基础语法和常用包后,你是否对如何使用Python在实际工作中进行数据分析一头雾水?如果是,今天这篇文章一定能带给你一些用数据分析解决实际问题的思路. 数据分析的目的决定了你的分 ...

  10. 机器学习实战-泰坦尼克号生存预测案例

    泰坦尼克号生存预测案例 操作平台:Jupyter Notebook 实验数据:从官方下载的泰坦尼克号测试集与训练集 使用语言:python 实验步骤: 安装我们所需要的第三方库,本次实验需要额外下载安 ...

最新文章

  1. 如何使用C#中的WebClient将数据发布到特定URL
  2. 一个html有几个css,几个CSS的黑科技_html/css_WEB-ITnose
  3. 【poj2464】树状数组
  4. jQuery css
  5. python多线程调度_python并发编程之进程、线程、协程的调度原理(六)
  6. ALSA播放时杂音问题
  7. 代码制作数字流星雨_C语言实现流星雨
  8. 微软应用商店显示服务器错误代码,微软应用商店打不开 显示错误代码: 0x80131500...
  9. maven的pom详解
  10. JS根据身份证号码获取性别
  11. SSL证书以及其验证过程
  12. 电磁屏蔽一般可分为三种
  13. 城市场景车路协同网络该怎么建?
  14. 戴尔服务器修改主板IP,Dell R410, R710设置iDRAC(远控卡)的ip地址
  15. scikit-learn广义线性模型之最小二乘法
  16. Python 数据分析 —— Numpy
  17. 使用matplotlib,pylab进行python绘图
  18. 《神经科学:探索脑》学习笔记(第13章 运动的脊髓控制)
  19. 美丽天天秒系统开发模式与美丽天天秒系统源码分享
  20. 美国CPSIA关于玩具和儿童产品的测试要求,CPC证书要求

热门文章

  1. 软考软件设计师下午真题-面向对象的程序设计与实现-组合设计模式(2021年上半年试题六))Java代码讲解
  2. 计算机如何保护数据,保护数据-避免丢失
  3. 【UE4_蓝图】录制麦克风声音/系统声音并输出保存WAV文件
  4. F450无人机组装与调试
  5. 电驴搜索服务器正在连接,电驴 电驴连接不上服务器-完美教程资讯
  6. Android 12之启动画面Splash Screens(一) -- 适配
  7. VC++_2010_学习版_未能下载以下组件解决方案和microsoft应用程序错误报告
  8. 支持二级汉字的 php 汉字助记码生成
  9. linux uart驱动协议
  10. Fujitsu(富士通)扫描仪——fi-6130z 无感安装设置