梳理思路
观察数据集
初步观察数据集,发现指标如下:“婚姻状况 申请方式 申请顺序 课程 日间/晚间出勤 以前的学历 国籍 母亲的学历 父亲的学历 母亲的职业 父亲的职业 是否流离失所 教育方面的特殊需要 是否为债务人 学费是否过期 性别 是否为奖学金持有者 入学时的年龄 国际学生 已记入的第1学期课程数 已注册的第1学期的课程数 第1学期的课程数(评价) 第1学期的课程数(批准) 第1学期课程单位(等级) 第1学期的课程数(无评价) 第2学期的课程数(学分制) 第2学期的课程数(已注册) 第2学期的课程数(评估) 第2学期的课程数(批准) 第2学期的课程数(成绩) 第2学期的课程数(无评价) 该地区失业率 该地区通货膨胀率 该地区国内生产总值 学业状态”,包含的特征由小及大,囊括了人口统计数据,社会经济因素,学业成绩信息。
以上的变量有分类变量、数字变量和有序变量,因此,将项目分为以下几个角度进行分析:
- 描述性统计
- 相关性分析
- 算法预测
特征浏览与分类
不难看出,数据特征还是比较多的,故我们可以将数据特征进行分类:个人基本信息、学业基本信息、课程信息、家庭背景信息、地区经济信息,具体分类方法如下:
根据以上的分类,我们可以将学业状态,是否获得奖学金,学业成绩作为标签,进行相关性分析和预测。同一地区失业率、通货膨胀、国内生产总值(GDP)相同,因此可以得到学生所处地区的经济和发展情况,具有一定的现实意义。因此,我们可以先设想以下几个问题去进行分析:
- 识别学生辍学的风险因素:提前去进行指导教育,降低辍学率
- 发掘影响成绩的因素:对症下药,提高学习成绩
- 不同地区社会因素对居民的影响:帮助社会机构指定帮助读书、就业的具体措施
准备工作与数据预处理
1、数据查看
#导入所需要的库
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
from pyecharts.charts import *
from pyecharts import options as opts
#导入数据
df=pd.read_csv('dataset.csv')
#对数据进行去重
print('去重前',df.shape[0],'行数据')
print('去重后',df.drop_duplicates().shape[0],'行数据')
>>>
>去重前 4424 行数据
>去重后 4424 行数据
#去重前与去重后数据量保持一致,说明无重复记录,可以直接使用数据
简单看看前5行,了解基本构成。
df.head()
查看各个特征的类型以及检查是否存在缺失值。
df.info()
#一共有34个特征。
#这里我们查看空值的存在情况发现‘Non-Null Count’均为4424,等于数据行数,不存在空值无需进行填充操作。
2、特征处理
(1)学业状态映射
这里主要做的是特征映射和特征提取,我们希望:
- 将学业状态由文字转为数字
- 获取到学生所属的地区
#查看学生状态这一列包含的学生状态类型
df['学业状态'].unique()
>>>
>array(['辍学', '毕业', '已入学'], dtype=object)
#对每一状态类型进行计数
df['学业状态'].value_counts()
>>>
>毕业 2209
辍学 1421
已入学 794
Name: 学业状态, dtype: int64
#按照学业完成度,进行如下映射:0表示“辍学”,1表示“已入学”,2表示毕业
df['学业状态_num']=df['学业状态'].map({'辍学':0,'已入学':1,'毕业':2})
>>>
>2 2209
0 1421
1 794
Name: 学业状态_num, dtype: int64
#可以看到前后处理的数量保持一致
(3)所在地区提取
下面开始获取区域并进行映射,现货区所有地区不重复的信息。
df[['该地区失业率','该地区通货膨胀率','该地区国内生产总值']].drop_duplicates().sort_values(by='该地区国内生产总值')
#按照国内生产总值进行升序排序
可以看到一共有10个地方,且“该地区国内生产总值”没有重复,因此,从低到高我们映射为“0-9”地区编号
#映射0-9地区编号
df['该地区国内生产总值'].value_counts()
>>>
> 0.32 571
-3.12 533
1.74 525
1.79 445
-1.70 419
2.02 414
-4.06 397
0.79 390
3.51 368
-0.92 362
Name: 该地区国内生产总值, dtype: int64
df['local']=df['该地区国内生产总值'].map({-4.06:0,-3.12:1,-1.70:2,-0.92:3,0.32:4,0.79:5,1.74:6,1.79:7,2.02:8,3.51:9})
df['local'].value_counts()
>>>
>4 571
1 533
6 525
7 445
2 419
8 414
0 397
5 390
9 368
3 362
Name: local, dtype: int64
经过处理看到,我们获取到了不同地区的编码,且前后数量保持一致,说明所做工作无误。
EDA:透过画像掌握信息
1、目标学生基本信息调研(国籍+性别+婚姻+流浪+债务)
制作学生国籍分布图
guoji = df[['性别','国籍']].groupby(['国籍']).count().sort_values(by=['性别'],ascending=False)
x1 = list(guoji.index)
y1 = guoji['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1000px',height='400px',theme='macarons'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生国籍分布图",pos_left='40%'))
chart0.render_notebook()
df['国际学生'].value_counts()
>>>
>0 4314
1 110
Name: 国际学生, dtype: int64
4314/4424
>>>
>0.9751356238698011
可以看到学生国籍呈现数据和国际学生"0"数量与国籍为“1”的数量对应起来。
- 说明超过97%的学生为本国学生,但同时也能吸引外国学生前来学习。
df['性别'].value_counts()
>>>
>0 2868
1 1556
Name: 性别, dtype: int64
df['婚姻状况'].value_counts()
>>>
>1 3919
2 379
4 91
5 25
6 6
3 4
Name: 婚姻状况, dtype: int64
#饼图
df_pair_sex=[['0',2826],['1',1556]]
df_pair_marry=[['1',3919],['2',379],['4',91],['5',25],['6',6],['3',4]]
char1=Pie(init_opts=opts.InitOpts(width='1000px',height='300px',theme='macarons'))
char1.add('学生性别构成',df_pair_sex,radius=['40%','70%'],rosetype='radius',center=['25%','50%'],label_opts=opts.LabelOpts(is_show=True,formatter='性别{b}:{c}人,占{d}%'))
char1.add('学生婚姻情况构成',df_pair_sex,radius=['40%','70%'],rosetype='radius',center=['25%','50%'],label_opts=opts.LabelOpts(is_show=True,formatter='婚姻状况{b}:{c}人,占{d}%'))
chart1.set_global_opts(legend_opts=opts.LegendOpts(is_show=False,pos_right='0%',orient='vertical'),title_opts=opts.TitleOpts(title="学生性别分布 学生婚姻情况构成",pos_left='22%'))
chart1.render_notebook() #显示
根据上图,可以看到:
- 性别占比不是很均匀,性别为“0”的学生较多
- 一共有6种婚姻状况,其中“1”占比比较高。
df['是否流离失所'].value_counts()
>>>
>1 2426
0 1998
Name: 是否流离失所, dtype: int64
df['是否为债务人'].value_counts()
>>>
>0 3921
1 503
Name: 是否为债务人, dtype: int64
#饼图
df_pair_home = [['0', 2426], ['1', 1998]]
df_pair_debt = [['2',3921],['3',503]]
chart2 = Pie(init_opts=opts.InitOpts(width='900px',height='300px',theme='macarons' ))
chart2.add('学生是否流离失所', df_pair_home,radius=['40%', '70%'],rosetype='radius',center=['30%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人,占{d}%'))
chart2.add('学生是否为债务人', df_pair_debt,radius=['40%', '70%'],center=['75%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人,占{d}%'))
chart2.set_global_opts(legend_opts=opts.LegendOpts(is_show=False,pos_right='0%',orient='vertical'),title_opts=opts.TitleOpts(title="学生是否流离失所 学生是否为债务人",pos_left='20%'))
chart2.render_notebook() #显示
从上图可以看出,有接近一半的学生流离失所,有10%多的学生背负着债务。这些因素必然会影响学生的学业表现。
2、学业相关信息调研(申请+入学年龄+出勤+学历+奖学金+学费有效期+特殊教育)
fangshi = df[['性别','申请方式']].groupby(['申请方式']).count().sort_values(by=['性别'],ascending=False)
x1 = list(fangshi.index)
y1 = fangshi['性别'].tolist()
chart001 = Bar(init_opts=opts.InitOpts(width='700px',height='400px'))
chart001.add_xaxis(x1)
chart001.add_yaxis('申请方式', y1,color='#5AB1EF',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart001.set_global_opts(legend_opts=opts.LegendOpts(is_show=False),title_opts=opts.TitleOpts(title="学生申请方式与申请顺序分布图",pos_left='40%'))
shunxu = df[['性别','申请顺序']].groupby(['申请顺序']).count().sort_values(by=['性别'],ascending=False)
x1 = list(shunxu.index)
y1 = shunxu['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='500px',height='400px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('申请顺序', y1,color='#D87A80',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(is_show = False))
grid = Grid(init_opts=opts.InitOpts(width='1100px',height='400px',theme = 'macarons')) # 定义组合画布
grid.add(chart001,grid_opts = opts.GridOpts(pos_top = "10%",pos_bottom="10%", pos_left="5%",pos_right="40%")) # 增加子图
grid.add(chart0,grid_opts = opts.GridOpts(pos_top = "10%",pos_bottom="10%", pos_left="65%",pos_right="5%")) # 增加子图
grid.render_notebook()
xueli = df[['性别','以前的学历']].groupby(['以前的学历']).count().sort_values(by=['性别'],ascending=False)
x1 = list(xueli.index)
y1 = xueli['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='700px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#FFB980',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生以前的学历分布图",pos_left='40%'))
chart0.render_notebook()
np.min(df['入学时的年龄']), np.max(df['入学时的年龄'])
>>>
>(17, 70)
age = df[['性别','入学时的年龄']].groupby(['入学时的年龄']).count()
x1 = list(age.index)
y1 = age['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1800px',height='400px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#B6A2DE',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生入学时的年龄分布图",pos_left='40%'))
chart0.render_notebook()
从上表可以看出:
- 学生年龄大多数集中在18-21岁之间,最小为17岁入学,最大的学生有70岁
- 结合学历分布,我们可以推测出“1”学历为高中学历
df['日间/晚间出勤'].value_counts()
>>>
>1 3941
0 483
Name: 日间/晚间出勤, dtype: int64
df['学费是否过期'].value_counts()
>>>
>1 3896
0 528
Name: 学费是否过期, dtype: int64
df['教育方面的特殊需要'].value_counts()
>>>
>0 4373
1 51
Name: 教育方面的特殊需要, dtype: int64
#饼图
df_pair_cq = [['日', 3941], ['晚', 483]]
df_pair_gq = [['未过期',3896],['已过期',528]]
df_pair_ts = [['无',4373],['有',51]]
chart2 = Pie(init_opts=opts.InitOpts(width='1000px',height='300px',theme='macarons' ))
chart2.add('学生日间/晚间出勤', df_pair_cq,radius=['20%', '50%'],center=['25%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人\n占{d}%'))
chart2.add('学费是否过期', df_pair_gq,radius=['20%', '50%'],center=['50%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人\n占{d}%'))
chart2.add('教育方面的特殊需要', df_pair_ts,radius=['20%', '50%'],center=['75%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人\n占{d}%'))
chart2.set_global_opts(legend_opts=opts.LegendOpts(is_show=False,pos_right='0%',orient='vertical'),title_opts=opts.TitleOpts(title="学生日间/晚间出勤 学费是否过期 教育方面的特殊需要",pos_left='18%'))
chart2.render_notebook() #显示
通过上表,我们可以推测:
- 10.92%的人选择晚上上课,推测可能为在职读大学,错开工作时间
- 有10%多的学生学费过期,这与债务人占比11.7%对应起来,猜测他们因没钱支付学费导致过期
- 有51人有特殊教育需要
df['是否为奖学金持有者'].value_counts()
>>>
>0 3325
1 1099
Name: 是否为奖学金持有者, dtype: int64
df_pair_jxj = [['否',3325],['是',1099]]
chart2 = Pie(init_opts=opts.InitOpts(width='800px',height='300px',theme='macarons' ))
chart2.add('是否为奖学金持有者', df_pair_jxj,radius=['20%', '50%'],center=['50%', '50%'],label_opts=opts.LabelOpts(is_show=True ,formatter = ' {b}:{c}人\n占{d}%'))
chart2.set_global_opts(legend_opts=opts.LegendOpts(is_show=False,pos_right='0%',orient='vertical'),title_opts=opts.TitleOpts(title="学生是否为奖学金持有者",pos_left='38%'))
chart2.render_notebook() #显示
3、课程信息调研
观察原数据集,我们可以发现,每学期课程都有6种相关变量,我们需要挖掘一下其中的关系,先提取10行数据观察看看。
df[['已记入的第1学期课程数','已注册的第1学期的课程数','第1学期的课程数(评价)','第1学期的课程数(批准)','第1学期课程单位(等级)','第1学期的课程数(无评价)']].head(10)
我们进行如下假设:
- ‘第1学期的课程数(评价)’数量>=‘已注册的第1学期的课程数’数量
- ‘已注册的第1学期的课程数’数量>=‘第1学期的课程数(批准)’数量
- ‘第1学期的课程数(评价)数量’>=‘第1学期的课程数(批准)’数量
进行验证:
np.sum(df['第1学期的课程数(评价)']>= df['已注册的第1学期的课程数'])
>>>
>4255
np.sum(df['已注册的第1学期的课程数']>=df['第1学期的课程数(批准)'])
>>>
>4424
np.sum(df['第1学期的课程数(评价)']>=df['第1学期的课程数(批准)'])
>>>
>4424
可以看出假设2,3成立,故联系实际做出以下推测:
- 已注册课程代表在系统里抢了这门课(但是不一定会有评价)
- 评价可能包括旁听,所以数量大于批准
- 批准课程才会出成绩
综上,我们选择“第1学期的课程数(批准)”,“第1学期课程单位(等级)”作为值的进一步分析的对象。
对于第二学期同理。
kcs1 = df[['性别','第1学期的课程数(批准)']].groupby(['第1学期的课程数(批准)']).count().sort_values(by=['性别'],ascending=False)
x1 = list(kcs1.index)
y1 = kcs1['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1000px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#5AB1EF',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生第1学期的课程数(批准)分布图",pos_left='30%'))
chart0.render_notebook()
kcs2 = df[['性别','第2学期的课程数(批准)']].groupby(['第2学期的课程数(批准)']).count().sort_values(by=['性别'],ascending=False)
x2 = list(kcs2.index)
y2 = kcs2['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1000px',height='300px'))
chart0.add_xaxis(x2)
chart0.add_yaxis('', y2,color='#5AB2EF',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生第2学期的课程数(批准)分布图",pos_left='30%'))
chart0.render_notebook()
两学期呈现出较为一致的特点:
- 多数学生批准课程数为5-6节课
- 因为辍学,很多学生批准课数为0
- 批准选课3-4,7-8节的学生也不少
np.min(df['第1学期课程单位(等级)']),np.max(df['第1学期课程单位(等级)'])
>>>
>(0.0, 18.875)
df['grade1'] = round(df['第1学期课程单位(等级)'],1)
df_gr1 = df[(df['第1学期课程单位(等级)']>0 )&(df['第1学期课程单位(等级)']<19 )]
plt.hist(df_gr1['grade1'],bins=15)
plt.show()
df['grade2'] = round(df['第2学期的课程数(成绩)'],1)
df_gr2 = df[(df['第2学期的课程数(成绩)']>0 )&(df['第2学期的课程数(成绩)']<19 )]
plt.hist(df_gr2['grade2'],bins=15)
plt.show()
可以看到成绩集中在11-14分之间,整体偏右分布。
3、家庭背景信息调研
fqxl = df[['性别','父亲的学历']].groupby(['父亲的学历']).count().sort_values(by=['性别'],ascending=False)
x1 = list(fqxl.index)
y1 = fqxl['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1000px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#5AB1EF',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生父亲的学历分布图",pos_left='30%'))
chart0.render_notebook()
mqxl = df[['性别','母亲的学历']].groupby(['母亲的学历']).count().sort_values(by=['性别'],ascending=False)
x1 = list(mqxl.index)
y1 = mqxl['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1000px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#D87A80',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生母亲的学历分布图",pos_left='40%'))
chart0.render_notebook()
结合学生入学学历来看:“1”代表高中,因此母亲的学历中。高中学历最多
fqxl = df[['性别','父亲的职业']].groupby(['父亲的职业']).count().sort_values(by=['性别'],ascending=False)
x1 = list(fqxl.index)
y1 = fqxl['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1400px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#5AB1EF',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生父亲的职业分布图",pos_left='30%'))
chart0.render_notebook()
mqxl = df[['性别','母亲的职业']].groupby(['母亲的职业']).count().sort_values(by=['性别'],ascending=False)
x1 = list(mqxl.index)
y1 = mqxl['性别'].tolist()
chart0 = Bar(init_opts=opts.InitOpts(width='1100px',height='300px'))
chart0.add_xaxis(x1)
chart0.add_yaxis('', y1,color='#D87A80',itemstyle_opts={'barBorderRadius':[60, 60, 20, 20]})
chart0.set_global_opts(legend_opts=opts.LegendOpts(pos_right='10%',pos_top='2%'),title_opts=opts.TitleOpts(title="学生母亲的职业分布图",pos_left='40%'))
chart0.render_notebook()
可以看出男性从事的职业更多元化一些,其中家长们的职业主要集中在“10”。
由于不知道职业的映射标准,这里无法进行进一步分析。
相关性分析:查找数据背后的联系
由于数据特征较多,整体做相关性表呈现效果可能不够直观,因此我们选择感兴趣的几个方面,进行“逐个击破”。我们也能删除一些类似“父母的职业”,这种不了解映射规则的变量,提高效率。
1、影响学生辍学的因素分析
我们关注到基本信息因素、学业背景因素对是否辍学产生的影响。
(1)基本信息因素对辍学的影响
df_0 = df[['婚姻状况','国际学生','性别','是否为债务人','是否流离失所','学业状态_num']]
df_c0 = df_0.corr(method='spearman')
df_c0
从基本信息来看
- 婚姻状况、性别、是否为债务人产生较大的负相关,即婚姻状况非未婚、性别为1、债务为“3”的人越容易辍学
- 流离失所状态为“1”的人似乎不容易辍学
(2)学业背景因素对辍学的影响
df_0 = df[['日间/晚间出勤','教育方面的特殊需要','学费是否过期','是否为奖学金持有者','入学时的年龄','学业状态_num']]
df_c0 = df_0.corr(method='spearman')
df_c0
从学业背景信息来看:
- 学费过期与获奖学金与学业状态拥有较大的正相关,即没有货的奖学金、学费过期越容易辍学
- 入学年龄拥有较大的负相关,即年龄越大相对越容易辍学。
2、影响学生选课、成绩的分析
df_0 = df[['该地区失业率','该地区通货膨胀率','该地区国内生产总值' ,'第1学期的课程数(批准)','第1学期课程单位(等级)','第2学期的课程数(批准)','第2学期的课程数(成绩)']]
df_c0 = df_0.corr(method='spearman')
df_c0
df_0 = df[['婚姻状况','国际学生','性别','是否为债务人','是否流离失所' ,'第1学期的课程数(批准)','第1学期课程单位(等级)','第2学期的课程数(批准)','第2学期的课程数(成绩)']]
df_c0 = df_0.corr(method='spearman')
df_c0
df_0 = df[['日间/晚间出勤','教育方面的特殊需要','学费是否过期','是否为奖学金持有者','入学时的年龄','第1学期的课程数(批准)','第1学期课程单位(等级)','第2学期的课程数(批准)','第2学期的课程数(成绩)']]
df_c0 = df_0.corr(method='spearman')
df_c0
结合多张表可以看出:
- 1学期和2学期课程数量、成绩高度正相关:说明学生水平稳定,符合现实情况,也说明成绩优秀的学生也倾向于选更多的课
- 年龄越大的人倾向于晚间出勤
- 持有奖学金与课程数量、成绩高度正相关
3、社会区域信息调查
此时我们根据分出的不同地区计算出辍学率、流离失所率、债务率,我们将从区域层面进行相关性探究。
这里我们重点关注一个问题:预测学生未来是否可能辍学。==我们将“已入学”和“在读”合并记为一类,记为0;值得关注的辍学样本记为1,进行重新编码,此时我们后续可以构建二分类模型,并进行预测。
df['是否辍学'] = df['学业状态'].map({'辍学':1,'已入学':0,'毕业':0})
df['是否辍学'].value_counts()
>>>
>0 3003
1 1421
Name: 是否辍学, dtype: int64
df_social = df.groupby(['local']).agg({'是否辍学':[np.mean],'是否流离失所':[np.mean],'是否为债务人':[np.mean],'该地区失业率':[np.mean],'该地区通货膨胀率':[np.mean],'该地区国内生产总值':[np.mean]})
df_social.corr(method='spearman')
从不同地区指标来看:
- 生产总值的降低强相关与失业率、辍学率的提升,想关于高通货膨胀率
- 失业率的提升和流离失所率紧紧相关
可以看出,通过财政手段去干预地区经济刻不容缓,不然会带来严重的连锁反应。
预测:让每个孩子都能远离辍学
#导入相关库
from sklearn.model_selection import train_test_split,cross_val_score,GridSearchCV
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score,roc_auc_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn import tree
from sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNB
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, recall_score
排除掉意义不明确变量以及相关性较低的变量后,我们建立模型。
X = df[['婚姻状况','国际学生','性别','是否为债务人','是否流离失所',
'该地区失业率','该地区通货膨胀率','该地区国内生产总值' ,
'日间/晚间出勤','教育方面的特殊需要','学费是否过期',
'是否为奖学金持有者','入学时的年龄','第1学期的课程数(批准)',
'第1学期课程单位(等级)','第2学期的课程数(批准)','第2学期的课程数(成绩)']]
y = df['是否辍学']
#划分训练集和测试集
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)
print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
>>>
>(3096, 17)
(3096,)
(1328, 17)
(1328,)
1、模型探索
models = [GaussianNB(),BernoulliNB(),KNeighborsClassifier(),RandomForestClassifier(),KNeighborsClassifier(),tree.DecisionTreeClassifier(),LogisticRegression(),SVC()]
for clf in models:
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
score = clf.score(X_test,y_test)
recall = recall_score(y_test,y_pred)
print('模型score为:',round(score,4),' recall为:',round(recall,4),' ',str(clf))# ,' auc为:',round(auc,4)
>>>
>模型score为: 0.8283 recall为: 0.7095 GaussianNB()
模型score为: 0.8336 recall为: 0.6143 BernoulliNB()
模型score为: 0.8223 recall为: 0.6143 KNeighborsClassifier()
模型score为: 0.8584 recall为: 0.6857 RandomForestClassifier()
模型score为: 0.8223 recall为: 0.6143 KNeighborsClassifier()
模型score为: 0.8185 recall为: 0.7119 DecisionTreeClassifier()
模型score为: 0.8517 recall为: 0.7024 LogisticRegression()
模型score为: 0.8178 recall为: 0.5381 SVC()
for clf in models:
scores = cross_val_score(clf, X_train, y_train, cv=10)
print('交叉验证下模型 ',str(clf),' 的score为:',round(np.mean(scores),4))# ,' auc为:',round(auc,4)
>>>
>交叉验证下模型 GaussianNB() 的score为: 0.8285
交叉验证下模型 BernoulliNB() 的score为: 0.8388
交叉验证下模型 KNeighborsClassifier() 的score为: 0.8236
交叉验证下模型 RandomForestClassifier() 的score为: 0.8563
交叉验证下模型 KNeighborsClassifier() 的score为: 0.8236
交叉验证下模型 DecisionTreeClassifier() 的score为: 0.7845
交叉验证下模型 LogisticRegression() 的score为: 0.8501
交叉验证下模型 SVC() 的score为: 0.8191
2、对选择出的模型调节参数
目前看好的算法有 GaussianNB(),RandomForestClassifier()、LogisticRegression(),这几个模型召回率以及交叉验证下的模型得分均有不错的表现。
朴素贝叶斯方法基本采用默认参数,因此不进行具体参数调节。
(1)logistics回归参数调节
for i in np.linspace(0.5,5,10): # 格点搜索惩罚强度
clf = LogisticRegression(penalty="l1",solver="liblinear",C=i)
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
score = clf.score(X_test,y_test)
recall = recall_score(y_test,y_pred)
print('模型score为:',round(score,4),' recall为:',round(recall,4),' 参数为',i,'l1')
clf = LogisticRegression(penalty="l2",solver="newton-cg",C=i)
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
score = clf.score(X_test,y_test)
recall = recall_score(y_test,y_pred)
print('模型score为:',round(score,4),' recall为:',round(recall,4),' 参数为',i,'l2')
>>>
>模型score为: 0.8554 recall为: 0.7 参数为 0.5 l1
模型score为: 0.8547 recall为: 0.6976 参数为 0.5 l2
模型score为: 0.8547 recall为: 0.7 参数为 1.0 l1
模型score为: 0.8547 recall为: 0.7 参数为 1.0 l2
模型score为: 0.8547 recall为: 0.7 参数为 1.5 l1
模型score为: 0.8547 recall为: 0.7 参数为 1.5 l2
模型score为: 0.8547 recall为: 0.7 参数为 2.0 l1
模型score为: 0.8547 recall为: 0.7 参数为 2.0 l2
模型score为: 0.8547 recall为: 0.7 参数为 2.5 l1
模型score为: 0.8547 recall为: 0.7 参数为 2.5 l2
模型score为: 0.8554 recall为: 0.7024 参数为 3.0 l1
模型score为: 0.8547 recall为: 0.7 参数为 3.0 l2
模型score为: 0.8554 recall为: 0.7024 参数为 3.5 l1
模型score为: 0.8554 recall为: 0.7024 参数为 3.5 l2
模型score为: 0.8554 recall为: 0.7024 参数为 4.0 l1
模型score为: 0.8554 recall为: 0.7024 参数为 4.0 l2
模型score为: 0.8554 recall为: 0.7024 参数为 4.5 l1
模型score为: 0.8554 recall为: 0.7024 参数为 4.5 l2
模型score为: 0.8554 recall为: 0.7024 参数为 5.0 l1
模型score为: 0.8554 recall为: 0.7024 参数为 5.0 l2
通过调节solver(‘liblinear’,‘saga’,‘lbfgs’,‘newton-cg’)和C的取值,观察最终取 l1 正则化下的 solver = “liblinear” C = 1 ,此时召回率和score均有所提升。
(2)随机森林参数调节
param_grid = {
'bootstrap': [True],
'max_depth': [6],#np.linspace(6,8,3),
'max_features': [7],#[2, 3, 4, 5, 6, 7, 8, 9, None],
'n_estimators': [50],#, 100, 200, 300, 500, 800],
'min_samples_split': [8]#[2, 4, 6, 8, 10, 12]
}
rfc = RandomForestClassifier()
clf = GridSearchCV(estimator = rfc, param_grid = param_grid, cv = 3)
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
score = clf.score(X_test,y_test)
recall = recall_score(y_test,y_pred)
print('模型score为:',round(score,4),' recall为:',round(recall,4))
print(clf.best_params_)
print(clf.best_estimator_)
>>>
>模型score为: 0.8599 recall为: 0.7071
{'bootstrap': True, 'max_depth': 6, 'max_features': 7, 'min_samples_split': 8, 'n_estimators': 50}
RandomForestClassifier(max_depth=6, max_features=7, min_samples_split=8,
n_estimators=50)
最终我们将模型score提升到0,878,召回率提高到0,747。
至此我们可以更准确的去发现学生辍学的风险并进行及时的干预。