数据分析——实战|朝阳医院药品销售分析案例

通过前面学习的pandas基础,下面开始实践~每天进步一点点

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

本项目根据以上过程详细分析朝阳医院药品销售数据!

数据获取

链接:https://pan.baidu.com/s/1IsU5jEgPxTmLGq3z3ja_pA
提取码:h51q

1.提出问题

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

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

1)月均消费次数

2)月均消费金额

3)客单价

4)消费趋势

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

2.理解数据

1)导入数据包,提取数据文件
# coding:utf-8
import pandas as pd

salesDf = pd.read_excel(r'.\朝阳医院2018年销售数据.xlsx')
2)查看导入数据的基本状况
#查看导入数据的类型
print(type(salesDf))
# 查看列数据类型
print(salesDf.dtypes())
# 行数 列数
print(salesDf.shape)
#查看列名
print(salesDf.columns)
#查看每列数据的统计数目
print(salesDf.count())
#查看前五列
print(salesDf.head())
打印结果:
image.png
3.数据清洗

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

• 选择子集

• 列名重命名

• 缺失数据处理

• 数据类型转换

• 数据排序

• 异常值处理

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

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

1)选择子集

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

2)列名重命名

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

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

3)缺失数据处理

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

▪ 删除

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

▪ 合理值填充

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

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

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

#查看一下缺失值的数量
#通常可以用isnull函数来查找缺失值
salesDf[salesDf[['销售时间','社保卡号']].isnull().values == True]
image.png
#序号6574因为销售时间和社保卡号都缺失,所以会出现两次。所以我们要去掉一下重复数据。
naDf = salesDf[salesDf[['销售时间','社保卡号']].isnull().values == True].drop_duplicates()
print(naDf)
image.png

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

#缺失数据行数
print(naDf.shape[0])
#现在把这些缺失数据进行删除
#含有销售时间和社保卡号的缺失数据删除
salesDf = salesDf.dropna(subset=['销售时间','社保卡号'],how = 'any')
#删除后数据集规模显示
print(salesDf.shape)

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

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

4)数据类型转换

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

salesDf['销售数量'] = salesDf['销售数量'].astype('float')
salesDf['应收金额'] = salesDf['应收金额'].astype('float')
salesDf['实收金额'] = salesDf['实收金额'].astype('float')
print('转换后的数据类型:\n',salesDf.dtypes)

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

# 日期转换
def dateChange(dateSer):
    dateList = [i.split(' ')[0] for i in dateSer]
    dateChangeSer = pd.Series(dateList)
    return dateChangeSer


dateChangeSer = dateChange(salesDf['销售时间'])
print(dateChangeSer)
salesDf['销售时间'] = dateChangeSer
print(salesDf.head())
image.png

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

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

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

dateSer=pd.to_datetime(salesDf['销售时间'], format = '%Y-%m-%d', errors='coerce')
print(dateSer)
print(dateSer.isnull().any())
compareDf = pd.DataFrame(dateSer[dateSer.isnull()],salesDf[dateSer.isnull()]['销售时间'])
print(compareDf)
image.png

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

salesDf['销售时间'] = dateSer
print(salesDf.dtypes)
salesDf = salesDf.dropna(subset=['销售时间', '社保卡号'], how='any')
print(salesDf.shape)
salesDf = salesDf.reset_index(drop=True)

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

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

6)异常值处理

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

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

删除异常值前: (6552, 7)
删除异常值后: (6509, 7)

4.构建模型

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

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

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

# 总消费次数计算
kpDf = salesDf.drop_duplicates(subset=['销售时间', '社保卡号'])
total = kpDf.shape[0]
print('总消费次数为:', total)
# 月份数计算
startDay = salesDf.loc[0, '销售时间']
print('开始日期:', startDay)
endDay = salesDf.loc[salesDf.shape[0] - 1, '销售时间']
print('结束日期:', endDay)
monthCount = (endDay - startDay).days // 30
print('月份数:', monthCount)
# # 业务指标1:月均消费次数=总消费次数 / 月份数
kpi1 = total / monthCount
print('业务指标1:月均消费次数=', kpi1)
image.png

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

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

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

业务指标2:月平均消费金额= 50672.494999999995
3)指标3:客单价=总消费金额 / 总消费次数

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

业务指标3:客单价= 56.88212722170252

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

import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt

# 在进行操作之前,先把数据复制到另一个数据框中,防止对之前清洗后的数据框造成影响
groupDf = salesDf
# 第1步:重命名行名(index)为销售时间所在列的值
groupDf.index = groupDf['销售时间']
print(groupDf.head())
image.png
# 第2步:分组
gb = groupDf.groupby(groupDf.index.month)
# 第3步:应用函数,计算每个月的消费总额
mounthDf = gb.sum()
print(mounthDf)
image.png
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['font.serif'] = ['SimHei']
sns.set_style("darkgrid", {"font.sans-serif": ['simhei', 'Arial']})
# 绘制销售数量图
plt.plot(mounthDf['销售数量'], color='b')
plt.show()

image.png

参考链接:https://mp.weixin.qq.com/s?__biz=MzIyNTg3ODM0Ng==&mid=2247483699&idx=1&sn=67826b5afdfb7cc7d924601edf6a6acb&chksm=e8784916df0fc000769969be83de5f4268f9e49657495b93f417d9b714393c08aeb38eb61925&token=498825246&lang=zh_CN&scene=21#wechat_redirect

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