多角度学生学业探索与辍学预测

通过对学生数据集的深入分析,包括描述性统计、相关性分析和算法预测,研究了影响学生辍学的因素,如婚姻状况、债务、地区经济状况等。此外,还探讨了学业成绩、课程信息与学生背景之间的关系。通过相关性分析,发现学费过期、奖学金持有情况与辍学率有关,而年龄、出勤模式和课程数量对学业成绩有一定影响。最后,利用机器学习模型预测了辍学风险,以支持早期干预策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

梳理思路

观察数据集

初步观察数据集,发现指标如下:“婚姻状况 申请方式 申请顺序 课程 日间/晚间出勤 以前的学历 国籍 母亲的学历 父亲的学历 母亲的职业 父亲的职业 是否流离失所 教育方面的特殊需要 是否为债务人 学费是否过期 性别 是否为奖学金持有者 入学时的年龄 国际学生 已记入的第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()

前5行的数据
查看各个特征的类型以及检查是否存在缺失值。

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)

每个学期的6各变量
我们进行如下假设:

  • ‘第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()

学生第1学期的课程数(批准)

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()

学生第2学期的课程数(批准)
两学期呈现出较为一致的特点:

  • 多数学生批准课程数为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()

第1学期课程单位等级

df['grade2'] = round(df['第2学期的课程数(成绩)'],1)
df_gr2 = df[(df['第2学期的课程数(成绩)']>0 )&(df['第2学期的课程数(成绩)']<19 )]
plt.hist(df_gr2['grade2'],bins=15)
plt.show()

第2学期课程数(成绩)
可以看到成绩集中在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

影响学生成绩、选课的因素1

df_0 = df[['婚姻状况','国际学生','性别','是否为债务人','是否流离失所' ,'第1学期的课程数(批准)','第1学期课程单位(等级)','第2学期的课程数(批准)','第2学期的课程数(成绩)']]
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

影响学生选课、成绩的因素3
结合多张表可以看出:

  • 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。
至此我们可以更准确的去发现学生辍学的风险并进行及时的干预。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值