數據分析:某地醫院藥品銷售業務數據分析

數據分析:某地醫院藥品銷售業務數據分析

 

本篇文章以朝陽醫院2018年銷售數據爲例,目的是瞭解朝陽醫院在2018年裏的銷售情況幾個業務指標

  • 月均消費次數
  • 月均消費金額
  • 客單價
  • 消費趨勢

數據分析的步驟:提出問題→理解數據→數據清洗→構建模型→數據可視化

一.確定業務問題

我們知道,數據分析是指用適當的統計分析方法對收集來的大量數據進行分析,提取有用信息和形成結論而對數據加以詳細研究和概括總結的過程。

那麼,與之對應的數據分析基本過程包括:獲取數據、數據清洗、構建模型、數據可視化以及消費趨勢等

二:數據概覽

# 2018年朝陽醫院數據消費金額趨勢圖
import matplotlib.pyplot as plt
from pandas import Series,DataFrame
import pandas as pd
import numpy as np

fileNameStr='F:\\Downloads\朝陽醫院2018年銷售數據.xlsx'
xls=pd.ExcelFile(fileNameStr,dtype='object')
salesDf = xls.parse('Sheet1',dtype='object')
salesDf.info()

打印結果

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6578 entries, 0 to 6577
Data columns (total 7 columns):
購藥時間    6576 non-null object
社保卡號    6576 non-null float64
商品編碼    6577 non-null float64
商品名稱    6577 non-null object
銷售數量    6577 non-null float64
應收金額    6577 non-null float64
實收金額    6577 non-null float64
dtypes: float64(5), object(2)
memory usage: 359.8+ KB

數據概覽

salesDf.head()

打印結果

    購藥時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
0	2018-01-01 星期五	001616528	236701	強力VC銀翹片	6	82.8	69
1	2018-01-02 星期六	001616528	236701	清熱解毒口服液	1	28	24.64
2	2018-01-06 星期三	0012602828	236701	感康	2	16.8	15
3	2018-01-11 星期一	0010070343428	236701	三九感冒靈	1	28	28
4	2018-01-15 星期五	00101554328	236701	三九感冒靈	8	224	208
# 行、列數
salesDf.shape
(6578, 7)

salesDf.index
RangeIndex(start=0, stop=6578, step=1)
s

salesDf.columns
Index(['購藥時間', '社保卡號', '商品編碼', '商品名稱', '銷售數量', '應收金額', '實收金額'], dtype='object')

salesDf.count()
購藥時間    6576
社保卡號    6576
商品編碼    6577
商品名稱    6577
銷售數量    6577
應收金額    6577
實收金額    6577
dtype: int64

數據缺失: 總共有6578行7列數據,但是“購藥時間”和“社保卡號”這兩列只有6576個數據,而“商品編碼”一直到“實收金額”這些列都是隻有6577個數據, 數據中存在缺失值,可以推斷出數據中存在一行缺失值,此外“購藥時間”和“社保卡號”這兩列都各自存在一個缺失數據。

在任何數據分析的操作步驟中,爲保證數據分析準確性,數據清洗步驟就顯得尤爲重要。

三.數據清洗

數據清洗過程,或稱數據預處理,主要包括以下幾個步驟

  • 選擇子集
  • 列名重命名
  • 刪除缺失數據
  • 數據類型轉換
  • 數據排序
  • 異常值處理

1選擇子集

在我們獲取到的數據中,可能數據量非常龐大,並不是每一列都有價值都需要分析,這時候就需要從整個數據中選取合適的子集進行分析,這樣能從數據中獲取最大價值。

2列名重命名

在數據分析過程中,有些列名和數據容易混淆或產生歧義,不利於數據分析,這時候需要把列名換成容易理解的名稱,可以採用rename函數實現:

salesDf.rename(columns ={'購藥時間':'銷售時間'},inplace=True)  #inplace=True,數據框本身會改動
salesDf.head()

打印結果

    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
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 缺失數據處理

任何一個得到的數據都很有可能會有缺失值,刪除列(銷售時間,社保卡號)中爲空的行,使用dropna刪除缺失數據

print('刪除缺失值前大小',salesDf.shape)

# how='any' 給定的任何一列中有缺失值就刪除
salesDf=salesDf.dropna(subset=['銷售時間','社保卡號'],how='any')
print('刪除缺失後大小',salesDf.shape)

打印結果

刪除缺失值前大小 (6578, 7)
刪除缺失後大小 (6575, 7)

4 數據類型處理

在導入的時候爲了防止有些數據導入不進來,所以強制所有數據都是object類型,但在實際分析上這樣是不可能的。

通過觀察,銷售數量,應收金額,實收金額,應該改成float類型,銷售時間應該清理後改成時間類型,對於改變成float類型的幾列,使用astype函數,代碼如下。

salesDf['銷售數量']=salesDf['銷售數量'].astype('float')
salesDf['應收金額']=salesDf['應收金額'].astype('float')
salesDf['實收金額']=salesDf['實收金額'].astype('float')
print('轉換後的數據類型:\n',salesDf.dtypes)

打印結果

`轉換後的數據類型:
     銷售時間     object
    社保卡號     object
    商品編碼     object
    商品名稱     object
    銷售數量    float64
    應收金額    float64
    實收金額    float64
    dtype: object
    

而銷售時間那一列,則需要進行處理後才能轉換爲時間類型,把銷售時間的日期和星期分開

分割時間列,定義函數:分割銷售日期,獲取銷售日期

def splitSaletime(timeColSer):
    timeList=[]
    for value in timeColSer:
        #例如2018-01-01 星期五,分割後爲:2018-01-01
        dateStr=value.split(' ')[0]
        timeList.append(dateStr)
    
    timeSer=pd.Series(timeList)
    return timeSer

獲取“銷售時間”這一列,對字符串進行分割,獲取銷售日期


timeSer=salesDf.loc[:,'銷售時間']

dateSer=splitSaletime(timeSer)

修改銷售時間這一列的值

打印結果

dateSer[0:3]

    0    2018-01-01
    1    2018-01-02
    2    2018-01-06
    dtype: object

獲取分割之後的銷售日期,少了星期時間字符

salesDf.loc[:,'銷售時間']=dateSer

salesDf.head()

打印結果

    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
0	2018-01-01	001616528	236701	強力VC銀翹片	6.0	82.8	69.00
1	2018-01-02	001616528	236701	清熱解毒口服液	1.0	28.0	24.64
2	2018-01-06	0012602828	236701	感康	2.0	16.8	15.00
3	2018-01-11	0010070343428	236701	三九感冒靈	1.0	28.0	28.00
4	2018-01-15	00101554328	236701	三九感冒靈	8.0	224.0	208.00

5 數據排序

使用sort_values進行排序,by:按哪幾列排序,ascending=True 表示升序排列,ascending=False表示降序排列

#按銷售時間進行升序排列
salesDf=salesDf.sort_values(by='銷售時間',ascending=True)

#查看排序後的前10行
salesDf.head(10)

打印結果

    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
0	2018-01-01	001616528	236701	強力VC銀翹片	6.0	82.8	69.0
3436	2018-01-01	0010616728	865099	硝苯地平片(心痛定)	2.0	3.4	3.0
1190	2018-01-01	0010073966328	861409	非洛地平緩釋片(波依定)	5.0	162.5	145.0
3859	2018-01-01	0010073966328	866634	硝苯地平控釋片(欣然)	6.0	111.0	92.5
3888	2018-01-01	0010014289328	866851	纈沙坦分散片(易達樂)	1.0	26.0	23.0
894	2018-01-01	0013331728	861405	苯磺酸氨氯地平片(絡活喜)	2.0	69.0	62.0
893	2018-01-01	0011743428	861405	苯磺酸氨氯地平片(絡活喜)	1.0	34.5	31.0
4368	2018-01-01	00103283128	870921	卡託普利片	1.0	2.4	2.2
4562	2018-01-01	0010074599128	874684	厄貝沙坦氫氯噻嗪片(依倫平)	5.0	118.0	118.0
5039	2018-01-01	0010017493928	868042	馬來酸左旋氨氯地平片(玄寧)	1.0	46.0	46.0

重命名行名(index),使用reset_index修改成從0到N按順序排序的索引值index

salesDf=salesDf.reset_index(drop=True)

查看數據 salesDf.head(6)

    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
0	2018-01-01	001616528	236701	強力VC銀翹片	6.0	82.8	69.0
1	2018-01-01	0010616728	865099	硝苯地平片(心痛定)	2.0	3.4	3.0
2	2018-01-01	0010073966328	861409	非洛地平緩釋片(波依定)	5.0	162.5	145.0
3	2018-01-01	0010073966328	866634	硝苯地平控釋片(欣然)	6.0	111.0	92.5
4	2018-01-01	0010014289328	866851	纈沙坦分散片(易達樂)	1.0	26.0	23.0
5	2018-01-01	0013331728	861405	苯磺酸氨氯地平片(絡活喜)	2.0	69.0	62.0

6 異常值處理

查看彙總數據描述,其中銷售數量值不能小於0

salesDf.describe()

打印結果

    銷售數量	應收金額	實收金額
count	6549.000000	6549.000000	6549.000000
mean	2.384486	50.449076	46.284370
std	2.375227	87.696401	81.058426
min	-10.000000	-374.000000	-374.000000
25%	1.000000	14.000000	12.320000
50%	2.000000	28.000000	26.500000
75%	2.000000	59.600000	53.000000
max	50.000000	2950.000000	2650.000000

通過條件判斷來刪除異常值

querySer=salesDf.loc[:,'銷售數量']>0

print('刪除異常值前:',salesDf.shape)
salesDf=salesDf.loc[querySer,:]
print('刪除異常值後:',salesDf.shape)

# 打印結果
刪除異常值前: (6549, 7)
刪除異常值後: (6506, 7)

數據的預處理工作完成,接下來分析業務的各個指標

四 構建數據模型

1.月份數

業務指標1:月均消費次數=總消費次數 / 月份數

在計算總的消費次數當中將每個人每天的不同消費記錄作爲消費一次,用drop_duplicates去掉同一天同一個人的重複消費記錄

根據列名(銷售時間,社區卡號),如果這兩個列值同時相同,只保留1條,將重複的數據刪除


kpi1_Df=salesDf.drop_duplicates(subset=['銷售時間', '社保卡號'])

#總消費次數
totalI=kpi1_Df.shape[0]

print('總消費次數=',totalI)
# 打印結果:總消費次數= 5342

# 計算月份數

#按銷售時間升序排序
kpi1_Df=kpi1_Df.sort_values(by='銷售時間',ascending=True)

#重命名行名,索引排序
kpi1_Df=kpi1_Df.reset_index(drop=True)

kpi1_Df.head()

打印結果

    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
0	2018-01-01	001616528	236701	強力VC銀翹片	6.0	82.8	69.0
1	2018-01-01	0012697828	861464	複方利血平片(複方降壓片)	4.0	10.0	9.4
2	2018-01-01	0010060654328	861458	複方利血平氨苯蝶啶片(北京降壓0號)	1.0	10.3	9.2
3	2018-01-01	0011811728	861456	酒石酸美託洛爾片(倍他樂克)	1.0	7.0	6.3
4	2018-01-01	0013448228	861507	苯磺酸氨氯地平片(安內真)	1.0	9.5	8.5

計算總月份數,第一行時間與結尾時間之差除以30取整

startTime=kpi1_Df.loc[0,'銷售時間']
#最大時間值
endTime=kpi1_Df.loc[totalI-1,'銷售時間']


#天數
daysI=(endTime-startTime).days
#月份數: 運算符“//”表示取整除 
#返回商的整數部分,例如9//2 輸出結果是4
monthsI=daysI//30
print('月份數:',monthsI)

月份數: 6

2.月均消費次數

業務指標2:月均消費次數=總消費次數 / 月份數

計算月均消費次數

kpi1_I=totalI // monthsI
print('業務指標2:月均消費次數=',kpi1_I)

# 打印結果
業務指標2:月均消費次數= 890

3.月均消費金額

指標3:月均消費金額 = 總消費金額 / 月份數

#總消費金額
totalMoneyF=salesDf.loc[:,'實收金額'].sum()
#月均消費金額
monthMoneyF=totalMoneyF / monthsI
print('業務指標3:月均消費金額=',monthMoneyF)

業務指標3:月均消費金額= 50668.35166666666

4.客單價

指標4:客單價=總消費金額 / 總消費次數

客單價(per customer transaction)是指商場(超市)每一個顧客平均購買商品的金額,客單價也即是平均交易金額。

'''
totalMoneyF:總消費金額
totalI:總消費次數
'''
pct=totalMoneyF / totalI
print('客單價:',pct)
客單價: 56.909417821040805

5.消費趨勢圖

#在進行操作之前,先把數據複製到另一個數據框中,防止對之前清洗後的數據框造成影響
groupDf=salesDf
#第1步:重命名行名(index)爲銷售時間所在列的值
groupDf.index=groupDf['銷售時間']
groupDf.head()

打印結果


    銷售時間	社保卡號	商品編碼	商品名稱	銷售數量	應收金額	實收金額
銷售時間							
2018-01-01	2018-01-01	001616528	236701	強力VC銀翹片	6.0	82.8	69.0
2018-01-01	2018-01-01	0010616728	865099	硝苯地平片(心痛定)	2.0	3.4	3.0
2018-01-01	2018-01-01	0010073966328	861409	非洛地平緩釋片(波依定)	5.0	162.5	145.0
2018-01-01	2018-01-01	0010073966328	866634	硝苯地平控釋片(欣然)	6.0	111.0	92.5
2018-01-01	2018-01-01	0010014289328	866851	纈沙坦分散片(易達樂)	1.0	26.0	23.0

分組

gb=groupDf.groupby(groupDf.index.month)
gb
# 打印結果
<pandas.core.groupby.DataFrameGroupBy object at 0x000000000ED4CC18>
#第3步:應用函數,計算每個月的消費總額
mounthDf=gb.sum()
mounthDf

打印結果

	銷售數量	應收金額	實收金額
銷售時間			
1	2527.0	53561.6	49461.19
2	1858.0	42028.8	38790.38
3	2225.0	45318.0	41597.51
4	3005.0	54296.3	48787.84
5	2225.0	51263.4	46925.27
6	2328.0	52300.8	48327.70
7	1483.0	32568.0	30120.22

選取每個月的應收金額和實收金額的消費總額

mounthDf=DataFrame(mounthDf,columns=['應收金額','實收金額'])
mounthDf

打印結果

	應收金額	實收金額
銷售時間		
1	53561.6	49461.19
2	42028.8	38790.38
3	45318.0	41597.51
4	54296.3	48787.84
5	51263.4	46925.27
6	52300.8	48327.70
7	32568.0	30120.22

五 數據可視化

from pylab import *  
mpl.rcParams['font.sans-serif'] = ['SimHei']  #防止中文亂碼

mounthDf.plot(title='2018年朝陽醫院數據消費金額趨勢圖',figsize=(15,8),fontsize=20)

image

我們可以發現,週五週六的銷售總額要顯著的的高於其他日期,即週五週六應該前來買藥的人更多,銷售的藥品更多。

1月和第七月消費總金額是最高的,在第七月消費金額最低。

醫藥銷售量和天氣變化有一定的影響,尤其在冬季天氣寒冷和初春季節,容易受到天氣影響,氣溫變化大,市民容易感冒,從而在醫藥行業銷售更多了醫藥,銷售量上升,在氣溫平穩時期銷售量下降。

醫藥銷售金額會受到節日、天氣、重大活動等因素的影響。

 

六 數據建模



# 2018年朝陽醫院數據消費金額趨勢圖
import matplotlib.pyplot as plt
from pandas import Series,DataFrame
import pandas as pd
import numpy as np
from pylab import *  
mpl.rcParams['font.sans-serif'] = ['SimHei']  #防止中文亂碼

fileNameStr='F:\\Downloads\朝陽醫院2018年銷售數據.xlsx'
xls=pd.ExcelFile(fileNameStr,dtype='object')
salesDf = xls.parse('Sheet1',dtype='object')
    

def splitSaletime(timeColSer):
    timeList=[]
    for value in timeColSer:
        #例如2018-01-01 星期五,分割後爲:2018-01-01
        dateStr=value.split(' ')[0]
        timeList.append(dateStr)
    
    timeSer=pd.Series(timeList)
    return timeSer


salesDf.rename(columns ={'購藥時間':'銷售時間'},inplace=True)  #inplace=True,數據框本身會改動

#how='any' 給定的任何一列中有缺失值就刪除
salesDf=salesDf.dropna(subset=['銷售時間','社保卡號'],how='any')

salesDf['銷售數量']=salesDf['銷售數量'].astype('float')
salesDf['應收金額']=salesDf['應收金額'].astype('float')
salesDf['實收金額']=salesDf['實收金額'].astype('float')


timeSer=salesDf.loc[:,'銷售時間']

dateSer=splitSaletime(timeSer)

salesDf.loc[:,'銷售時間']=dateSer


salesDf.loc[:,'銷售時間']=pd.to_datetime(salesDf.loc[:,'銷售時間'],
                                    format='%Y-%m-%d', errors='coerce')
salesDf=salesDf.dropna(subset=['銷售時間','社保卡號'],how='any')

#按銷售時間進行升序排列
salesDf=salesDf.sort_values(by='銷售時間',ascending=True)

salesDf=salesDf.reset_index(drop=True)

# 刪除異常值
querySer=salesDf.loc[:,'銷售數量']>0

salesDf=salesDf.loc[querySer,:]

kpi1_Df=salesDf.drop_duplicates(subset=['銷售時間', '社保卡號'])

#總消費次數
totalI=kpi1_Df.shape[0]


#按銷售時間升序排序
kpi1_Df=kpi1_Df.sort_values(by='銷售時間',ascending=True)

#重命名行名,索引排序
kpi1_Df=kpi1_Df.reset_index(drop=True)

startTime=kpi1_Df.loc[0,'銷售時間']
#最大時間值
endTime=kpi1_Df.loc[totalI-1,'銷售時間']


#天數
daysI=(endTime-startTime).days
#月份數: 運算符“//”表示取整除 
#返回商的整數部分,例如9//2 輸出結果是4
monthsI=daysI//30

# 業務指標2:月均消費次數
kpi1_I=totalI // monthsI

#總消費金額
totalMoneyF=salesDf.loc[:,'實收金額'].sum()
#業務指標3:月均消費金額
monthMoneyF=totalMoneyF / monthsI

'''
totalMoneyF:總消費金額
totalI:總消費次數
'''
pct=totalMoneyF / totalI

#在進行操作之前,先把數據複製到另一個數據框中,防止對之前清洗後的數據框造成影響
groupDf=salesDf
#第1步:重命名行名(index)爲銷售時間所在列的值
groupDf.index=groupDf['銷售時間']

gb=groupDf.groupby(groupDf.index.month)

#第3步:應用函數,計算每個月的消費總額
mounthDf=gb.sum()

mounthDf=DataFrame(mounthDf,columns=['應收金額','實收金額'])

mounthDf.plot(title='2018年朝陽醫院數據消費金額趨勢圖',figsize=(15,8),fontsize=20)

 

 

b一隻阿木木

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章