CSDN文章编写#7
#2021-0316-7 毕设--基于雨课堂平台的学生在线预习行为数据分析
接上篇:链接: 传送门.
2 学生预习行为特征的聚类分析及算法优化
为了进一步研究学生的类型,笔者根据学生的在线预习学习行为数据,主要采用聚类分析方法对本班学生进行分类探究。聚类分析是常用的数据挖掘技术,通过直接对比不同对象不同数据之间的特征,将特征相近的归为一类,反之则归入不同的类。聚类分析方法由不同算法决定不同聚类分析,最终本文选择K-means及K-means++算法对学生进行分类。
关键代码:
K-means:
import pandas as pd
import numpy as np
from pandas import DataFrame,Series
from sklearn.cluster import KMeans
from sklearn.cluster import Birch
import warnings
warnings.filterwarnings("ignore")
#读取文件
datafile = u'E:/Program Files/Tencent/QQ/新疆大学/大三暑假至大四上半学期/科研实践/雨课堂数据/统计汇总.xls'#文件所在位置,u为防止路径中有中文名称,此处没有,可以省略
outfile = u'E:\Program Files\Tencent\QQ\新疆大学\大三暑假至大四上半学期\科研实践\雨课堂数据\聚类\聚类.xls'#设置输出文件的位置
data = pd.read_excel(datafile)#datafile是excel文件,所以用read_excel,如果是csv文件则用read_csv
d = DataFrame(data)
d = d.drop(columns=["ID","预习总用时","刷课总次数","预习未完成总次数"])
# ,"观看总页数","预习总用时","预习未完成总次数"
f = d[["观看总页数","超前完成总次数"]]
f['观看总页数'] = f['观看总页数'].astype("float")
# f['预习完成总次数'] = f['预习完成总次数'].astype("float")
f['超前完成总次数'] = f['超前完成总次数'].astype("float")
#聚类
mod = KMeans(n_clusters=3, n_jobs = 4, max_iter = 500,init='random')#聚成3类数据,并发数为4,最大循环次数为500
mod.fit_predict(d)#y_pred表示聚类的结果
#聚成3类数据,统计每个聚类下的数据量,并且求出他们的中心
r1 = pd.Series(mod.labels_).value_counts()
r2 = pd.DataFrame(mod.cluster_centers_)
r = pd.concat([r2, r1], axis = 1)
r.columns = list(d.columns) + [u'类别数目']
print(r)
#给每一条数据标注上被分为哪一类
r = pd.concat([d, pd.Series(mod.labels_, index = d.index)], axis = 1)
r.columns = list(d.columns) + [u'聚类类别']
print(r.head(15))
# r.to_excel(outfile)#如果需要保存到本地,就写上这一列
聚类结果:
二分K-means:
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0]
# 首先创建一个矩阵来存储数据集中每个点的簇分配结果及平方误差
clusterAssment = mat(zeros((m, 2)))
# ----创建一个初始簇----
# 计算整个数据集的质心
centroid0 = mean(dataSet, axis=0).tolist()[0]
# 使用一个列表来保留所有的质心
centList = [centroid0]
# 遍历数据集中所有点来计算每个点到质心的误差值
for j in range(m):
clusterAssment[j, 1] = distMeas(mat(centroid0), dataSet[j, :]) ** 2
# while循环会不停的对簇进行划分,直到得到想要的簇数目为止
while (len(centList) < k):
lowestSSE = inf # python中inf表示正无穷
# 遍历簇列表centList中的每一个簇
for i in range(len(centList)):
# -----尝试划分每一簇----
# 对每个簇,将该簇中的所有点看成一个小的数据集ptsInCurrCluster
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
# 将ptsInCurrCluster输入到函数KMeans()中进行处理(k=2)。K-均值算法会生成两个质心(簇),同时给出每个簇的误差值
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
# 划分后的样本误差之和
sseSplit = sum(splitClustAss[:, 1])
# 剩余数据集的误差之和
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
print("sseSplit, and notSplit: ", sseSplit, sseNotSplit)
# 将划分后的样本误差之和+剩余数据集的误差之和作为本次划分的误差
if (sseSplit + sseNotSplit) < lowestSSE:
# 决定要划分某一个簇
bestCentToSplit = i
# 划分后的质心
bestNewCents = centroidMat
# 划分后的簇分配结果矩阵,包含两列:第一列记录簇索引,第二列存储误差
bestClustAss = splitClustAss.copy()
# 本次划分的误差
lowestSSE = sseSplit + sseNotSplit
# ----更新簇的分配结果----
# 将划分簇中所有点的簇分配结果进行修改,当使用KMeans()函数并且指定簇数为2时,会得到两个编号分别为0和1的结果簇
# 需要将这些簇编号修改为划分簇及新加簇的编号,该过程可以通过两个数组过滤器来完成。
bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSplit
print('the bestCentToSplit is: ', bestCentToSplit)
print('the len of bestClustAss is: ', len(bestClustAss))
# 新的质心会被添加到centList中
centList[bestCentToSplit] = bestNewCents[0, :].tolist()[0]
centList.append(bestNewCents[1, :].tolist()[0])
# 更新SSE的值(sum of squared errors)
clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSplit)[0], :] = bestClustAss
return mat(centList), clusterAssment
#聚类算法效率比较
import time
r = time.time()
mod = KMeans(n_clusters=3,max_iter = 500,init='k-means++')#聚成3类数据,并发数为4,最大循环次数为500
mod.fit_predict(d)#y_pred表示聚类的结果
print("k-means++聚类花费时间为:",time.time()-r)
r1 = time.time()
kmeans = KMeans(n_clusters=3,max_iter = 500,init='random')
kmeans.fit_predict(d)
print("随机聚类花费时间为:",time.time()-r1)
# print(mod.fit_predict(d))
#聚成3类数据,统计每个聚类下的数据量,并且求出他们的中心
r1 = pd.Series(mod.labels_).value_counts()
r2 = pd.DataFrame(mod.cluster_centers_)
# print(r2,time.time()-r)
r = pd.concat([r2, r1], axis = 1)
r.columns = list(d.columns) + [u'类别数目']
# #给每一条数据标注上被分为哪一类
r = pd.concat([d, pd.Series(mod.labels_, index = d.index)], axis = 1)
r.columns = list(d.columns) + [u'聚类类别']
r.head()
K-means及K-means++算法比较:
由分析结果得出结论:
3 学生预习行为特征与学习效果的相关性分析
特征选择是为了降维,从中挑选最具代表意义的特征。其选择方法是用一些如 Pearson相关系数、基尼指标等的评价指标单独地计算出各个特征与目标变量之间的相关系数,再由相关系数的大小判断是否这俩特征的相关性强弱。在这里笔者使用Pearson相关系数作为评价指标,它的计算方式如下:
X表示一个特征值的多个观测值,Y表示这个特征观测值对应的类别列表,X、
Y分别是X和Y的平均值。Pearson相关系数的取值在0~l间,通常,相关系数r的绝对值越大,相关性越强,即r越接近-l或l, r代表变量间的关系密切程度,具体如表3-4所示。
图3-4 相关系数
数据集处理。之前的数据不包含学生的成绩这一项字段,分析的最终结果是需要查看哪些字段与学生最终的成绩有关或者哪些因素影响着学生最终成绩,因此数据集需要插入学生数据并做数据集成:
import warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
import sqlalchemy
import pymysql
import math
\# import matplotlib.pyplot as plt
\# %matplotlib inline
\# import matplotlib.style as psl
file = "E:/Program Files/Tencent/QQ/新疆大学/大三暑假至大四上半学期/科研实践/雨课堂数据/统计汇总.xls"
file0 = "E:/Program Files/Tencent/QQ/新疆大学/大三暑假至大四上半学期/科研实践/雨课堂数据/雨课堂部分数据/web安全程序设计_2018_92480_3.7(含期末成绩).xls"
putfile = "E:/Program Files/Tencent/QQ/新疆大学/大三暑假至大四上半学期/科研实践/雨课堂数据/雨课堂部分数据/学生预习及成绩汇总.xls"
d = pd.read_excel(file,index_col=False,header=0)
d0 = pd.read_excel(file0,sheet_name="期末成绩单",index_col=False,header=0)
\# d0['Unnamed: 0'] = d0['Unnamed: 0'].astype("int64")
\# d=d.drop(columns=["预习总用时","预习未完成总次数"])
d["卷面成绩"] = d0["卷面成绩"]
d.head()
\# d.to_excel(putfile,index=False)
学生成绩字段集成结果:
图3-5 局部学生成绩集成结果
图3-6 分析结果
由图3-6以及图3-4可知,卷面成绩与观看总页数、卷面成绩与预习总用时、卷面成绩与刷课总次数、卷面成绩与超前完成总次数、卷面成绩与预习完成总次数及卷面成绩与预习未完成总次数的相关系数分别是0.06102、-0.027321、-0.016982、-0.033944、0.049811以及-0.049811。这里的预习完成数与预习未完成数相同且绝对值相同,是因为预习为完成次数是由预习完成次数导出来的,而完成总次数是不变的,所以会出现上面这种情况。除此之外,可以看出各个特征字段与卷面成绩之间相关系数的绝对值极小,接近于零,相关程度特别微弱,接近不相关。出现此现象的一部分原因是学生成绩受各种方面的影响,如心理、学习方法、家庭背景等种种因素。
4 学生预习行为特征与学习效果的回归分析
由3.3可以得到卷面成绩与观看总页数、卷面成绩与超前完成总次数、卷面成绩与预习完成总次数相关系数相较于其他的数值较大,所以这里我们重点研究这三个关键特征的回归分析。
结果分析:
回归分析1
在模型汇总表格中,我们可以看到相关系数为0.236,决定系数为0.055。
回归分析2
从ANOVA,即方差分析结果图中我们可以看出,对回归方程显著性进行假设检验(F检验),显著性P>0.05,说明在本次分析中,模型不具统计学意义。
回归分析3
在系数表格中,常数项为-12.129,一次项系数分为-3.889、10.704及7.903,对一次项系数进行假设检验的P=0.795>>0.05,则认为总体的一次项系数为0,在表格中,常量为-12.129,变量超前完成总次数、预习完成总次数以及刷课总次数的系数分为-3.889、10.704、7.903,所以我们可以得到一元线性回归方程分为:y= -3.889x-12.129、y= 10.704x-12.129、y= -3.889x-12.129。