139、利用pandas进行简单数据分析——医院销售数据分析案例

这一周找了一个有关医院销售分析案例进行练习,具体如下。

在本篇文章中,数据分析的基本过程包括两方面:
一、数据分析的目的
数据分析是指用适当的统计分析方法对收集来的大量数据进行分析,提取有用信息和形成结论而对数据加以详细研究和概括总结的过程。
本篇文章中,假设以朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况,这就需要知道几个业务指标,例如:月均消费次数,月均消费金额、客单价以及消费趋势。
二、数据分析基本过程
数据分析基本过程包括:获取数据、数据清洗、构建模型、数据可视化以及消费趋势
1.获取数据
Excel中的数据部分截图:

药品销售部分数据截图.png

先导入包,然后读取文件,读取的时候用object读取,防止有些数据读不了:
1.png

然后查看这些基本信息:
2.png

总共有6578行7列数据,但是“购药时间”和“社保卡号”这两列只有6576个数据,而“商品编码”一直到“实收金额”这些列都是只有6577个数据,这就意味着数据中存在缺失值,可以推断出数据中存在一行缺失值,此外“购药时间”和“社保卡号”这两列都各自存在一个缺失数据,这些缺失数据在后面步骤中需要进一步处理。
2.数据清洗
数据清洗过程包括:选择子集、列名重命名、缺失数据处理、数据类型转换、数据排序及异常值处理
(1)选择子集
在我们获取到的数据中,可能数据量非常庞大,并不是每一列都有价值都需要分析,这时候就需要从整个数据中选取合适的子集进行分析,这样能从数据中获取最大价值。在本次案例中不需要选取子集,暂时可以忽略这一步。
(2)列名重命名

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

(3)缺失数据处理

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

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

可以使用astype()函数转为浮点型数据:
5.png

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

接着把切割后的日期转为时间格式,方便后面的数据统计:
7.png

(5)数据排序

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

(6)异常值处理

先查看数据的描述统计信息:
9.png

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

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

月均消费次数 = 总消费次数 / 月份数
11.jpg

① 计算总消费次数:
12.png

② 计算月份数:
13.png

③ 计算月均消费次数:
14.png

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

月均消费金额 = 总消费金额 / 月份数
15.png

(3)业务指标3:客单价

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

(4)业务指标4:消费趋势

先导入相关的包:
17.png

① 分析每天的消费金额
18.png
20.png

从结果可以看出,每天消费总额差异较大,除了个别天出现比较大笔的消费,大部分人消费情况维持在500元以内。
② 分析每月的消费金额

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

结果显示,7月消费金额最少,这是因为7月份的数据不完整,所以不具参考价值。
1月、4月、5月和6月的月消费金额差异不大,2月和3月的消费金额迅速降低,这可能是2月和3月处于春节期间,大部分人都回家过年的原因。
③ 分析药品销售情况

对“商品名称”和“销售数量”这两列数据进行聚合为Series形式,方便后面统计,并按降序排序:
24.png

截取销售数量最多的前十种药品,并用条形图展示结果:
25.png
26.png

得到销售数量最多的前十种药品信息,这些信息将会有助于加强医院对药房的管理。

源码:

# coding: utf-8

# ## 读取excel

# In[1]:

import numpy as np
from pandas import Series,DataFrame
import pandas as pd


# In[2]:

file_name = 'D:\\Python\\notebook\\朝阳医院2018年销售数据.xlsx'
xls = pd.ExcelFile(file_name)
dataDF = xls.parse('Sheet1',dtype='object')
dataDF.head() # 只列出前5行数据


# ## 查看基本信息

# In[3]:

# 查看数据的形状,即几行几列
dataDF.shape


# In[4]:

# 查看索引
dataDF.index


# In[5]:

# 查看每一列的列表头内容
dataDF.columns


# In[6]:

# 查看每一列数据统计数目
dataDF.count()


# ## 数据清洗

# #### 列名重命名  

# In[7]:

# 使用rename函数,把"购药时间" 改为 "销售时间"
dataDF.rename(columns={'购药时间':'销售时间'},inplace=True)
dataDF.head()


# #### 缺失数据处理

# In[8]:

print('删除缺失值前:',dataDF.shape)
dataDF.info()

# 使用dropna函数删除缺失值
dataDF = dataDF.dropna(subset=['销售时间','社保卡号'],how='any')

print('\n删除缺失值后:',dataDF.shape)
dataDF.info()


# #### 数据类型转换

# In[9]:

# 将字符串转为浮点型数据
dataDF['销售数量'] = dataDF['销售数量'].astype('float')
dataDF['应收金额'] = dataDF['应收金额'].astype('float')
dataDF['实收金额'] = dataDF['实收金额'].astype('float')
dataDF.dtypes


# In[10]:

# 定义函数将星期去除
def splitSaletime(timeColser):
    timelist = []
    for val in timeColser:
        data = val.split(' ')[0] # [0]表示选取的分片,这里表示切割完后选取第一个分片
        timelist.append(data)
        
    # 将列表转行为一维数据Series类型
    timeSer = Series(timelist)   
    return timeSer

# 获取"销售时间"这一列数据
time = dataDF.loc[:,'销售时间']
# 调用函数去除星期,获得日期
data = splitSaletime(time)
# 修改"销售时间"这一列的值
dataDF.loc[:,'销售时间'] = data
dataDF.head()


# In[11]:

# 字符串转日期
'''
errors='coerce'如果原始数据不符合日期的格式,转换后的值为NaT
format 是原始数据中日期的格式
'''
dataDF.loc[:,'销售时间'] = pd.to_datetime(dataDF.loc[:,'销售时间'],format='%Y-%m-%d',errors='coerce')
dataDF.dtypes


# In[12]:

'''
转换日期过程中不符合日期格式的数值会被转换为空值None,
这里删除列(销售时间,社保卡号)中为空的行
'''
dataDF = dataDF.dropna(subset=['销售时间','社保卡号'],how='any')


# #### 数据排序

# In[13]:

# 按销售时间进行升序排序
dataDF = dataDF.sort_values(by='销售时间',ascending=True)
dataDF.head()


# In[14]:

# 重置索引(index)
dataDF = dataDF.reset_index(drop=True)
dataDF.head()


# #### 异常值处理

# In[15]:

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


# In[16]:

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

# 排除异常值后再次查看描述统计信息
dataDF.describe()


# ## 构建模型及数据可视化

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

# 计算总消费次数

# In[17]:

# 删除重复数据
kpi1_Df = dataDF.drop_duplicates(subset=['销售时间','社保卡号'])
# 有多少行
totalI = kpi1_Df.shape[0]
print('总消费次数=',totalI)  


# 计算月份数

# In[18]:

# 排序
# 按销售时间升序排序
kpi1_Df = kpi1_Df.sort_values(by='销售时间',ascending=True)
# 重命名行名(index)
kpi1_Df = kpi1_Df.reset_index(drop=True)

# 获取时间范围
# 最小时间值
startTime = kpi1_Df.loc[0,'销售时间']
# 最大时间值
endTime = kpi1_Df.loc[totalI-1,'销售时间']

# 计算月份数
# 天数
daysI = (endTime-startTime).days
# 月份数:运算符"//"表示取整除,返回商的整数部分
monthsI = daysI // 30

print('月份数:',monthsI)


# 计算月均消费次数

# In[19]:

kpi1_I = totalI // monthsI
print('业务指标1:月均消费次数=',kpi1_I)


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

# In[20]:

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


# ### 业务指标3:客单价

# In[21]:

# 客单价 = 总消费金额 / 总消费次数
pct = totalMoneyF / totalI
print('业务指标3:客单价=',pct)


# ### 业务指标4:消费趋势

# In[22]:

import matplotlib.pyplot as plt
import matplotlib

# 画图时用于显示中文字符
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']   # SimHei是黑体的意思

# 在操作之前先复制一份数据,防止影响清洗后的数据
groupDf = dataDF


# #### 每天的消费金额

# In[23]:

# 重命名行(index)为销售时间所在列的值
groupDf.index = groupDf['销售时间']
groupDf.head()


# In[24]:

# 画图
plt.plot(groupDf['实收金额'])

plt.title('按天消费金额图')
plt.xlabel('时间')
plt.ylabel('实收金额')
# 保存图片
plt.savefig('C:\\Users\\Administrator\\Desktop\\day.png')
# 显示图片
plt.show()


# #### 每月的消费金额

# In[25]:

# 将销售时间聚合按月分组
gb = groupDf.groupby(groupDf.index.month)
print(gb)


# In[26]:

# 应用函数,计算每个月的消费总额
monthDf = gb.sum()
print(monthDf)


# In[27]:

# 描绘按月消费金额图
plt.plot(monthDf['实收金额'])
plt.title('按月消费金额图')
plt.xlabel('月份')
plt.ylabel('实收金额')
plt.savefig('C:\\Users\\Administrator\\Desktop\\month.png')
plt.show()


# #### 药品销售情况

# In[28]:

# 聚合统计各种药品的销售数量
medicine = groupDf[['商品名称','销售数量']]
bk = medicine.groupby('商品名称')[['销售数量']]
re_medicine = bk.sum()

# 对药品销售数量按降序排序
re_medicine = re_medicine.sort_values(by='销售数量',ascending=False)

re_medicine.head()


# In[29]:

# 截取销售数量最多的十种药品
top_medicine = re_medicine.iloc[:10,:]
print(top_medicine)


# In[30]:

# 用条形图展示销售数量前十的药品
top_medicine.plot(kind='bar')

plt.title('药品销售前十情况')
plt.xlabel('药品种类')
plt.ylabel('销售数量')
plt.legend(loc=0)
plt.savefig('C:\\Users\\Administrator\\Desktop\\medicine.png')
plt.show()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容