目录
一、数据分析流程
Python是数据分析利器,掌握了Python的编程基础后,就可以逐渐进入数据分析的奇妙世界。一个完整的数据分析项目大致可分为以下五个流程:
1. 数据获取
一般有数据分析师岗位需求的公司都会有自己的数据库,数据分析师可以通过SQL查询语句来获取数据库中想要数据。Python已经具有连接sql server、mysql、orcale等主流数据库的接口包,比如pymssql、pymysql、cx_Oracle等。
而获取外部数据主要有两种获取方式,一种是获取国内一些网站上公开的数据资料;一种是通过编写爬虫代码自动爬取数据。如果希望使用Python爬虫来获取数据,我们可以使用以下Python工具:
-
Requests-主要用于爬取数据时发出请求操作。
-
BeautifulSoup-用于爬取数据时读取XML和HTML类型的数据,解析为对象进而处理。
-
Scapy-一个处理交互式数据的包,可以解码大部分网络协议的数据包
2. 数据存储
对于数据量不大的项目,可以使用excel来进行存储和处理,但对于数据量过万的项目,使用数据库来存储与管理会更高效便捷。
3. 数据预处理
数据预处理也称数据清洗。大多数情况下,我们拿到手的数据是格式不一致,存在异常值、缺失值等问题的,而不同项目数据预处理步骤的方法也不一样。笔者认为数据分析有80%的工作都在处理数据。如果选择Python作为数据清洗的工具的话,我们可以使用Numpy和Pandas这两个工具库:
-
Numpy - 用于Python中的科学计算。它非常适用于与线性代数,傅里叶变换和随机数相关的运算。它可以很好地处理多维数据,并兼容各种数据库。
-
Pandas –Pandas是基于Numpy扩展而来的,可以提供一系列函数来处理数据结构和运算,如时间序列等。
4. 建模与分析
这一阶段首先要清楚数据的结构,结合项目需求来选取模型。
常见的数据挖掘模型有:
在这一阶段,Python也具有很好的工具库支持我们的建模工作:
-
scikit-learn-适用Python实现的机器学习算法库。scikit-learn可以实现数据预处理、分类、回归、降维、模型选择等常用的机器学习算法。
-
Tensorflow-适用于深度学习且数据处理需求不高的项目。这类项目往往数据量较大,且最终需要的精度更高。
5.可视化分析
数据分析最后一步是撰写数据分析报告,这也是数据可视化的一个过程。在数据可视化方面,Python目前主流的可视化工具有:
-
Matplotlib-主要用于二维绘图,它能让使用者很轻松地将数据图形化,并且提供多样化的输出格式。
-
Seaborn-是基于matplotlib产生的一个模块,专攻于统计可视化,可以和Pandas进行无缝链接。
按照这个流程,每个阶段所涉及的知识点可以细分如下:
从上图我们也可以得知,在整个数据分析流程,无论是数据提取、数据预处理、数据建模和分析,还是数据可视化,Python目前已经可以很好地支持我们的数据分析工作。
二、代码实现
1.数据准备
数据是存在Excel中的,可以使用pandas的Excel文件读取函数将数据读取到内存中,这里需要注意的是文件名和Excel中的sheet页的名字。读取完数据后可以对数据进行预览和查看一些基本信息。
获取数据:百度网盘 请输入提取码提取码: 6xm2
1.1导入原始数据
-
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 数据清洗
-
清洗过程包括:选择子集、列名重命名、缺失数据处理、数据类型转换、数据排序及异常值处理
-
(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()
2.构建模型及数据可视化 -
数据清洗完成后,需要利用数据构建模型(就是计算相应的业务指标),并用可视化的方式呈现结果。
2.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 业务指标2:月均消费金额
-
月均消费金额 = 总消费金额 / 月份数
-
#消费总金额 totalMoneyF = dataDF.loc[:,'实收金额'].sum() mounthMoney = totalMoneyF // mounthI print('业务指标2:月均消费金额=', mounthMoney)
2.3 客单价 -
客单价 = 总消费金额 / 总消费次数#客单价 pct = totalMoneyF / totalI print('业务指标3:客单价=', pct)
2.4 消费趋势
a. 导入python可视化相关的包
b. 分析每天的消费金额
iimport 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('按月消费金额') plt.xlabel('时间') plt.ylabel('实收金额') plt.show()
结果显示,7月消费金额最少,这是因为7月份的数据不完整,所以不具参考价值。
1月、4月、5月和6月的月消费金额差异不大.
2月和3月的消费金额迅速降低,这可能是2月和3月处于春节期间,大部分人都回家过年的原因。d. 分析药品销售情况
对“商品名称”和“销售数量”这两列数据进行聚合为Series形式,方便后面统计,并按降序排序:#聚合统计各种药品数量 medicine = groupDF[['商品名称','销售数量']] bk = medicine.groupby('商品名称')[['销售数量']] re_medicine = bk.sum() #对销售药品数量按将序排序 re_medicine = re_medicine.sort_values(by='销售数量', ascending=False) re_medicine.head()
截取销售数量最多的前十种药品,并用条形图展示结果:
top_medicine = re_medicine.iloc[:10,:] top_medicine # 数据可视化,用条形图展示前十的药品 top_medicine.plot(kind = 'bar') plt.title('销售前十的药品') plt.xlabel('药品') plt.ylabel('数量') plt.show()
结论:对于销售量排在前几位的药品,医院应该时刻关注,保证药品不会短缺而影响患者。得到销售数量最多的前十种药品的信息,这些信息也会有助于加强医院对药房的管理。
e. 每天的消费金额分布情况
每天的消费金额分布情况:一横轴为时间,纵轴为实收金额画散点图。# 每天消售金额 -- 散点图 plt.scatter(dataDF['销售时间'],dataDF['实收金额']) plt.title('每天销售金额') plt.xlabel('时间') plt.ylabel('实收金额') plt.show()
结论:从散点图可以看出,每天消费金额在500以下的占绝大多数,个别天存在消费金额很大的情况。
参考文章:https://www.jianshu.com/p/1becc1e5dbea