题目筛选算法 – R语言

如今,在线测试越来越多,在线题库的使用也更加频繁,而根据质量对试题进行筛选的算法可以大量的节约人工和时间。以下为一套全流程的简易版的试题筛选算法,从数据清洗到数据分析再到数据筛选。通过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)
  
}

发表评论

您的电子邮箱地址不会被公开。