0

I have data on students exam results. Each student takes a 100 question exam. Each question has been allocated (unequally) to one of 5 domains and also has a unique learning outcome associated with it. I need to summarise the data in a table (likely spread over multiple pdf pages), identifying for each domain, the learning outcomes the student got correct/incorrect.

For each Domain I have managed to get a table which shows the the learning outcomes the student got correct/incorrect, but I can't figure out how to join the tables together after the loop. I'd really appreciate some help with this...

Please find below a copy of my code:

library(tidyverse)
library(dplyr)

library(gridExtra)
library(grid)
library(gtable)
library(ids)

# Create some random Learning Objectives:
LO <- ids::adjective_animal(2500, 2, style = "sentence")

# Create data:
testdata <- data.frame(cbind(StudentID=rep(1:25,each=100),
                             QuestionNumber=rep(1:100),
                             Correct=sample(c(0,1),replace=TRUE,size=2500),
                             Domain=c("A","A","A","B","C","C","C","D","D","E"),
                             LearningOutcome=LO))
rm(LO)

# Create lists for Loops:
DomainList <- unique(testdata$Domain)
StudentList <- unique(testdata$StudentID)

Domain.Colours <- list("steelblue3","red2","violetred3","forestgreen","chocolate1")


for (i in 1:length(unique(testdata$StudentID))){  
  
  for (j in 1:length(DomainList)){
    
    # Select Domain Specfic Data
    TEMP.DatabyDomain <- testdata[testdata$Domain==DomainList[j],]
    
    # Split Learning Outcomes into Correct/Incorrect Lists
    TEMP.Correct   <- TEMP.DatabyDomain$LearningOutcome[TEMP.DatabyDomain$StudentID == StudentList[i] & TEMP.DatabyDomain$Correct == 1]
    TEMP.Incorrect <- TEMP.DatabyDomain$LearningOutcome[TEMP.DatabyDomain$StudentID == StudentList[i] & TEMP.DatabyDomain$Correct == 0]
    
    # Update the lengths to be the same
    n <- max(length(TEMP.Correct), length(TEMP.Incorrect))
    length(TEMP.Correct) <- n                      
    length(TEMP.Incorrect) <- n
    
    # Combine the data into a new df
    FeedbackData <- data.frame(TEMP.Correct, TEMP.Incorrect)
    FeedbackData <- sapply(FeedbackData, as.character)
    
    # Replace NAs with ""
    FeedbackData[is.na(FeedbackData)] <- " "
    
    # Create column headings
    colnames(FeedbackData) <- c(paste("Correctly answered questions\n in domain:",DomainList[j]),paste("Incorrectly answered questions\n in domain:",DomainList[j]))
    
    # Table Settings
    tt1 <- ttheme_default(core=list(fg_params=list(fontsize=8)),
                          rowhead=list(fg_params=list(hjust=0, x=0)),
                          colhead=list(bg_params=list(fill=paste(Domain.Colours[j])),
                                       fg_params=list(col="white")))
    
    # Table Results
    tfeedback <- tableGrob(FeedbackData, theme=tt1, rows = NULL)
    
    }
    
    ### Here is where I get stuck ###
    # I would like to append the 5 Domain tables together, so that they are one long table with the column headings in-between
  
    tfeedbacktitle <- textGrob(paste("Feedback for Students"),gp=gpar(fontsize=20))
    padding <- unit(10,"mm")

    table <- gtable_add_rows(
      tfeedback, 
      heights = grobHeight(tfeedbacktitle) + padding,
      pos = 0)
    table <- gtable_add_grob(
      table, 
      tfeedbacktitle, 
      1, 1, 1, ncol(table))  

    # Code below to allow table to cover multiple pages
    # Taken from <https://stackoverflow.com/questions/15937131/print-to-pdf-file-using-grid-table-in-r-too-many-rows-to-fit-on-one-page>
    fullheight <- convertHeight(sum(table$heights), "cm", valueOnly = TRUE)
    margin <- unit(0,"in")
    margin_cm <- convertHeight(margin, "cm", valueOnly = TRUE)
    a4height <- 29.7 - margin_cm
    nrows <- nrow(table)
    npages <- ceiling(fullheight / a4height)
    
    heights <- convertHeight(table$heights, "cm", valueOnly = TRUE) 
    rows <- cut(cumsum(heights), include.lowest = FALSE,
                breaks = c(0, cumsum(rep(a4height, npages))))
    
    groups <- split(seq_len(nrows), rows)
    
    gl1 <- lapply(groups, function(id) table[id,])
    
    mypath <- file.path(pathOutput,paste("StudentID_", StudentList[i], ".pdf", sep = ""))
    
    pdf(file=mypath, paper = "a4", width = 0, height = 0)
    for(page in seq_len(npages)){
      grid.newpage()
      grid.rect(width=unit(21,"cm") - margin,
                height=unit(29.7,"cm")- margin)
      grid.draw(gl1[[page]])
    }
    
    dev.off()
}

The link shows a picture of what I hope to achieve: Example Final Result

VikkiO
  • 11
  • 3
  • Sorry Ronak, I tried to keep the minimum I could so that you can see what I need to do. There is testdata generated in the code. – VikkiO Apr 03 '21 at 07:38
  • The code produces a single pdf file per student, but it only contains 1 of the Domain tables. I would like each students' pdf to contain all of the tables, one on top of the other, across as many pages as needs. I've now added an example picture to my post. – VikkiO Apr 03 '21 at 21:55
  • I think you may want to read this SO question - the answer should help you https://stackoverflow.com/questions/36373630/how-to-create-a-loop-that-includes-both-a-code-chunk-and-text-with-knitr-in-r – jessi Apr 03 '21 at 23:14
  • Thanks Jessi, but I can't figure out how that would help me to combine the tfeedback tables together to make 1 table. – VikkiO Apr 04 '21 at 16:54
  • oh...I thought you wanted to have each feedback table (for as many tables as it took) with headings. – jessi Apr 04 '21 at 17:11

0 Answers0