knitr::opts_chunk$set( echo = TRUE, fig.width = 6, fig.asp = 0.618, out.width = "70%", fig.align = "center", warning = FALSE, message = FALSE ) library(tidyverse)
This is done with
remotes::install_github("jonotuke/examMarking")
If you do not have the package remotes installed, then use
install.packages("remotes")
library(tidyverse) library(examMarking)
There is some in-built data to play with. This is the anonomised SMI 2018 data.
data("SMI_2018_marks") SMI_2018_marks
This is the form that you get from canvas. We have also added the marks for the exam questions columns Q1 to Q6.
Canvas sets any mark that the students did not submit as NA, and exemptions as EX.
SMI_2018_marks %>% filter(str_detect(`Assignment 1 (77417)`, "EX"))
With this data, we have 5 assignments, then the project, then 8 online quizzes, and finally the exam questions. We will rename this to make cleaning easier.
colnames(SMI_2018_marks) <- c("ID", str_c("A", 1:5), "P", str_c("OQ0", 1:8), str_c("Q", 1:6))
We can convert missing to zero and EX to NA with the following:
SMI_2018_marks SMI_2018_marks <- SMI_2018_marks %>% clean_marks_df("^A") %>% clean_marks_df("^P$") %>% clean_marks_df("^OQ") %>% clean_marks_df("^Q")
Note that we can use regular expressions to give the columns that need cleaning.
For each assessment piece, we can calculate the proportions. This assumes that there is a row with the totals. In canvas, this is the first row.
SMI_2018_marks <- SMI_2018_marks %>% get_prop_df("^A", total_row = 1) %>% # Assignments get_prop_df("^P$", total_row = 1) %>% # project get_prop_df("^OQ", total_row = 1) # Online quizzes SMI_2018_marks
For the exam, we need to exam total, we obtain this by adding the question columns
SMI_2018_marks$E <- SMI_2018_marks %>% get_total("^Q")
And then convert this to a proportion and the exam questions.
SMI_2018_marks <- SMI_2018_marks %>% get_prop_df("^E$|^Q", total_row = 1) SMI_2018_marks
For each assessment part - assignments and online quizzes, we get the average proportion
SMI_2018_marks$A <- SMI_2018_marks %>% get_mean("^A") SMI_2018_marks$OQ <- SMI_2018_marks %>% get_mean("^OQ")
So we now have the proportion for the various parts of the course. We need to convert this to proportion of the assessment.
SMI_2018_marks %>% select(ID, A, OQ, P, E)
SMI_2018_marks <- SMI_2018_marks %>% mutate( A = A * 15, OQ = OQ * 5, P = P * 10, E = E * 70 )
SMI_2018_marks %>% select(ID, A, OQ, P, E)
SMI_2018_marks$Total <- SMI_2018_marks %>% get_total("^A$|^OQ$|^P$|^E$")
SMI_2018_marks %>% select(ID, A, OQ, P, E, Total)
SMI_2018_marks$grade <- get_grades(SMI_2018_marks$Total, trace = TRUE)
SMI_2018_marks %>% select(ID, A, OQ, P, E, Total, grade)
Finally we can get rid of the totals column
SMI_2018_marks <- SMI_2018_marks[-1, ] SMI_2018_marks
We can get a summary of each grade for the grade roster summary with
get_grade_summary(SMI_2018_marks$grade)
First we can look at individual students breakdown for each section of the course with the plot_assessment()
plot.
SMI_2018_marks %>% plot_assessment(RE = "^A$|^OQ$|^P$|^E$", name = ID, grade = grade)
To assess if the exam worked, then we can look at the separation for each question and also the average mark.
SMI_2018_marks %>% profile_plot(RE = "^Q", grade = grade)
This can also be used for other parts of the course
SMI_2018_marks %>% profile_plot(RE = "^A\\d", grade = grade)
This normalisation works on the logit scale
tibble(raw = 0:100, norm = norm_glonek(raw, a = 0.5, b = 2)) %>% ggplot(aes(raw, norm)) + geom_point()
Applying this to the SMI dataset we get
SMI_2018_marks <- SMI_2018_marks %>% mutate(norm = norm_glonek(Total, a = 0.2, b = 2))
We can compare the raw and norm
norm_compare(SMI_2018_marks$Total, SMI_2018_marks$norm)
df <- tibble( total = 1:100 ) df <- df %>% mutate(norm_total = add_mccann_spike(total)) df %>% ggplot(aes(total, norm_total)) + geom_point() norm_compare(df$total, df$norm_total)
The function fill_grade_roster()
will put the marks into the blank grade roster.
For this example, we have a grade roster
grade_roster <- read_lines("../inst/grade_example.csv") grade_roster
Also we need the totals to be rounded
SMI_2018_marks <- SMI_2018_marks %>% mutate(Total = round(Total))
fill_grade_roster(SMI_2018_marks$ID, SMI_2018_marks$Total, infile = "../inst/grade_example.csv", outfile = "../inst/example_output.csv")
grade_roster <- read_lines("../inst/example_output.csv") grade_roster
Open, check and save a correct CSV form.
This function asked for by Barry will run a audit on all students with a few marks of a boundary. The default is one mark for the boundaries 50, 65, 75, and 85.
run_audit(SMI_2018_marks, Total)
Not that you may have to restart RStudio to get this after installing hte package.
There is a template of a exam report that can be obtained from the Rmarkdown menu:
knitr::include_graphics("rmarkdown.png")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.