本篇文章以朝陽醫院2018年銷售數據爲例,目的是瞭解朝陽醫院在2018年裏的銷售狀況幾個業務指標python
數據分析的步驟:提出問題→理解數據→數據清洗→構建模型→數據可視化app
咱們知道,數據分析是指用適當的統計分析方法對收集來的大量數據進行分析,提取有用信息和造成結論而對數據加以詳細研究和歸納總結的過程。函數
那麼,與之對應的數據分析基本過程包括:獲取數據、數據清洗、構建模型、數據可視化以及消費趨勢等ui
# 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
數據概覽spa
salesDf.head()
打印結果code
購藥時間 社保卡號 商品編碼 商品名稱 銷售數量 應收金額 實收金額 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個數據, 數據中存在缺失值,能夠推斷出數據中存在一行缺失值,此外「購藥時間」和「社保卡號」這兩列都各自存在一個缺失數據。orm
在任何數據分析的操做步驟中,爲保證數據分析準確性,數據清洗步驟就顯得尤其重要。blog
數據清洗過程,或稱數據預處理,主要包括如下幾個步驟排序
在咱們獲取到的數據中,可能數據量很是龐大,並非每一列都有價值都須要分析,這時候就須要從整個數據中選取合適的子集進行分析,這樣能從數據中獲取最大價值。
在數據分析過程當中,有些列名和數據容易混淆或產生歧義,不利於數據分析,這時候須要把列名換成容易理解的名稱,能夠採用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
任何一個獲得的數據都頗有可能會有缺失值,刪除列(銷售時間,社保卡號)中爲空的行,使用dropna刪除缺失數據
print('刪除缺失值前大小',salesDf.shape) # how='any' 給定的任何一列中有缺失值就刪除 salesDf=salesDf.dropna(subset=['銷售時間','社保卡號'],how='any') print('刪除缺失後大小',salesDf.shape)
打印結果
刪除缺失值前大小 (6578, 7) 刪除缺失後大小 (6575, 7)
在導入的時候爲了防止有些數據導入不進來,因此強制全部數據都是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
使用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
查看彙總數據描述,其中銷售數量值不能小於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:月均消費次數=總消費次數 / 月份數
在計算總的消費次數當中將每一個人天天的不一樣消費記錄做爲消費一次,用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:月均消費次數=總消費次數 / 月份數
計算月均消費次數
kpi1_I=totalI // monthsI print('業務指標2:月均消費次數=',kpi1_I) # 打印結果 業務指標2:月均消費次數= 890
指標3:月均消費金額 = 總消費金額 / 月份數
#總消費金額 totalMoneyF=salesDf.loc[:,'實收金額'].sum() #月均消費金額 monthMoneyF=totalMoneyF / monthsI print('業務指標3:月均消費金額=',monthMoneyF) 業務指標3:月均消費金額= 50668.35166666666
指標4:客單價=總消費金額 / 總消費次數
客單價(per customer transaction)是指商場(超市)每個顧客平均購買商品的金額,客單價也便是平均交易金額。 ''' totalMoneyF:總消費金額 totalI:總消費次數 ''' pct=totalMoneyF / totalI print('客單價:',pct) 客單價: 56.909417821040805
#在進行操做以前,先把數據複製到另外一個數據框中,防止對以前清洗後的數據框形成影響 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)
咱們能夠發現,週五週六的銷售總額要顯著的的高於其餘日期,即週五週六應該前來買藥的人更多,銷售的藥品更多。
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一隻阿木木