Introduction
Moodle, a learning management system, store data related to quiz taking in Moodle Quiz Report which has 4 types: Grades report, Responses report, Statistic report, and Manual grading.
moodleQuiz package have a functions that operate specifically on 2 types of report: Grades report and Responses report. The notable difference between the 2 are:
Grades report has columns started with
Q. x /x
, these columns store score of each questions.Responses report has columns started with
Response x
, these columns store raw text responses of each questions.
The rest of the columns are the same for these 2 reports:
Surname
: student’s last name,First name
: student’s first nameInstitution
: student’s institution,Department
: student’s departmentEmail address
: student’s Email (often, an educational account)State
: the state of the attempt usually have 2 values, one of: “Finished” for submitted attempt, and “In progress” for ongoing not-yet-submitted attempt.Started on
andCompleted
are the time point at which student started and submitted the quiz.Time taken
is a duration from started to submitted the quiz.Grade/xx
: total student’s score (if present) of the quiz.
Getting Data
Grades report and Responses report can be downloaded from Moodle server in various formats (e.g., .csv
, .xlsx
, .json
). You have to find a way to import that report in to R
as a data frame or list of data frames, which is the data structure in which functions in moodleQuiz package will operate on.
Once you imported the report into R
, do not format or clean anything prior to passing into the functions that I will introduce to you next.
Example Data
moodleQuiz package comes with example data of Grade reports and Responses reports.
Grades Reports
grades_ls
is a list containing 3 data frames of Moodle Grades Report.
class(grades_ls)
#> [1] "list"
Each elements are data frames of Grade reports from 3 different quiz that the same group of students have completed.
names(grades_ls)
#> [1] "Quiz_1" "Quiz_2" "Quiz_3"
For example, this is the Grade report of Quiz_1
head(grades_ls$Quiz_1)
#> # A tibble: 6 × 18
#> Surname First…¹ Insti…² Depar…³ Email…⁴ State Start…⁵ Time …⁶ Compl…⁷ Grade…⁸
#> <chr> <chr> <lgl> <lgl> <chr> <chr> <chr> <chr> <chr> <dbl>
#> 1 Roquemo… Jada NA NA u017@e… Fini… 2 June… 16 min… 2 June… 9.47
#> 2 Ali Ronin NA NA u001@e… Fini… 2 June… 17 min… 2 June… 9.74
#> 3 Hoffpau… Jerry NA NA u002@e… Fini… 2 June… 11 min… 2 June… 9.47
#> 4 Babbitt Nathan NA NA u026@e… Fini… 2 June… 11 min… 2 June… 10
#> 5 Huynh Rohith NA NA u024@e… Fini… 2 June… 14 min… 2 June… 10
#> 6 Ellis Joy NA NA u013@e… Fini… 2 June… 10 min… 2 June… 10
#> # … with 8 more variables: `Q. 1 /0.79` <dbl>, `Q. 2 /0.26` <dbl>,
#> # `Q. 3 /2.37` <dbl>, `Q. 4 /2.11` <dbl>, `Q. 5 /2.11` <dbl>,
#> # `Q. 6 /0.26` <dbl>, `Q. 7 /1.05` <chr>, `Q. 8 /1.05` <dbl>, and abbreviated
#> # variable names ¹`First name`, ²Institution, ³Department, ⁴`Email address`,
#> # ⁵`Started on`, ⁶`Time taken`, ⁷Completed, ⁸`Grade/10.00`
Notice the last row here which is an “Overall average” that Moodle calculated for us.
grades_ls$Quiz_1 %>%
select(Surname, starts_with("G"), starts_with("Q")) %>%
tail(1)
#> # A tibble: 1 × 10
#> Surname Grade…¹ Q. 1 …² Q. 2 …³ Q. 3 …⁴ Q. 4 …⁵ Q. 5 …⁶ Q. 6 …⁷ Q. 7 …⁸
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 Overall avera… 9.55 0.64 0.26 2.28 2.02 2.1 0.25 0.95
#> # … with 1 more variable: `Q. 8 /1.05` <dbl>, and abbreviated variable names
#> # ¹`Grade/10.00`, ²`Q. 1 /0.79`, ³`Q. 2 /0.26`, ⁴`Q. 3 /2.37`, ⁵`Q. 4 /2.11`,
#> # ⁶`Q. 5 /2.11`, ⁷`Q. 6 /0.26`, ⁸`Q. 7 /1.05`
Responses Report
responses_ls
is a list containing 2 data frames of Moodle Responses Report. Each data frame is a quiz that the same group of students have done.
names(responses_ls)
#> [1] "Quiz_1" "Quiz_2"
head(responses_ls$Quiz_1)
#> # A tibble: 6 × 12
#> Surname First…¹ Insti…² Depar…³ Email…⁴ State Start…⁵ Time …⁶ Compl…⁷ Grade…⁸
#> <chr> <chr> <lgl> <lgl> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Xiong Lance NA NA u004@e… Fini… 19 Apr… 11 min… 19 Apr… Not ye…
#> 2 Halamic… Sydney NA NA u002@e… Fini… 19 Apr… 15 min… 19 Apr… Not ye…
#> 3 Palomino Itzel NA NA u016@e… Fini… 19 Apr… 19 min… 19 Apr… Not ye…
#> 4 el-Faro… Hakam NA NA u001@e… Fini… 19 Apr… 12 min… 19 Apr… Not ye…
#> 5 al-Farha Hamood NA NA u012@e… Fini… 19 Apr… 12 min… 19 Apr… Not ye…
#> 6 Solis Domini… NA NA u007@e… Fini… 19 Apr… 14 min… 19 Apr… Not ye…
#> # … with 2 more variables: `Response 1` <chr>, `Response 2` <chr>, and
#> # abbreviated variable names ¹`First name`, ²Institution, ³Department,
#> # ⁴`Email address`, ⁵`Started on`, ⁶`Time taken`, ⁷Completed, ⁸`Grade/2.00`
Check Submission
check_sub()
check student submission from state
column of Grades or Responses report.
Calling this function would
Clean and format column names
Encode
State
column resulting inEncode
column, “Finished” attempt encoded as 1, and “In progress” attempt encoded as 0.Filter rows by time and encoding. By default, It choose the first maximum score of each student.
Check Submission of Quiz 1
grades_ls$Quiz_1 %>%
check_sub(id_regex = "[:digit:]+") %>%
select(Name, ID, State, Encode)
#> # A tibble: 26 × 4
#> Name ID State Encode
#> <chr> <chr> <chr> <dbl>
#> 1 Jada Roquemore 017 Finished 1
#> 2 Ronin Ali 001 Finished 1
#> 3 Jerry Hoffpauir 002 Finished 1
#> 4 Nathan Babbitt 026 Finished 1
#> 5 Rohith Huynh 024 Finished 1
#> 6 Joy Ellis 013 Finished 1
#> 7 Jeremy Nelson 023 Finished 1
#> 8 Christopher Dillon 003 Finished 1
#> 9 Israel Munoz 018 Finished 1
#> 10 Zubaida al-Attar 022 Finished 1
#> # … with 16 more rows
Check Submission of All Quizzes
You can check whether student submit multiple quizzes by passing a list of data frames as show here as grades_ls
.
grades_ls %>%
check_sub(id_regex = "[:digit:]+") %>%
select(Name, ID, ends_with("Encode"), Total)
#> # A tibble: 26 × 6
#> Name ID Quiz_1_Encode Quiz_2_Encode Quiz_3_Encode Total
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 Jada Roquemore 017 1 1 1 3
#> 2 Ronin Ali 001 1 1 1 3
#> 3 Jerry Hoffpauir 002 1 1 1 3
#> 4 Nathan Babbitt 026 1 1 1 3
#> 5 Rohith Huynh 024 1 1 1 3
#> 6 Joy Ellis 013 1 1 1 3
#> 7 Jeremy Nelson 023 1 1 1 3
#> 8 Christopher Dillon 003 1 1 1 3
#> 9 Israel Munoz 018 1 1 1 3
#> 10 Zubaida al-Attar 022 1 1 1 3
#> # … with 16 more rows
Notice that the name of each data frame (e.g., “Quiz_1”) are prefix in the column names, and Total
column is calculated by sum of encoding by each student in each quiz.
Now you can check whether students submit each quizzes in a course and how many they missed by just calling one function!
Combine Responses
combine_resp()
combine student’s responses (Response x
) from the Responses report.
Calling this function would
Clean and format column names
Filter rows by time and encoding. By default, It choose the first maximum score of each student.
Return data frame with
Response_x
columns
Combine Response of Quiz 1
This would get similar result to check_sub()
, except it also return Response_*
column and has an ability to split Cloze columns.
responses_ls$Quiz_1 %>%
combine_resp(id_regex = "[:digit:]+") %>%
select(Name, ID, starts_with("R"))
#> # A tibble: 26 × 4
#> Name ID Response_1 Respo…¹
#> <chr> <chr> <chr> <chr>
#> 1 Lance Xiong 004 intuitively singled out as the instigator of … she re…
#> 2 Sydney Halamicek 002 Tarzan’s face was a pathetic expression of di… the me…
#> 3 Itzel Palomino 016 not to harm you if you will return our ivory … -
#> 4 Hakam el-Farooq 001 “A derelict,” was the terse explanation of th… “I fea…
#> 5 Hamood al-Farha 012 At last they passed through two great walls a… Findin…
#> 6 Dominique Solis 007 the powers of the ape-man and overestimated t… though…
#> 7 Zhjade Alarid 009 upon the unsuspecting woman. A faint rustling… and fo…
#> 8 Jordan Tan 013 “In time to save you from death upon the alta… warrio…
#> 9 Kevin Vasquez 025 A FRIEND. Thus, …
#> 10 Angela Carrillo 018 very faithful and loyal wife, but as she had … “they …
#> # … with 16 more rows, and abbreviated variable name ¹Response_2
Combine Response of All Quizzes
Calling combine_resp()
on a list of data frame would get all responses of each students combined in 1 data frame.
responses_ls %>%
combine_resp(id_regex = "[:digit:]+") %>%
select(Name, ID, contains("R")) %>%
glimpse()
#> Rows: 26
#> Columns: 7
#> $ Name <chr> "Lance Xiong", "Sydney Halamicek", "Itzel Palomino",…
#> $ ID <chr> "004", "002", "016", "001", "012", "007", "009", "01…
#> $ Quiz_1_Response_1 <chr> "intuitively singled out as the instigator of the ou…
#> $ Quiz_1_Response_2 <chr> "she replied. “The thing that has just happened has …
#> $ Quiz_2_Response_1 <chr> "The next day Thuran was worse. Almost constantly he…
#> $ Quiz_2_Response_2 <chr> "you insisted it would have been too late to have re…
#> $ Quiz_2_Response_3 <chr> "While Monsieur Flaubert spoke Tarzan selected a cig…
Notice that the name of each data frame (e.g., “Quiz_1”) are prefix in the column names.
Count Responses
count_resp()
counts how many responses each student answered from the Responses report.
Calling this function would
Clean and format column names
Filter rows by time and encoding. By default, It choose the first maximum score of each student.
Return data frame with
Count_Resp_x
columns
In contrast to combine_resp()
, count_resp()
returns counts of how many responses do each students answered.
Beware that count_resp()
counts any non-blank answers that is not -
(dash) as an answers, even though it is not appropriate to the question.
Count Responses of Quiz 1
responses_ls$Quiz_1 %>%
count_resp(id_regex = "[:digit:]+") %>%
select(Name, ID, starts_with("Count"))
#> # A tibble: 26 × 3
#> Name ID Count_Resp_2
#> <chr> <chr> <int>
#> 1 Lance Xiong 004 2
#> 2 Sydney Halamicek 002 2
#> 3 Itzel Palomino 016 1
#> 4 Hakam el-Farooq 001 2
#> 5 Hamood al-Farha 012 2
#> 6 Dominique Solis 007 2
#> 7 Zhjade Alarid 009 2
#> 8 Jordan Tan 013 2
#> 9 Kevin Vasquez 025 2
#> 10 Angela Carrillo 018 2
#> # … with 16 more rows
Count Responses from All Quizzes
Calling count_resp()
on a list of data frame would yields counts of responses for all quizzes and the total counts is calculated.
responses_ls %>%
count_resp(id_regex = "[:digit:]+") %>%
select(Name, ID, contains("Count"), starts_with("Total"))
#> # A tibble: 26 × 5
#> Name ID Quiz_1_Count_Resp_2 Quiz_2_Count_Resp_3 Total_5
#> <chr> <chr> <int> <int> <int>
#> 1 Lance Xiong 004 2 3 5
#> 2 Sydney Halamicek 002 2 3 5
#> 3 Itzel Palomino 016 1 3 4
#> 4 Hakam el-Farooq 001 2 3 5
#> 5 Hamood al-Farha 012 2 3 5
#> 6 Dominique Solis 007 2 3 5
#> 7 Zhjade Alarid 009 2 2 4
#> 8 Jordan Tan 013 2 2 4
#> 9 Kevin Vasquez 025 2 3 5
#> 10 Angela Carrillo 018 2 2 4
#> # … with 16 more rows
Using count_resp()
to checking response counts can be a great way to check whether student did the quiz as expected, since the checking is done per questions, while check_sub()
only check for student’s submission of the quiz.
Interestingly, If the student did every questions from the quiz and forgot to press the “Submit all and finished” button, the result from check_sub()
would shows that student didn’t do the quiz (“In progress”), while count_resp()
can reveal that student do the quiz with that many questions answered.
Combine Grades
combine_grades()
filter, adjust, and combine student’s grade from Grade/xx
column.
Calling this function would
Clean and format column names
Filter rows by time and grade. By default, It choose the first maximum score of each student.
Return data frame with
Grade_x
columns (and may beQx
)
Combine & Adjust Grades of Quiz 1
You can filter grade by choose_grade
parameter and readjust maximum grade to any score by new_max_grade
parameter.
grades_ls$Quiz_1 %>%
combine_grades(
id_regex = "[:digit:]+",
choose_grade = "max", # Choose only maximum grade of each student
new_max_grade = 100 # Adjust maximum grade to 100
) %>%
select(Name, ID, starts_with("G"), starts_with("Q"))
#> # A tibble: 26 × 11
#> Name ID Grade…¹ Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Jada Roquemore 017 94.7 2.6 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 2 Ronin Ali 001 97.4 7.9 2.6 23.7 21.1 21.1 2.6 7.9 10.5
#> 3 Jerry Hoffpauir 002 94.7 2.6 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 4 Nathan Babbitt 026 100 7.9 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 5 Rohith Huynh 024 100 7.9 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 6 Joy Ellis 013 100 7.9 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 7 Jeremy Nelson 023 92.1 7.9 2.6 21.1 15.8 21.1 2.6 10.5 10.5
#> 8 Christopher Di… 003 94.7 2.6 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 9 Israel Munoz 018 100 7.9 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> 10 Zubaida al-Att… 022 100 7.9 2.6 23.7 21.1 21.1 2.6 10.5 10.5
#> # … with 16 more rows, and abbreviated variable name ¹Grade_100
Combine Grades for All Quizzes
Calling combine_grades()
on list of data frame would combine the score of each quizzes from each student in to 1 data frame, and Total_x
column will be added as a sum of student’s score.
You can adjust the maximum score of each individual quizzes by input new_max_grade
as a numeric vector corresponding to each quiz. In this example, I will adjust the maximum score of “Quiz_1” and “Quiz_2” to 25, and “Quiz_3” to 50.
grades_ls %>%
combine_grades(
id_regex = "[:digit:]+",
choose_grade = "max", # Choose only maximum grade of each student
new_max_grade = c(25, 25, 50) # Maximum score of each quizzes
) %>%
select(Name, ID, contains("Grade"), starts_with("Total")) %>%
glimpse()
#> Rows: 26
#> Columns: 6
#> $ Name <chr> "Jada Roquemore", "Ronin Ali", "Jerry Hoffpauir", "Nat…
#> $ ID <chr> "017", "001", "002", "026", "024", "013", "023", "003"…
#> $ Quiz_1_Grade_25 <dbl> 23.675, 24.350, 23.675, 25.000, 25.000, 25.000, 23.025…
#> $ Quiz_2_Grade_25 <dbl> 19.425, 20.350, 16.650, 18.500, 25.000, 13.875, 25.000…
#> $ Quiz_3_Grade_50 <dbl> 49.95, 46.40, 49.95, 49.95, 49.95, 49.95, 46.40, 49.95…
#> $ Total_100 <dbl> 93.050, 91.100, 90.275, 93.450, 99.950, 88.825, 94.425…
Last updated: 2022-09-23