如今,在线测试越来越多,在线题库的使用也更加频繁,而根据质量对试题进行筛选的算法可以大量的节约人工和时间。以下为一套全流程的简易版的试题筛选算法,从数据清洗到数据分析再到数据筛选。通过R语言进行分析,通过Java实现筛选。
##dealData = function(filename){}是用于连接Jave所构建的function,若不需要,去掉即可###
dealData = function(filename) {
###################################
##载入需要的R包##
library("Rserve") #用于连接R与Java
library("TAM") #用于进行IRT分析
library(readxl) #用于读取excel数据表
####################################
setwd("C:/...") ##设置数据所在的目录##
testdata = read_excel("data.xlsx", col_names = TRUE) ##读取数据##
data = testdata
#######清洗数据#######
#删除大量作答都得0分的测试者数据##
##构建function,查找0多于数据行长的80%的数据##
ff = function(row) (sum(row==0) > ncol(data)*0.8)
##删除作答得到0分数量多于行长80%的测试者作答#####
data = data[!apply(data, 1,ff),]
#删除存在大量空值的行和列
B = B[!apply(B, 1, function(x){all(is.na(x))}),]
B = B[,!apply(B, 2, function(x){all(is.na(x))})]
##删除NA大于60%的行数据##
B = B[!apply(B, 1, function(x){sum(is.na(x))>=length(x)*0.6}),]
##删除满分或0分的行数据##
B = B[!apply(B, 1, function(x){max(table(as.numeric(x)))>=length(x)}),]
##删除NA大于60%的列数据##
B = B[,!apply(B, 2, function(x){sum(is.na(x))>=length(x)*0.6})]
##删除满分或0分的列数据##
B = B[,!apply(B, 2, function(x){max(table(as.numeric(x)))>=length(x)})]
####将数据转为无间隔顺序数据###
####例如:某道题的作答仅有0和2,转化后,变成有0、1####
####若某道题的作答仅有0、2、3,转化后,变成0、1、2########
x = list()
for(j in 1:ncol(B)){
x[[j]] = sort(unlist(unique(B[,j])))
for(i in 1:length(x[[j]])){
B[,j][which(B[,j]==x[[j]][i]),]=i-1
}
}
##设置输出小数点保留3位##
options(digits=3)
#########将选择题各类选项进行计分转化#########
#在1,2,3列的3道题的答案为A,凡作答A的转化为1,其他转化为0#
#在4,5,6列的3道题的答案为B,凡作答B的转化为1,其他转化为0#
#在7,8,9列的3道题的答案为C,凡作答C的转化为1,其他转化为0#
#在10,11,12列的3道题的答案为D,凡作答D的转化为1,其他转化为0#
B[,c(1,2,3)] = (B[,c(1,2,3)]=="A")*1
B[,c(4,5,6)] = (B[,c(4,5,6)]=="B")*1
B[,c(7,8,9)] = (B[,c(7,8,9)]=="C")*1
B[,c(10,11,12)] = (B[,c(10,11,12)]=="D")*1
###如果没有计分转化,直接跳到此步骤#########
resp = B
###########数据分析过程######################
mod1 = TAM::tam.mml(resp,irtmodel="PCM") ###使用PCM进行分析##
#summary(mod1)
IRTdiff = mod1$item[,c(1,2,4)] ##提取IRT难度##
Abil = tam.wle(mod1) ##求能力值##
PersonAbility = Abil$theta #提取作答者能力##
Fit = msq.itemfit(mod1) ##求题目拟合值##
Fit = Fit$itemfit[,c(1,3,6)] ##提取题目拟合值##
cttmean = mod1$item[,3] ###提取每道题作答的平均值###
maxcol = apply(resp,2,max,na.rm = TRUE) ##此语句假设每道题的所有作答中至少有一个作答为满分,若某题的作答没有最高分时,需要重新设置####重新设置: maxcol = c(每题最高分)########
CTTdiff = cttmean/maxcol ###求CTT难度###
Discrim = cor(resp,PersonAbility,use="complete.ob")##求区分度,即能力值与作答相关##
alldata = cbind(IRTdiff,CTTdiff,Discrim,Fit[,-1]) ###将所有数据组成一个数组###
colnames(alldata) = c("Item","N","IRTdiff","CTTdiff","Discrim", "Outfit", "Infit") ###给数组每列命名####
########根据上述数据中的各项参数值设定题目筛选条件#####################
##设定条件为:作答人数超过100的题目且题量超过30,区分度大于0.5,拟合值在0.65~1.2间##
condition = ifelse(alldata$N >= 100 & length(rownames(alldata)) >= 30
& alldata$Discrim >= 0.5
& alldata$Outfit <= 1.2 & alldata$Outfit >= 0.65
& alldata$Infit <= 1.2 & alldata$Infit >= 0.65
,1,0)
######将筛选出来的题目按照CTT难度进行高、中、低分组###########
easycon = ifelse(alldata$CTTdiff >= 0.75& alldata$CTTdiff < 0.9, 1,0)
medcon = ifelse(alldata$CTTdiff >= 0.5 & alldata$CTTdiff < 0.75,1,0)
hardcon = ifelse(alldata$CTTdiff >= 0.15 & alldata$CTTdiff < 0.5,1,0)
condata = cbind(alldata,condition,easycon,medcon,hardcon)
condata
###########提取各种难度类型的题号######################
easyItem = rownames(condata[which(condata$condition == 1 & condata$easycon == 1),])
mediumItem = rownames(condata[which(condata$condition == 1 & condata$medcon == 1),])
hardItem = rownames(condata[which(condata$condition == 1 & condata$hardcon == 1),])
e = list(easyItem = easyItem, mediumItem = mediumItem, hardItem = hardItem)
return (e)
}