当前位置 博文首页 > 司夏的博客:Python数据分析案例-药店销售数据分析

    司夏的博客:Python数据分析案例-药店销售数据分析

    作者:[db:作者] 时间:2021-08-07 09:56

    最近学习了Python数据分析的一些基础知识,就找了一个药品数据分析的小项目来练一下手。

    数据分析的目的:

    本篇文章中,假设以朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况,通过对朝阳区医院的药品销售数据的分析,了解朝阳医院的患者的月均消费次数,月均消费金额、客单价以及消费趋势、需求量前几位的药品等。

    数据分析基本过程包括:获取数据、数据清洗、构建模型、数据可视化以及消费趋势分析。

    数据准备

    数据是存在Excel中的,可以使用pandas的Excel文件读取函数将数据读取到内存中,这里需要注意的是文件名和Excel中的sheet页的名字。读取完数据后可以对数据进行预览和查看一些基本信息。

    获取数据:朝阳医院2018年销售数据.xlsx(非真实数据) 提取码: 6xm2

    导入原始数据

    import numpy as np
    from pandas import Series,DataFrame
    import pandas as pd
    #导入数据
    file_name = '朝阳医院2018年销售数据.xlsx'
    # 使用ExcelFile()时需要传入目标excel文件所在路径及文件名称
    xls = pd.ExcelFile(file_name)
    # 使用parse()可以根据传入的sheet名称来提取对应的表格信息
    dataDF = xls.parse('Sheet1',dtype='object')
    # 输出前五行数据
    dataDF.head()
    
    # 使用sheet_names来查看当前表格中包含的所有sheet名称(按顺序)
    print(xls.sheet_names[0])
    

    在这里插入图片描述

    查看数据基本信息:

    #查看基本信息
    #查看数据几行几列
    print(dataDF.shape)
    #查看索引
    print(dataDF.index)
    #查看每一列的列表头内容
    print(dataDF.columns)
    #查看每一列数据统计数目
    print(dataDF.count())
    

    在这里插入图片描述

    数据清洗

    ?
    数据清洗过程包括:选择子集、列名重命名、缺失数据处理、数据类型转换、数据排序及异常值处理

    (1)选择子集

    ?
    在我们获取到的数据中,可能数据量非常庞大,并不是每一列都有价值都需要分析,这时候就需要从整个数据中选取合适的子集进行分析,这样能从数据中获取最大价值。在本次案例中不需要选取子集,暂时可以忽略这一步。

    (2)列重命名

    在数据分析过程中,有些列名和数据容易混淆或产生歧义,不利于数据分析,这时候需要把列名换成容易理解的名称,可以采用rename函数实现:

    #列重命名
    dataDF.rename(columns={'购药时间':'销售时间'},inplace=True)
    dataDF.head()
    

    在这里插入图片描述

    (3)缺失值处理

    获取的数据中很有可能存在缺失值,通过查看基本信息可以推测“购药时间”和“社保卡号”这两列存在缺失值,如果不处理这些缺失值会干扰后面的数据分析结果。
    ?
    缺失数据常用的处理方式为删除含有缺失数据的记录或者利用算法去补全缺失数据。
    ?
    在本次案例中为求方便,直接使用dropna函数删除缺失数据,具体如下:

    #缺失值处理
    print('删除缺失值前:', dataDF.shape)
    
    # 使用info查看数据信息,
    print(dataDF.info())
    #删除缺失值
    dataDF = dataDF.dropna(subset=['销售时间','社保卡号'], how='any')
    print('\n删除缺失值后',dataDF.shape)
    print(dataDF.info())
    

    在这里插入图片描述

    (4)数据类型转换

    在导入数据时为了防止导入不进来,会强制所有数据都是object类型,但实际数据分析过程中“销售数量”,“应收金额”,“实收金额”,这些列需要浮点型(float)数据,“销售时间”需要改成时间格式,因此需要对数据类型进行转换。
    ?
    可以使用astype()函数转为浮点型数据:

    #数据类型转换
    dataDF['销售数量'] = dataDF['销售数量'].astype('float')
    dataDF['应收金额'] = dataDF['应收金额'].astype('float')
    dataDF['实收金额'] = dataDF['实收金额'].astype('float')
    print(dataDF.dtypes)
    

    在这里插入图片描述
    在“销售时间”这一列数据中存在星期这样的数据,但在数据分析过程中不需要用到,因此要把销售时间列中日期和星期使用split函数进行分割,分割后的时间,返回的是Series数据类型:

    '''
    定义函数:分割销售日期,提取销售日期
    输入:timeColSer 销售时间这一列,Series数据类型,例‘2018-01-01 星期五’
    输出:分割后的时间,返回Series数据类型,例‘2018-01-01’
    '''
    def splitSaletime(timeColSer):
        timeList=[]
        
        for value in timeColSer:
            dateStr=value.split(' ')[0] #用空格进行分割
            timeList.append(dateStr)
    ?
        timeSer=pd.Series(timeList)    #将列表转行为一维数据Series类型
        return timeSer
    

    ?

    #获取“销售时间”这一列
    timeSer = dataDF.loc[:,'销售时间']
    ?
    #对字符串进行分割,提取销售日期
    dateSer = splitSaletime(timeSer)
    ?
    #修改销售时间这一列的值
    dataDF.loc[:,'销售时间'] = dateSer
    ?dataDF.head()
    

    在这里插入图片描述

    '''
    数据类型转换:字符串转换为日期
    ?
    把切割后的日期转为时间格式,方便后面的数据统计:
    '''
    #errors='coerce' 如果原始数据不符合日期的格式,转换后的值为空值NaT
    dataDF.loc[:,'销售时间']=pd.to_datetime(dataDF.loc[:,'销售时间'],format='%Y-%m-%d', errors='coerce')
    ?
    print(dataDF.dtypes)
    

    在这里插入图片描述

    dataDF.isnull().sum()
    

    在这里插入图片描述

    '''
    转换日期过程中不符合日期格式的数值会被转换为空值
    删除含有NaT的空行
    '''
    dataDF = dataDF.dropna(subset=['销售时间','社保卡号'],how='any')
    datasDF = dataDF.reset_index(drop = True)
    dataDF.info()
    

    在这里插入图片描述

    (5)数据排序

    此时时间是没有按顺序排列的,所以还是需要排序一下,排序之后索引会被打乱,所以也需要重置一下索引。
    ?
    其中by:表示按哪一列进行排序,ascending=True表示升序排列,ascending=False表示降序排列

    #数据排序
    dataDF = dataDF.sort_values(by='销售时间', ascending=True)
    dataDF = dataDF.reset_index(drop=True)
    dataDF.head()
    

    在这里插入图片描述

    (6)异常值处理

    先查看数据的描述统计信息

    #查看描述统计信息
    dataDF.describe()
    

    在这里插入图片描述
    通过描述统计信息可以看到,“销售数量”、“应收金额”、“实收金额”这三列数据的最小值出现了负数,这明显不符合常理,数据中存在异常值的干扰,因此要对数据进一步处理,以排除异常值的影响:

    #将'销售数量'这一列小于0的数据排除掉
    pop = dataDF.loc[:,'销售数量'] > 0
    dataDF = dataDF.loc[pop,:]
    dataDF.describe()
    

    在这里插入图片描述

    构建模型及数据可视化

    数据清洗完成后,需要利用数据构建模型(就是计算相应的业务指标),并用可视化的方式呈现结果。

    (1)业务指标1:月均消费次数

    月均消费次数 = 总消费次数 / 月份数(同一天内,同一个人所有消费算作一次消费)

    #计算总消费次数
    #删除重复数据
    kpil_Df = dataDF.drop_duplicates(subset=['销售时间','社保卡号'])
    totalI = kpil_Df.shape[0]
    print('总消费次数=',totalI)
    
    #计算月份数
    #按销售时间升序排序
    kpil_Df = kpil_Df.sort_values(by='销售时间', ascending=True)
    #重命名行名
    kpil_Df = kpil_Df.reset_index(drop=True)
    #获取时间范围
    startTime = kpil_Df.loc[0,'销售时间']
    endTime = kpil_Df.loc[totalI-1,'销售时间']
    #计算月份
    #天数
    daysI = (endTime-startTime).days
    mounthI = daysI//30
    print('月份数=',mounthI)
    ?
    #月平均消费次数
    kpil_I = totalI//mounthI
    print('业务指标1:月均消费次数=', kpil_I)
    

    在这里插入图片描述

    (2)业务指标2:月均消费金额

    月均消费金额 = 总消费金额 / 月份数

    #消费总金额
    totalMoneyF = dataDF.loc[:,'实收金额'].sum()
    mounthMoney = totalMoneyF // mounthI
    print('业务指标2:月均消费金额=', mounthMoney)
    

    在这里插入图片描述

    (3)客单价

    客单价 = 总消费金额 / 总消费次数

    #客单价
    pct = totalMoneyF / totalI
    print('业务指标3:客单价=', pct)
    

    在这里插入图片描述

    (4)消费趋势

    ?

    a. 导入python可视化相关的包

    ?

    b. 分析每天的消费金额

    import matplotlib.pyplot as plt
    import matplotlib
    #画图时用于显示中文字符
    from pylab import mpl
    ?
    mpl.rcParams['font.sans-serif'] = ['SimHei'] # SimHei是黑体的意思
    #在操作之前先复制一份
    #mpl.rcParams['font.sans-serif'] = ['Songti'] # SimHei是黑体的意思
    #font = FontProperties(fname='/Library/Fonts/Songti.ttc') #设置字体
    #在操作之前先复制一份数据,防止影响清洗后的数据
    groupDF = dataDF
    ?
    #将'销售时间'设置为index
    groupDF.index = groupDF['销售时间']
    print(groupDF.head())
    gb = groupDF.groupby(groupDF.index)
    print(gb)
    dayDF = gb.sum()
    print(dayDF)
    #画图
    plt.plot(dayDF['实收金额'])
    plt.title('按天消费金额')
    plt.xlabel('时间')
    plt.ylabel('实收金额')
    plt.show()
    

    在这里插入图片描述
    从结果可以看出,每天消费总额差异较大,除了个别天出现比较大笔的消费,大部分人消费情况维持在1000-2000元以内。

    c. 分析每月的消费金额

    ?
    接下来,我销售时间先聚合再按月分组进行分析:

    #将销售时间聚合按月分组
    gb = groupDF.groupby(groupDF.index.month)
    print(gb)
    monthDF = gb.sum()
    print(monthDF)
    ?
    plt.plot(monthDF['实收金额'])
    plt.title('按月消费金额')