目录
- 简介
- 数据整理
- 探索性数据分析
- 结论
简介
医疗永远是人们关注的问题之一,合理的医疗资源分配有助于社会稳定,国家和平。然而在日常医疗中,常常出现预约挂号后却未就诊的病人,因此研究这些浪费医疗资源的人特征,对医疗资源的合理分配有重要的意义。
本次采用的数据集是kaggle上的Medical Appointment No Shows数据集,包含10万条巴西预约挂号的求诊信息,其中每行数据录入了有关患者特点的多个数值。本次分析主要研究以下问题:
- 就诊时间与预定时间越短,如约就诊的可能性就越高么?
- 不同性别、年龄的患者按照其挂号预约前往医院就诊的情况是否不同?
- 哪些种类的患者经常去看医生?他们的年龄、性别分布情况各自如何?他们按照其挂号预约前往医院就诊的情况是怎样的?
- 福利项目是否有助于患者按预约就诊?
- 是否接收到短信提醒对按照其挂号预约前往医院就诊的情况有影响吗?
(数据下载地址:https://www.kaggle.com/joniarroba/noshowappointments/data)
#导入所用的包
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
数据整理
常规属性
#加载数据
df = pd.read_csv(r'D:\python35\Udacity\未前往就诊挂号预约\noshowappointments-kagglev2-may-2016.csv')
#查看数据形状
df.shape
#查看数据类型,是否有缺失
df.info()
#查看冗余数据
df.duplicated().sum()
问题:
- 概览前几行数据,列标签中存在几处拼写错误:Hipertension、Handcap。另一方面,为方便起见,可使用下划线替代No-show的‘-’。
- 字段ScheduledDay、AppointmentDay是时间类数据,为方便处理,需转换为datetime,然后计算waitingdays的时间,再划分合理时间段。另一方面,为了年龄与其他影响因素的关系,这里将年龄划分为年龄段Generation。
数据清洗
在上面数据评估之中,我们需要做的是转换列标签,时间化相关数据、划分合理的等待时间段和年龄段。
#转换列标签
df.rename(columns={'No-show':'NoShow',
'Hipertension':'Hypertension',
'Handcap':'Handicap'},inplace=True)
#时间化相关数据
df['AppointmentDay']=pd.to_datetime(df['AppointmentDay'])
df['ScheduledDay']= pd.to_datetime(df['ScheduledDay'])
df['WaitingDays']= df['AppointmentDay']-df['ScheduledDay']
# watingdays取天数,当天为-1,其他为异常值
df['WaitingDays']= df['WaitingDays'].apply(lambda x: x.days)
#清除异常值
df = df[df['WaitingDays']>=-1]
#段化时间
time_labels = ['immediately','OneDay','OneWeek','TwoWeek','OneMonth']
time_cut = [-2,0,6,13,29,999]
df['WaitingDays'] = pd.cut(df['WaitingDays'], time_cut, labels=time_labels)
#清除年龄异常值
df = df[df['Age']>=0]
#段化年龄
age_labels=['0~10','10~20','20~30','30~40','40~50','50~60','60~70','70+']
age_cut = [-1,10,20,30,40,50,60,70,999]
df['Generation'] = pd.cut(df['Age'], age_cut, labels=age_labels)
探索性数据分析
1. 就诊时间与预定时间越短,如约就诊的可能性就越高么?
# 计算不同等待时间段的如约就诊的概率值
probability = df.query('NoShow == "Yes"')['WaitingDays'].value_counts(sort=False)/df['WaitingDays'].value_counts(sort=False)
probability.name = 'Probability'
#设置背景参数、图片参数
plt.figure(figsize=(6,4))
sns.set(style='dark', color_codes=True, font_scale=1.3)
ax1 = sns.countplot(x='WaitingDays', data=df, hue='NoShow')
ax2 = ax1.twinx()
ax2.set_ylim([0, 0.5])
sns.pointplot(x=probability.index, y=probability, color='r', ax=ax2)
plt.title('Probability of the different WaitingDays ');
- 立即预约就诊的患者,人数最多,也最不容易爽约。
- 在预约时间和就诊时间的时间差越小,如约就诊的概率就越高。
2. 不同性别、年龄段的患者按照其挂号预约前往医院就诊的情况是否不同?
a.数据集中性别、年龄如约就诊的总体分布情况
#单独取出'Gender','NoShow','Age'的数据
Gender_Age = df.groupby(['Gender','NoShow','Age'])['PatientId'].count()
Gender_Age.name='count'
#可视化
plt.plot(Gender_Age['F']['No'].index,Gender_Age['F']['No'].values,'b')
plt.plot(Gender_Age['M']['No'].index,Gender_Age['M']['No'].values,'r')
plt.plot(Gender_Age['F']['Yes'].index,Gender_Age['F']['Yes'].values,'y')
plt.plot(Gender_Age['M']['Yes'].index,Gender_Age['M']['Yes'].values,'g')
plt.legend(['F&No','M&No','F&Yes','M&Yes'])
plt.title("Distribution of Gender and Age")
- 低年龄段中女性患者与男性患者的人数相当。
- 中高年龄段中女性患者的人数约是男性患者的两倍。
b. 年龄段、性别与如约就诊情况的关系
sns.factorplot(x='Generation',hue='NoShow', data=df, kind='count',col='Gender',size=5);
就具体年龄段而言:
- 女性患者中50 ~ 60年龄段如约就诊的人数最多,20~30年龄段爽约人数最多。
- 男性患者0~10岁如约就诊的人数最多,爽约人数也是最多。
c. 不同年龄段、性别患者如约就诊的可能性
#取出'Gender','NoShow','Agelevels'人数统计
Gender_Age_df = df.groupby(['Gender','NoShow','Generation'])['PatientId'].count().reset_index()
#分别计算不同性别、年龄段的如约就诊的概率概率
female = Gender_Age_df.query('Gender == "F" & NoShow=="Yes"').groupby('Generation').sum()/Gender_Age_df.query('Gender == "F"').groupby('Generation').sum()
female['Gender'] = 'F'
male = Gender_Age_df.query('Gender == "M" & NoShow=="Yes"').groupby('Generation').sum()/Gender_Age_df.query('Gender == "M"').groupby('Generation').sum()
male['Gender'] = 'M'
#合并男女性别
Gender = male.append(female).reset_index()
Gender.rename(columns={'PatientId':'Probability'},inplace=True)
#作图
sns.set_style('darkgrid')
sns.pointplot(x='Generation', y='Probability', data=Gender, markers="x", hue='Gender')
plt.title('Probability of showing up with correlation of Generation and Gender',fontsize=13);
- 总体上来看,10 ~ 20和20 ~ 30年龄段的爽约的可能性最大,高龄段60 ~ 70和70+如约就诊的可能性最大
- 就年龄段的分布变化来说,爽约的男性患者的差异性大于女性患者
3. 哪些种类的患者经常去看医生?他们的年龄分布情况如何?他们按照其挂号预约前往医院就诊的情况是怎样的?
a. 不同疾病种类的患者年龄分布
sns.set_style('dark')
f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 4), sharex=True)
f.suptitle('Age distribution of different types of Diseases')
sns.distplot(df[df['Hypertension']==1]['Age'],ax=ax1)
sns.distplot(df[df['Diabetes']==1]['Age'],color='g',ax=ax2)
sns.distplot(df[df['Alcoholism']==1]['Age'],color='r',ax=ax3)
ax1.legend('Hypertension')
ax2.legend('Diabetes')
ax3.legend('Alcoholism');
- 该数据集包含Hypertension、Alcoholism、Diabetes这三类的患者
- Hypertension、Diabetes患者人群集中在50~75岁之间,而Alcoholism患者人群集中在50岁左右。
b.不同种类疾病的患者,如约就诊的概率随年龄段的变化
#提取不同年龄段,如约就诊的概率
ttt = df.query('NoShow == "Yes"').groupby('Generation').sum()/df.groupby('Generation').sum()
#作图
sns.set()
f, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6), sharex=True)
f.suptitle('Probability of showing up with correlation of Generation and Diseases')
sns.pointplot(x=ttt.index,y=ttt.Hypertension,ax=ax1)
ax1.set_xlabel('')
sns.pointplot(x=ttt.index,y=ttt.Diabetes,color='r',ax=ax2)
ax2.set_xlabel('')
sns.pointplot(x=ttt.index,y=ttt.Alcoholism,color='g',ax=ax3)
- Hypertension患者和Alcoholism患者,最容易失约的是10~20年龄段
- Diabetes患者最容易失约的是0~10年龄段
4.福利项目是否有助于患者按预约就诊?
# 定义计算失约可能性的函数
def probability(df,label):
count_df = df.groupby(['NoShow',label]).count().reset_index()
count_df= count_df.loc[:,['NoShow',label,'PatientId']]
x0 = count_df.query('NoShow == "Yes" & {} == 1'.format(label))['PatientId'].sum()
y0 = count_df.query('{} == 1'.format(label))['PatientId'].sum()
x1 = count_df.query('NoShow == "Yes" & {} == 0'.format(label))['PatientId'].sum()
y1 = count_df.query('{} == 0'.format(label))['PatientId'].sum()
pro_df = pd.DataFrame()
pro_df[label] = df[label].unique()
pro_df['Probability'] = [ x1/y1,x0/y0]
return pro_df
SP_df = probability(df,'Scholarship')
#设置背景
sns.set(style='dark')
ax1 = sns.countplot(x='Scholarship', data=df, hue='NoShow')
ax2 = ax1.twinx()
ax2.set_ylim([0, 0.5])
sns.pointplot(x='Scholarship', y='Probability', data=SP_df ,color='r',markers="*",ax=ax2)
plt.title('Probability of the different Scholoarship ');
- 福利项目的保障人群的失约率略高于非保障人群,这说明福利项目对降低失约率没有任何帮助,反而提高了失约率。
5.是否接收到短信提醒对按照其挂号预约前往医院就诊的情况有影响吗?
SMSP_df = probability(df,'SMS_received')
ax1 = sns.countplot(x='SMS_received', data=df, hue='NoShow')
ax2 = ax1.twinx()
ax2.set_ylim([0, 0.5])
sns.pointplot(x='SMS_received', y='Probability', data=SMSP_df ,color='y',markers=">",ax=ax2)
plt.title('Probability of the different SMS_received');
- 有短信提醒的患者的失约率高于没有短信提醒的患者,短信提醒对如约就诊的情况没有任何帮助
结论
- 就诊时间与预定时间越短,如约就诊的可能性就越高。
- 不同性别、年龄的患者按照其挂号预约前往医院就诊的情况不同:低年龄段更容易失约,男性患者失约的年龄段差异性大于女性患者。
- Hypertension、Alcoholism、Diabetes患者更容易看医生,Alcoholism患者年龄较Hypertension、Diabetes患者更加低龄化。
- Alcoholism患者和Hypertension患者最容易失约的是1020年龄段,Diabetes患者最容易失约的是010年龄段。
- 短信提醒、福利项目的保障有可能会提高失约率。