buildHeaders <- function(numberOfQuestions){
  colHeaders = c()
  #build headers
  for (n in seq(numberOfQuestions)){
    colHeaders = c(colHeaders,paste("q",as.character(n),sep = ""))
  }
  return(colHeaders)
}
variationWithinQuestionTypes <- function(m1,pat){
  #compare each question with the average
  totcol = ncol(m1)
  numcol = (totcol-2-10)/2
  aveQ = data.frame( matrix(ncol=3 ,nrow=0) )
  indivQ = data.frame( matrix(ncol=0 ,nrow=nrow(m1)) )
  for (q in seq(10,totcol-2,2)){
    #q is the number, q-1 is the tilte
    qtitle = colnames(m1)[q-1]
    if ( grepl(pat,qtitle)){
      thisQ = c(
        colnames(m1)[q-1],
        mean(m1[,q],na.rm=TRUE),
        length( na.omit(m1[,q]))
      )
      aveQ = rbind(aveQ,thisQ)
      indivQ[qtitle] = m1[,q]
      
    }
  }
  results = list("indi" = indivQ, "ave" = aveQ)
  return(results)
  
}
printVariationWithinQuestions <- function(indivQ,ndig){
  #library(Hmisc)
  #for some reason I cant calc the correlation when theres too many NA
  #res2<-rcorr(as.matrix(indivQ),type = "pearson")
  #flattenCorrMatrix(res2$r, res2$P)
   
  totlen = length(colnames(indivQ))
  #pval = matrix(1:totlen, nrow = totlen, ncol = totlen)
  pval = data.frame( matrix(ncol=totlen ,nrow=0) )
  for (i in seq(1,totlen)){
    thisline = c()
    for (j in seq(1,totlen)){
      if ( j < i){
        thisline = c(thisline," ")
      }else{
        #p = as.numeric(t.test(indivQ[,i],indivQ[,j])$p.value)
        #need a tryCatch just in case we are comparing questions without attempts
        #tryCatch(
        #  expr = {
            p = format(round( t.test(indivQ[,i],indivQ[,j])$p.value,ndig), nsmall = ndig)
            if( p < 0.05){ p = paste(p,"*",sep = "")}
        #  },
        #  error = {
        #    p = "nope"
        #  }
        #)
        thisline = c(thisline,p)
      }
    }
    pval = rbind(pval,thisline)
  }
  colnames(pval) = colnames(indivQ)
  rownames(pval) = colnames(indivQ)
  return(pval)
  #t.test(indivQ$q8_4,indivQ$q8_5)
}
buildDF_fromMilestone <- function(m1,numberOfQuestions){
  #Build df with just answers 
  totcol = ncol(m1)
  m1df = data.frame( matrix(ncol=numberOfQuestions+3,nrow=0) )
  
  
  colHeaders = c(buildHeaders(numberOfQuestions),"total")
   
  studNames = c()
  attNumb = c()
  #loop over students or row
  for (st in seq(1,nrow(m1)) ){
    #build attemptNumb and studNames. theyll be added as columns later
    attNumb = c(attNumb,m1[st,8])
    studNames = c(studNames,m1[st,1])
    
    #empty the score array and build it up as it finds each question
    thisSt = rep(NA,numberOfQuestions)
    #loop over columns to find nonempty scores
    for (q in seq(10,totcol-2,2)){
      score = m1[st,q]
      if ( !is.na( score ) ){
       questionNumber = colnames(m1)[q-1]
       questionNumber = unlist(strsplit(questionNumber,"_"))[1]
       questionNumber = as.numeric( gsub("^q","",questionNumber))
       thisSt[questionNumber] = score
      }
    }
    #studNames = c(studNames,m1[st,1])
    thisSt = c(thisSt, m1[st,totcol])
    m1df = rbind(m1df,thisSt)
  }
  colnames(m1df) = colHeaders
  m1df = rbind(m1df,colMeans(m1df))
  m1df$studName = c(studNames,NA)
  m1df$attNumb = c(attNumb,NA)
  return(m1df)
}

buildStats <- function(m1df,numberOfQuestions,att){
  m1dfat1 = data.frame( matrix(ncol=numberOfQuestions,nrow=0) )
  m1dfat1sd = data.frame( matrix(ncol=numberOfQuestions,nrow=0) )
   
  st100 = subset(m1df,m1df$total> 79.99 & m1df$attNumb == att)
  m1dfat1 = rbind(m1dfat1, colMeans(st100[,1:numberOfQuestions]) )
  m1dfat1sd = rbind(m1dfat1sd, apply(st100[,1:numberOfQuestions],2,sd) )
   
  st80 = subset(m1df,m1df$total> 69.99 & m1df$total < 79.99 & m1df$attNumb == att)
  m1dfat1 = rbind(m1dfat1, colMeans(st80[,1:numberOfQuestions]) )
  m1dfat1sd = rbind(m1dfat1sd, apply(st80[,1:numberOfQuestions],2,sd) )
   
  st0 = subset(m1df,m1df$total< 69.99 & m1df$attNumb == att)
  m1dfat1 = rbind(m1dfat1, colMeans(st0[,1:numberOfQuestions]) )
  m1dfat1sd = rbind(m1dfat1sd, apply(st0[,1:numberOfQuestions],2,sd) )
   
  colnames(m1dfat1) = buildHeaders(numberOfQuestions)
  colnames(m1dfat1sd) =buildHeaders(numberOfQuestions)
   
  rownames(m1dfat1) = c("Avg > 80", "Avg 70><80","Avg < 70")
  rownames(m1dfat1sd) = c("SD > 80", "SD 70><80","SD < 70")
  #return(list(m1dfat1,m1dfat1sd))
  ltot = length( 
    subset(m1df,m1df$attNumb == att)[,1]
    )
  l100 = length(st100[,1])
  l80 = length(st80[,1])
  l0 = length(st0[,1])
  useStats = c(ltot,l100,l80,l0)
  results = list("ave" = m1dfat1, "sd" = m1dfat1sd, "usestats" = useStats)
  return(results)
  
}
flattenCorrMatrix <- function(cormat, pmat) {
  ut <- upper.tri(cormat)
  data.frame(
    row = rownames(cormat)[row(cormat)[ut]],
    column = rownames(cormat)[col(cormat)[ut]],
    cor  =(cormat)[ut],
    p = pmat[ut]
    )
}
roundThisScore <- function(score){
  if (score <70){ r = 0 
  } else if (score <80){ r = 80 
  } else if (score >80){ r = 100  }
  return(r)
}
flowAndSettling <- function(m1df){
  #check students who did not attempt a 3rd and got lower than 80
  studs = unique(m1df$studName)
  studs = studs[!is.na(studs)]
  settle = data.frame( matrix(ncol=3,nrow=0) )
  flow = data.frame(matrix(ncol=3,nrow=0))
  for (stud in studs){
    thisStDF = m1df[which(m1df$studName == stud),]
    #if att number lower than 3 and score lower than 80
    maxNum = max(thisStDF$attNumb)
    maxScore = max(thisStDF$total)
    if ( maxNum < 3 & maxScore < 80 ){
      settle = rbind(settle,c(stud,maxNum,maxScore))
    }
    sc1 = roundThisScore( max(m1df[which(m1df$studName == stud & m1df$attNumb < 2),]$total) )
    sc2 = roundThisScore( max(m1df[which(m1df$studName == stud & m1df$attNumb < 3),]$total) )
    sc3 = roundThisScore( max(m1df[which(m1df$studName == stud & m1df$attNumb < 4),]$total) )
    flow = rbind(flow,c(sc1,sc2,sc3))
    
  }
  colnames(flow) = c("one","two","three")
  flow2 = data.frame(matrix(ncol=3,nrow=3))
  flow2[,1]=table(flow$one)
  flow2[,2]=table(flow$two)
  flow2[,3]=table(flow$three)
  colnames(flow2) = c("After 1st","After 2nd","After 3rd")
  
  colnames(settle) = c("name","MaxAttempt","MaxScore")
  all = list("settle" = settle,"flow" = flow2)
  return(all)
}

fullCourseReport <- function(m1,nquest,semester,milenumber){
  m1df = buildDF_fromMilestone(m1,nquest)
  results = flowAndSettling(m1df)
  flow = results$flow
   
  pct1st = flow[,1]/sum(flow[,1])*100
  pct2nd = flow[,2]/sum(flow[,2])*100
  pct3rd = flow[,3]/sum(flow[,3])*100
  stackAndGrouped = data.frame(matrix(ncol = 4,nrow=0))
  stackAndGrouped = rbind(stackAndGrouped,c("F",pct1st[1],semester,"First"))
  stackAndGrouped = rbind(stackAndGrouped,c("C",pct1st[2],semester,"First"))
  stackAndGrouped = rbind(stackAndGrouped,c("A",pct1st[3],semester,"First"))
  stackAndGrouped = rbind(stackAndGrouped,c("F",pct2nd[1],semester,"Second"))
  stackAndGrouped = rbind(stackAndGrouped,c("C",pct2nd[2],semester,"Second"))
  stackAndGrouped = rbind(stackAndGrouped,c("A",pct2nd[3],semester,"Second"))
  stackAndGrouped = rbind(stackAndGrouped,c("F",pct3rd[1],semester,"Third"))
  stackAndGrouped = rbind(stackAndGrouped,c("C",pct3rd[2],semester,"Third"))
  stackAndGrouped = rbind(stackAndGrouped,c("A",pct3rd[3],semester,"Third"))
   
  pct1st = paste(signif( pct1st,digits = 4),"%",sep = "")
  pct2nd = paste(signif( pct2nd,digits = 4),"%",sep = "")
  pct3rd = paste(signif( pct3rd,digits = 4),"%",sep = "")
  flow$`After 1st` = paste( flow$`After 1st`, pct1st,sep = " / ")
  flow$`After 2nd` = paste( flow$`After 2nd`, pct2nd,sep = " / ")
  flow$`After 3rd` = paste( flow$`After 3rd`, pct3rd,sep = " / ")
  scores = c("0","80","100")
  flow = cbind(scores,flow)
  knitr::kable(flow)
   
  row.names(results$flow) = c("0","80","100")
  barplot(as.matrix(results$flow),col=c("red","yellow","green"),main =paste("Milestone ", milenumber,": students scores best of 3 attempts"),legend = rownames(results$flow))
  
}
#lets first load the files
if (Sys.info()["sysname"] == "Windows"){
  m1 = read.csv("G:/My Drive/Teaching/Grades_and_SRT/Spring2022/newm1.csv",header = TRUE)
  m2 = read.csv("G:/My Drive/Teaching/Grades_and_SRT/Spring2022/newm2.csv",header = TRUE)
  m3 = read.csv("G:/My Drive/Teaching/Grades_and_SRT/Spring2022/newm3.csv",header = TRUE)
  m4 = read.csv("G:/My Drive/Teaching/Grades_and_SRT/Spring2022/newm4.csv",header = TRUE)
  m5 = read.csv("G:/My Drive/Teaching/Grades_and_SRT/Spring2022/newm5.csv",header = TRUE)
}else{
  m1 = read.csv("~/Teaching/Grades_and_SRT/Spring2022/newm1.csv",header = TRUE)
  m2 = read.csv("~/Teaching/Grades_and_SRT/Spring2022/newm2.csv",header = TRUE)
  m3 = read.csv("~/Teaching/Grades_and_SRT/Spring2022/newm3.csv",header = TRUE)
  m4 = read.csv("~/Teaching/Grades_and_SRT/Spring2022/newm4.csv",header = TRUE)
  m5 = read.csv("~/Teaching/Grades_and_SRT/Spring2022/newm5.csv",header = TRUE)
}

1 Milestone 1

fullCourseReport(m1,12,"S22","1")

1.1 M1 - Attempt 1 averages

m1df = buildDF_fromMilestone(m1,12)
# First attempt stats
results = buildStats(m1df,12,1)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 85 38 (45%) 4 (4.7%) 43 (51%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 7.94 8.11 8.18 7.65 8.12 7.23 7.89 7.22 7.50 6.14 7.67 6.58
Avg 70><80 8.33 7.98 7.30 8.16 7.41 6.25 4.16 6.02 6.95 2.08 6.25 4.16
Avg < 70 6.82 6.88 6.25 5.59 6.34 2.71 2.71 5.09 4.49 0.97 1.74 0.77
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 1.19 0.69 0.57 1.23 0.70 2.85 1.89 1.26 1.93 3.72 2.28 3.44
SD 70><80 0.00 0.69 2.08 0.35 1.85 4.16 4.81 0.93 1.13 4.16 4.16 4.81
SD < 70 2.05 2.12 2.45 2.03 1.92 3.95 3.95 1.41 3.32 2.70 3.43 2.45
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

1.2 M1 - Attempt 2 averages

# First attempt stats
results = buildStats(m1df,12,2)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 43 18 (42%) 10 (23%) 15 (35%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 8.33 8.33 8.22 7.21 8.19 7.87 7.87 6.95 8.34 6.02 7.87 5.55
Avg 70><80 7.66 8.19 7.92 7.15 7.60 5.00 6.66 6.76 5.56 3.33 6.66 2.50
Avg < 70 6.89 6.85 6.95 5.00 6.92 2.78 3.33 5.44 3.52 3.33 3.33 0.56
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 0.00 0.00 0.49 1.77 0.66 1.96 1.96 1.16 0.00 3.84 1.96 4.04
SD 70><80 1.16 0.44 1.32 1.27 1.05 4.30 3.51 1.07 3.93 4.30 3.51 4.02
SD < 70 1.24 1.61 2.03 1.90 1.56 4.06 4.22 1.40 3.63 4.22 4.22 2.15
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 2nd attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

2 Milestone 2

List of questions

  1. Heat capacity
  2. Solution preparation
  3. Predict acid/base/neutral
  4. Calculate pH of weak species
  5. Rank by pH
  6. Calculate pH of strong
  7. Titration 4 graphs: ID where we are A-P
  8. Build buffers
  9. Qualitative solubility: Le Chatelier
  10. Titration: find equivalence point
  11. Titration: Composition oalong the curve
  12. Quantitative solubility
fullCourseReport(m2,12,"S22","2")

2.1 M2 - Attempt 1 averages

m1df = buildDF_fromMilestone(m2,12)
# First attempt stats
results = buildStats(m1df,12,1)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 83 46 (55%) 15 (18%) 22 (27%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 7.91 7.98 7.74 7.61 7.79 7.61 7.06 7.61 7.58 7.15 7.85 6.70
Avg 70><80 7.59 7.23 6.42 6.12 5.55 5.56 5.55 5.69 6.48 6.94 7.54 3.89
Avg < 70 6.94 4.93 5.60 5.69 3.03 1.90 2.27 3.69 5.05 3.79 6.12 0.76
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 1.17 1.72 1.04 2.38 2.08 2.38 2.46 1.97 1.23 2.09 0.89 3.34
SD 70><80 1.65 2.93 1.38 3.82 4.06 4.07 3.75 1.84 2.50 2.57 1.74 4.30
SD < 70 1.87 4.20 1.47 3.98 4.10 3.58 2.79 2.80 2.63 3.38 2.73 2.45
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

2.2 M2 - Attempt 2 averages

# First attempt stats
results = buildStats(m1df,12,2)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 34 11 (32%) 10 (29%) 13 (38%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 7.57 7.58 7.50 7.58 8.33 8.34 7.95 5.68 7.32 6.82 7.98 6.82
Avg 70><80 7.77 5.00 6.86 8.34 6.66 7.51 6.66 6.25 5.69 5.41 6.14 2.50
Avg < 70 6.41 4.49 5.99 4.49 3.20 1.92 3.52 3.36 5.77 4.16 6.57 2.56
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 1.30 2.51 0.77 2.51 0.00 0.00 1.26 2.65 1.87 3.37 0.86 3.37
SD 70><80 1.17 4.31 1.25 0.00 3.51 2.64 2.91 2.60 2.12 3.43 2.72 4.02
SD < 70 2.63 4.33 1.72 4.33 4.22 3.66 3.74 2.89 2.39 2.95 1.69 4.00
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 2nd attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

3 Milestone 3

List of questions

  1. Calc Hess Law
  2. Signs of heat, H, and entropy
  3. Temperature dependence (no graph)
  4. Interpret K and DG
  5. Kinetic vs Thermo stability
  6. G all graphs
  7. Qualitative solubility
  8. Calculate the pH of any mixture ac/base
  9. Titration: find composition along the curves
  10. Stoichiometry combustion
  11. Lewis structures and vibration
  12. Rank EM radiation
fullCourseReport(m3,12,"S22","4")

3.1 M3 - Attempt 1 averages

m1df = buildDF_fromMilestone(m3,12)
# First attempt stats
results = buildStats(m1df,12,1)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 78 50 (64%) 13 (17%) 15 (19%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 8.16 7.75 7.89 7.83 7.50 8.02 7.51 6.17 8.06 8.33 7.47 8.12
Avg 70><80 7.69 7.13 6.41 7.69 5.45 7.85 5.67 1.92 7.56 3.84 6.50 7.91
Avg < 70 5.00 6.80 5.92 6.11 4.72 6.01 4.45 1.11 6.37 3.33 4.84 5.93
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 1.18 1.26 1.17 2.00 2.52 0.69 1.40 3.70 0.91 0.00 1.32 1.24
SD 70><80 2.31 1.41 2.86 2.31 3.95 1.01 2.57 3.66 1.16 4.32 1.58 1.04
SD < 70 4.22 1.33 2.32 3.81 4.13 2.02 2.74 2.93 1.63 4.22 1.93 3.46
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

3.2 M3 - Attempt 2 averages

# First attempt stats
results = buildStats(m1df,12,2)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 25 12 (48%) 6 (24%) 7 (28%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
Avg > 80 8.33 7.20 8.10 7.64 6.25 7.73 7.41 5.56 7.70 6.94 8.03 8.34
Avg 70><80 6.94 7.77 6.48 8.33 5.55 7.54 6.95 1.39 6.95 2.78 6.94 7.41
Avg < 70 4.76 6.82 5.55 5.95 3.57 7.45 5.56 0.00 6.35 2.38 5.44 7.55
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12
SD > 80 0.00 1.59 0.80 2.40 3.77 0.95 1.81 4.11 1.60 3.24 0.54 0.00
SD 70><80 3.40 1.01 2.27 0.00 4.30 1.23 1.52 3.40 2.33 4.30 1.39 2.27
SD < 70 4.45 2.19 2.78 4.06 4.45 1.28 3.21 0.00 2.76 4.06 1.66 2.10
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 2nd attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

4 Milestone 4

List of questions

  1. Balance redox reactions
  2. Parts of a battery
  3. Nernst: adding things to cathode and anode
  4. Calculate DeltaG from E
  5. Quantiative Electrodeposition
  6. ID products of electrolysis
  7. Organic redox
  8. Signs of heat, H, and S
  9. Quantitative solubility
  10. All titrations: composition along the curve
  11. Predict pH of any acid/base mixture
  12. Predict acid, base or neutral
  13. Solution preparation
  14. Ions in water

Number of students in the three grade categories at different attempts

fullCourseReport(m4,14,"S22","4")

4.1 M4 - Attempt 1 averages

m1df = buildDF_fromMilestone(m4,14)
# First attempt stats
results = buildStats(m1df,14,1)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 76 32 (42%) 19 (25%) 25 (33%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
Avg > 80 6.60 7.08 6.47 6.25 6.47 7.04 6.41 6.68 5.13 6.14 6.25 6.64 6.92 6.34
Avg 70><80 6.44 6.95 6.31 6.01 5.26 3.76 5.14 6.57 3.01 4.89 3.76 6.14 5.26 5.15
Avg < 70 3.92 6.00 5.77 3.14 2.57 2.43 3.81 6.12 1.14 3.14 2.57 5.17 3.71 2.77
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
SD > 80 1.07 0.22 0.96 2.40 2.11 0.63 1.28 1.09 3.26 2.26 2.40 0.63 1.26 1.30
SD 70><80 0.68 0.48 1.20 2.67 3.23 3.67 2.28 1.26 3.62 3.20 3.66 1.05 3.23 2.14
SD < 70 2.26 1.33 1.33 3.62 3.50 3.22 2.75 1.47 2.67 3.15 3.50 1.40 3.64 2.35
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

4.2 Variation within question groups

Q6

results = variationWithinQuestionTypes(m4,"q6_")
indivQ = results$indi
aveQ = results$ave

boxplot(indivQ)
library("psych")

knitr::kable(describe(indivQ))
vars n mean sd median trimmed mad min max range skew kurtosis se
q6_1 1 9 5.163889 3.152854 7.150 5.163889 0.000000 0.000 7.15 7.150 -0.8239226 -1.2887377 1.0509512
q6_2 2 15 3.575000 3.309807 3.575 3.575000 5.300295 0.000 7.15 7.150 0.0000000 -1.9111111 0.8545885
q6_3 3 10 5.720000 2.499661 7.150 6.256250 0.000000 0.000 7.15 7.150 -1.1935618 -0.0746281 0.7904622
q6_4 4 11 4.550000 3.607354 7.150 4.766667 0.000000 0.000 7.15 7.150 -0.4914204 -1.9079103 1.0876580
q6_5 5 12 4.766667 3.520417 7.150 5.005000 0.000000 0.000 7.15 7.150 -0.6205863 -1.7395833 1.0162567
q6_6 6 13 6.325000 2.141942 7.150 6.825000 0.000000 0.000 7.15 7.150 -2.0951253 3.0764400 0.5940679
q6_7 7 5 5.720000 1.958108 7.150 5.720000 0.000000 3.575 7.15 3.575 -0.2921187 -2.2533333 0.8756926
q6_8 8 7 5.107143 3.488843 7.150 5.107143 0.000000 0.000 7.15 7.150 -0.7528372 -1.6040816 1.3186586
q6_9 9 9 5.561111 3.152854 7.150 5.561111 0.000000 0.000 7.15 7.150 -1.1198947 -0.7989418 1.0509512
q6_10 10 9 3.972222 3.317460 3.575 3.972222 5.300295 0.000 7.15 7.150 -0.1819657 -1.9406097 1.1058199
q6_11 11 7 5.107143 3.488843 7.150 5.107143 0.000000 0.000 7.15 7.150 -0.7528372 -1.6040816 1.3186586
q6_12 12 11 5.200000 3.339761 7.150 5.561111 0.000000 0.000 7.15 7.150 -0.8846579 -1.3126722 1.0069757
q6_13 13 9 4.766667 3.575000 7.150 4.766667 0.000000 0.000 7.15 7.150 -0.5925926 -1.8148148 1.1916667
q6_14 14 5 2.860000 3.916216 0.000 2.860000 0.000000 0.000 7.15 7.150 0.2921187 -2.2533333 1.7513852

Q9

results = variationWithinQuestionTypes(m4,"q9_")
indivQ = results$indi
aveQ = results$ave

boxplot(indivQ)

library("psych")
knitr::kable(describe(indivQ))
vars n mean sd median trimmed mad min max range skew kurtosis se
q9_1 1 26 4.393846 3.542432 7.14 4.543636 0.000000 0 7.14 7.14 -0.4472406 -1.867419 0.6947281
q9_2 2 25 3.141600 3.617287 0.00 3.060000 0.000000 0 7.14 7.14 0.2273881 -2.024540 0.7234574
q9_3 3 18 3.570000 3.673500 3.57 3.570000 5.292882 0 7.14 7.14 0.0000000 -2.108025 0.8658522
q9_4 4 21 3.400000 3.654012 0.00 3.360000 0.000000 0 7.14 7.14 0.0886175 -2.084725 0.7973707
q9_5 5 16 3.570000 3.687080 3.57 3.570000 5.292882 0 7.14 7.14 0.0000000 -2.121094 0.9217700
q9_6 6 26 2.746154 3.542432 0.00 2.596364 0.000000 0 7.14 7.14 0.4472406 -1.867419 0.6947281

4.3 M4 - Attempt 2 averages

m1df = buildDF_fromMilestone(m4,14)
# First attempt stats
results = buildStats(m1df,14,2)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
Two 42 18 (43%) 14 (33%) 10 (24%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
Avg > 80 6.18 7.14 6.66 5.95 6.74 4.97 6.22 6.55 5.95 6.35 6.74 6.26 6.35 6.63
Avg 70><80 5.83 6.88 6.63 5.10 6.63 5.36 5.96 6.23 2.04 4.34 4.59 6.01 4.08 4.77
Avg < 70 5.10 5.89 5.43 5.71 4.28 2.86 2.86 5.78 1.43 5.35 3.57 4.92 3.57 3.82
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
SD > 80 1.46 0.00 0.98 2.74 1.68 3.28 1.66 1.06 2.74 1.96 1.68 1.24 2.31 1.02
SD 70><80 1.23 0.55 1.20 3.35 1.91 2.72 2.04 1.76 3.35 3.48 3.55 0.86 3.67 2.29
SD < 70 1.80 1.79 2.00 3.01 3.69 3.29 2.93 1.44 3.01 2.52 3.76 0.90 3.76 2.01
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

5 Milestone 5

List of questions

  1. Nernst: adding things to cathode and anode
  2. ID products of electrolysis
  3. Organic redox

Number of students in the three grade categories at different attempts

fullCourseReport(m5,14,"S22","5")

5.1 M5 - Attempt 1 averages

m1df = buildDF_fromMilestone(m5,14)
# First attempt stats
results = buildStats(m1df,14,1)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
One 73 43 (59%) 16 (22%) 14 (19%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
Avg > 80 6.76 6.90 6.15 6.92 6.40 7.02 6.31 6.89 6.84 6.48 6.31 6.92 5.48 6.72
Avg 70><80 5.51 7.06 4.47 6.25 5.27 6.92 5.80 3.79 6.34 5.21 4.46 4.09 4.02 5.81
Avg < 70 4.57 5.34 1.53 4.42 3.76 5.67 1.53 2.29 5.20 4.59 4.21 4.42 1.53 5.62
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
SD > 80 1.06 0.96 2.51 0.87 1.34 0.31 2.32 1.21 0.67 1.31 1.57 0.65 3.05 0.63
SD 70><80 2.30 0.36 3.58 1.19 1.84 0.40 2.88 3.56 1.16 2.49 2.52 3.10 3.66 0.90
SD < 70 2.52 1.98 3.04 1.58 2.23 1.84 3.04 3.32 1.74 2.37 2.39 2.93 3.04 0.83
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))

5.2 Variation within question groups

Q7

results = variationWithinQuestionTypes(m5,"q7_")
indivQ = results$indi
aveQ = results$ave

boxplot(indivQ)

library("psych")
knitr::kable(describe(indivQ))
vars n mean sd median trimmed mad min max range skew kurtosis se
q7_1 1 32 4.016250 3.598675 7.14 4.119231 0 0 7.14 7.14 -0.2402577 -2.0019376 0.6361619
q7_2 2 26 5.766923 2.869698 7.14 6.166364 0 0 7.14 7.14 -1.4722289 0.1787123 0.5627941
q7_3 3 25 5.712000 2.914893 7.14 6.120000 0 0 7.14 7.14 -1.4109061 -0.0048000 0.5829786
q7_4 4 33 4.543636 3.487920 7.14 4.760000 0 0 7.14 7.14 -0.5413726 -1.7574446 0.6071689

Q3

results = variationWithinQuestionTypes(m5,"q3_")
indivQ = results$indi
aveQ = results$ave

boxplot(indivQ)

library("psych")
knitr::kable(describe(indivQ))
vars n mean sd median trimmed mad min max range skew kurtosis se
q3_4 1 31 3.690323 3.632204 7.15 3.718000 0 0 7.15 7.15 -0.0614517 -2.059573 0.6523630
q3_1 2 30 5.243333 3.215902 7.15 5.660417 0 0 7.15 7.15 -1.0029674 -1.024924 0.5871406
q3_2 3 32 4.915625 3.367143 7.15 5.225000 0 0 7.15 7.15 -0.7714139 -1.447248 0.5952324
q3_3 4 23 4.663043 3.481941 7.15 4.892105 0 0 7.15 7.15 -0.5977914 -1.711468 0.7260349

5.3 M5 - Attempt 2 averages

m1df = buildDF_fromMilestone(m5,14)
# First attempt stats
results = buildStats(m1df,14,2)
m1dfat1 = results$ave
m1dfat1sd = results$sd
Attempt Total students Sudents with score > 80 Sudents with score 70-80 Students with score < 70%
Two 29 14 (48%) 4 (14%) 11 (38%)

Table of averages per question and their standard deviation

library(knitr)
knitr::kable(m1dfat1,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
Avg > 80 6.98 6.89 6.13 6.46 5.47 7.01 6.63 7.14 6.63 6.12 5.48 5.86 4.59 6.49
Avg 70><80 6.06 6.70 3.58 7.14 5.80 6.69 5.35 1.78 5.00 4.76 4.46 4.76 5.35 6.44
Avg < 70 6.43 6.73 0.65 5.84 5.53 6.82 1.30 3.25 5.97 5.19 2.43 4.65 1.30 6.31
knitr::kable(m1dfat1sd,digits=2)
q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14
SD > 80 0.46 0.65 2.60 1.45 1.71 0.32 1.91 0.00 0.71 2.23 2.47 2.58 3.55 1.03
SD 70><80 1.70 0.89 4.13 0.00 0.89 0.89 3.57 3.57 2.47 1.94 2.30 3.37 3.57 1.01
SD < 70 1.35 0.97 2.16 1.24 1.20 0.45 2.89 3.73 1.07 2.78 2.43 3.12 2.89 0.77
matplot(t(m1dfat1),type="b",ylim=c(0,9),xlab = "Question number",ylab = "Average score/8.33",main="Average score per question on 1st attempt")   
legend("bottomright",legend = c("1:>80","2:70-80","3:<70"))