学生分数探索(随机森林)

一、前言

自学数据分析的小白第一次写博客,更多详细内容可以到我的和鲸社区主页中查看:

学生分数探索,感谢各位大佬支持!

1.数据集背景

该数据集包括一所(虚构的)公立学校学生的三次考试成绩,以及可能对他们产生交互影响的各种个人和社会经济因素。

这些数据集是虚构的,仅用于教育目的。

最初的数据集生成器创建者是罗伊斯·基蒙斯先生

在kaggle上已经有了类似的数据集,但这个不同,而且可以说在两个方面更好。

-> 1)拥有更多的数据(>30k而不是其他数据集的1k),

-> 2)扩展了具有更多特征的数据集,并且具有缺失值,这使其成为数据清理和数据预处理的理想选择。

2.数据字段说明

字段说明

性别

男/女
分组A至E组
父母教育背景高中到硕士
午餐类型标准或免费/减价
完成备考课程已完成或未完成
父母婚恋状态已婚/单身/丧偶/离婚
参与运动频率从不/有时/定期
是否是第一个孩子是/否
兄弟姐妹数量0到7
上学交通工具校车/私家车
每周自习时间少于5小时/5至10小时/10小时以上
数学成绩0-100
阅读成绩0-100
写作成绩0-100

3.数据集预览

原数据集为英文

二、数据清洗及预处理

# 导包
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
# 读取数据
data  = pd.read_csv('/home/mw/input/data1231/Students_Exam_Scores.csv',encoding = 'gbk')
# 查看数据大小
data.shape
# (30641, 15)
# 查看数据前5行
display(data.head(5))
print('-'*130)
# 查看数据缺失情况
data.isna().sum()
# 部分字段缺失比较严重

# 查看特征缺失的情况
print(f'分组字段的统计情况\n{data.分组.value_counts()}')
print('-'*100)
print(f'父母教育背景字段的统计情况\n{data.父母教育背景.value_counts()}')
print('-'*100)
print(f'完成备考课程字段的统计情况\n{data.完成备考课程.value_counts()}')
print('-'*100)
print(f'父母婚恋状态字段的统计情况\n{data.父母婚恋状态.value_counts()}')
print('-'*100)
print(f'参与运动的频率字段的统计情况\n{data.参与运动的频率.value_counts()}')
print('-'*100)
print(f'是否是第一个孩子字段的统计情况\n{data.是否是第一个孩子.value_counts()}')
print('-'*100)
print(f'兄弟姐妹数量字段的统计情况\n{data.兄弟姐妹数量.value_counts()}')
print('-'*100)
print(f'上学交通工具字段的统计情况\n{data.上学交通工具.value_counts()}')
print('-'*100)
print(f'每周自习时间字段的统计情况\n{data.每周自习时间.value_counts()}')
print('-'*100)

# 缺失值填充
# 参与运动频率字段缺失用'有时'填充
data.参与运动的频率.fillna('有时',inplace=True)
# 是否是第一个孩子字段缺失用'是'填充
data.是否是第一个孩子.fillna('是',inplace=True)
# 每周自习时间字段用'5-10'填充
data.每周自习时间.fillna('5到10',inplace=True)
# 兄弟姐妹数量用均值填充
mean = round(data.兄弟姐妹数量.mean(),0)
data.兄弟姐妹数量.fillna(mean,inplace=True)
# 其余字段用Unknown填充
data.fillna('Unknown',inplace=True)
# 分数的异常值探索,查看一下是否有大于100或者小于0的数据
subject_list = ['数学成绩','写作成绩','阅读成绩']
for subject in subject_list:
    print(f'{subject}:')
    print("是否存在超出100的值:",any(data[subject]>100))
    print("是否存在小于0的值:",any(data[subject]<0))
    mean1 = data[subject].quantile(q=0.25)#下四分位差
    mean2 = data[subject].quantile(q=0.75)#上四分位差
    mean3 = mean2-mean1#中位差
    topnum2 = mean2+1.5*mean3
    bottomnum2 = mean2-1.5*mean3
    print("箱型图正常值的范围:",bottomnum2,'-',topnum2)
    print(f"小于{bottomnum2}的比例为:",round((data[data[subject]<bottomnum2].shape[0])/(data.shape[0])*100,2),"%")
    print('-'*100)
# 数据均符合取值范围,但是按照箱型图的正常取值范围来看,写作成绩的异常值数据比例最高,达到了9.27%

data.head(5)
# 去掉序号列
data = data.iloc[:,1:]
data.head(5)

 三、数据可视化

from pyecharts.charts import Bar
from pyecharts import options as opts
# 探索一下每个字段对于分数的影响
def BAR(score): # 画图函数
      y_list = score.index.values.tolist()
      bar = Bar()
      bar = bar.add_xaxis(score.columns.values.tolist())
      for i in y_list:
            bar = bar.add_yaxis(i, score.loc[i,:].values.round(2).tolist())
      return bar.render_notebook()
def SCORE(column): # 平均成绩聚类函数
      score = data.groupby([column]).agg({'数学成绩':'mean','阅读成绩':'mean','写作成绩':'mean'})
      return score
# 性别对于平均成绩的影响
BAR(SCORE('性别'))

 其余可视化图表可见:学生分数探索


根据可视化柱状图可以得出以下结论:
1.种族从A到E的各科成绩在逐步提升,数学成绩的差异最为明显;
2.父母教育水平是大学的,明显比其他平均分高很多,而高中及以下的学历学生平均分无太大区别,硕士学位的比学士学位的平均分高,但父母是双学士学位的却比学士学位的平均分低;
3.运动越频繁,和每周自习成绩越久,各科成绩平均分会更高一些;
4.写作和阅读成绩,女生的平均分明显高于男生,而在数学成绩上,男生明显高于女生;
5.午餐类型上,吃标准午餐的学生各科平均成绩明显比吃免费/降价的学生更高;
6.有完成备考课程的学生各科成绩会比没有备考学生成绩更高些;
7.是否是第一个孩子,上学乘坐什么交通工具,家里兄弟姐妹数量以及父母婚姻状态对各科成绩没有明显影响。

四、随机森林建模

# 随机森林预测
from sklearn.preprocessing import OrdinalEncoder
from sklearn.model_selection import GridSearchCV
from sklearn import metrics
# 将数据进行切分,分为训练集和测试集
X = data.iloc[:,:-3].values
X = OrdinalEncoder().fit_transform(X)
y = data.iloc[:,-3:].values
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=42)
# 建模的同时进行超参数调节
# 利用for循环调节n_estimators参数,同时用r2_score评估模型得分
score=[]
for i in range(0,400,10):
    regressor = RandomForestRegressor(n_estimators=i+1,random_state=42)
    regressor.fit(X_train, y_train)
    y_pred = regressor.predict(X_test)
# 用r2_score评估回归性能
    score.append(metrics.r2_score(y_test, y_pred))   
print(max(score),(score.index(max(score))*10)+1)
# 最好的n_estimators为371
#网格搜索得分最高的决策树的max_depth
param_grid = {'max_depth':np.arange(1,20,1)}
regressor = RandomForestRegressor(n_estimators=331,random_state=42)
GS = GridSearchCV(regressor,param_grid)
GS.fit(X_train, y_train)
GS.best_params_
# 最好的max_depth为7
# for循环找到最好的n_estimators为371,但是后续手动调参发现,n_estimators为227时得分最高
# 将测试集带入到训练好的随机森林模型中
final_regressor = RandomForestRegressor(n_estimators=227,max_depth=7,random_state=42)
final_regressor.fit(X_train, y_train)
y_pred = final_regressor.predict(X_test)
# r2_score评估回归性能
metrics.r2_score(y_test, y_pred)
# r2_score = 0.27279074141922294
# 查看决策树建模过程中每个特征的重要性
[*zip(data.columns[:-3].tolist(),final_regressor.feature_importances_.round(3))]

最终的r2_score得分为0.27279074141922294,可以看出模型的精度并不高

# 尝试去掉重要性最低的特征进行建模
data2 = data.drop(['是否是第一个孩子'],axis=1)
X1 = data2.iloc[:,:-3].values
X1 = OrdinalEncoder().fit_transform(X)
y1 = data.iloc[:,-3:].values
X1_train, X1_test, y1_train, y1_test = train_test_split(X1,y1,test_size=0.2,random_state=42)
# 建模,评估回归性能
final_regressor_2 = RandomForestRegressor(n_estimators=227,max_depth=7,random_state=42)
final_regressor_2.fit(X1_train, y1_train)
y1_pred = final_regressor_2.predict(X1_test)
metrics.r2_score(y1_test, y1_pred)
# 发现得分并没有升高,所以去掉这个特征对模型几乎没有影响

本次数据分析没有对训练集数据进行交叉验证,同时随机森林模型的精度并不高,后续还可以使用LightGBM 和 XGBOOST等机器学习集成算法进行探索,总之还有很多不足,希望各位大佬批评指正!

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值