library(tidyverse)
library(ggthemes)
library(plyr)
library(kableExtra)
library(magrittr)
library(here)
library(ggrepel)
library(tippy)

theme_alan <- function(base_size = 12 , base_family = "")
{
  half_line <- base_size/2
  colors <- ggthemes_data$few
  gray <- colors$medium["gray"]
  black <- colors$dark["black"]
  
  theme(
    line = element_line(colour = "black", size = 0.5, linetype = 1, lineend = "butt"),
    rect = element_rect(fill = "white", 
                        colour = "black", size = 0.5, linetype = 1),
    text = element_text(family = base_family, face = "plain", colour = "black", 
                        size = base_size, lineheight = 0.9, hjust = 0.5, vjust = 0.5,
                        angle = 0, margin = margin(), debug = FALSE),
    
    axis.line = element_blank(),
    axis.line.x = NULL,
    axis.line.y = NULL, 
    axis.text = element_text(size = rel(0.8), colour = "grey30"),
    axis.text.x = element_text(margin = margin(t = 0.8 * half_line/2), vjust = 1),
    axis.text.x.top = element_text(margin = margin(b = 0.8 * half_line/2), vjust = 0),
    axis.text.y = element_text(margin = margin(r = 0.8 * half_line/2), hjust = 1),
    axis.text.y.right = element_text(margin = margin(l = 0.8 * half_line/2), hjust = 0), 
    axis.ticks = element_line(colour = "grey20"), 
    axis.ticks.length = unit(half_line/2, "pt"),
    axis.title.x = element_text(margin = margin(t = half_line), vjust = 1),
    axis.title.x.top = element_text(margin = margin(b = half_line), vjust = 0),
    axis.title.y = element_text(angle = 90, margin = margin(r = half_line), vjust = 1),
    axis.title.y.right = element_text(angle = -90, margin = margin(l = half_line), vjust = 0),
    
    legend.background = element_rect(colour = NA),
    legend.spacing = unit(0.4, "cm"), 
    legend.spacing.x = NULL, 
    legend.spacing.y = NULL,
    legend.margin = margin(0.2, 0.2, 0.2, 0.2, "cm"),
    legend.key = element_rect(fill = "white", colour = NA), 
    legend.key.size = unit(1.2, "lines"), 
    legend.key.height = NULL,
    legend.key.width = NULL,
    legend.text = element_text(size = rel(0.8)), 
    legend.text.align = NULL,
    legend.title = element_text(hjust = 0),
    legend.title.align = NULL,
    legend.position = "right", 
    legend.direction = NULL,
    legend.justification = "center", 
    legend.box = NULL,
    legend.box.margin = margin(0, 0, 0, 0, "cm"),
    legend.box.background = element_blank(),
    legend.box.spacing = unit(0.4, "cm"),
    
    panel.background = element_rect(fill = "white", colour = NA),
    panel.border = element_rect(fill = NA, colour = "grey20"),
    panel.grid.major = element_line(colour = "grey92"),
    panel.grid.minor = element_line(colour = "grey92", size = 0.25),
    panel.spacing = unit(half_line, "pt"),
    panel.spacing.x = NULL,
    panel.spacing.y = NULL,
    panel.ontop = FALSE,
    
    strip.background = element_rect(fill = "NA", colour = "NA"),
    strip.text = element_text(colour = "grey10", size = rel(0.8)),
    strip.text.x = element_text(margin = margin(t = half_line, b = half_line)),
    strip.text.y = element_text(angle = 0, margin = margin(l = half_line, r = half_line)),
    strip.placement = "inside",
    strip.placement.x = NULL, 
    strip.placement.y = NULL,
    strip.switch.pad.grid = unit(0.1, "cm"), 
    strip.switch.pad.wrap = unit(0.1, "cm"), 
    
    plot.background = element_rect(colour = "white"),
    plot.title = element_text(size = rel(1.2), hjust = 0, vjust = 1, margin = margin(b = half_line * 1.2)),
    plot.subtitle = element_text(size = rel(0.9), hjust = 0, vjust = 1, margin = margin(b = half_line * 0.9)),
    plot.caption = element_text(size = rel(0.9), hjust = 1, vjust = 1, margin = margin(t = half_line * 0.9)), 
    plot.margin = margin(half_line, half_line, half_line, half_line),
    
    complete = TRUE)
}

# Wrapper Function for Long Graph Titles
wrapper <- function(x, ...) 
{
  paste(strwrap(x, ...), collapse = "\n")
}


pd <- position_dodge(width = 0.8)       #My standard dodging for graphs


#RDFZ Reds
RDFZPink <- "#cf8f8d"
RDFZRed1 <- "#ae002b"
RDFZRed2 <- "#991815"
RDFZRed3 <- "#78011e"
RDFZRed4 <- "#4b0315"

#Grade Colors
GradeColors <- c("#8900df", "#0092df", "#00df76", "#94df00", "#d4df25", "#df5300", "#9b0f00")

#function for allowing inline code chunks to be shown verbatim
rinline <- function(code){
  html <- '<code  class="r">``` `r CODE` ```</code>'
  sub("CODE", code, html)
}
## Read in the Data File
IRGrades <- read.csv(here("GPIR.csv"))

## Make a Unique Column for Grouping
IRGrades %<>%
  subset(Criteria == "Total") %>%
  unite(Combo, Name, Teacher, sep = "-", remove = FALSE) 

## Obtain Maximum Values (total score of test)
P3MaxVal <- 35

## Summarize the Data for Each Participant
IRTotals <- 
  IRGrades %>%
    group_by(Combo) %>%
    dplyr::summarise(sum = sum(Grade)) %>%
    mutate(Grade = round(sum/P3MaxVal*100, 1)) %>%
    separate(Combo, into = c("Name", "Teacher"), sep = "-", remove = TRUE) %>%
    arrange(Teacher, -Grade) 


write.csv(IRTotals, here("GPIRTotals.csv"))
#FILL THIS IN WHEN SETTING UP THE SHEET - IT WILL HELP WITH NAMING THE TABLES AND IMAGES THAT ARE OUTPUT, SAVING YOU TIME

Class <- "AS Global Perspectives"            #Name of the Class Goes Here
Eval <- "Cambridge Individual Report"       #Name of what is being evaluated goes here

Overall Performance

#Single Histogram

OverallTitle <- paste(Class, Eval, "Histogram of Grades", sep = " - ")

ggplot(data=IRTotals, aes(x=Grade)) +
  geom_histogram(aes(y=..density..), alpha = 1, position = "identity", fill = RDFZRed2) +
  labs(x="Grade (Percentage)", y="Density") +
  stat_function(fun=dnorm, args = list(mean=mean(IRTotals$Grade), sd=sd(IRTotals$Grade)), 
                color=RDFZRed4, size   = 1.4) +
  scale_x_continuous(limits = c(0,100)) + 
  theme_alan() +
  theme(legend.position = "none") +
  ggtitle(wrapper(OverallTitle, width = 45))

ggsave(here(paste(OverallTitle, ".png", sep = "")), plot = last_plot(), device = NULL, path = NULL,
       width = 10, height = 6, units = c("in", "cm", "mm"),
       dpi = 600)

Overall performance can be seen in the graph above. We had a fairly normal distribution of grades (Shapiro-Wilk Normality test: W= 0.92, p= 0) with a mean score of 65% (Median = 68.6%) and a standard deviation of 20.9%.

The high score on the report was 34 out of 0 marks (97.1%), which was achieved by 1 student(s).

The low score on the test was 0 out of 0 marks (0%), which was achieved by 2 student(s)

Letter Grades (Raw)

Typical raw letter grade boundaries for the school can be seen in the table below:

#Note that this already includes rounding up any grade above 0.5 to the next letter grade (e.g. 79.5% = A)
Letters <- c("A*", "A", "B", "C", "D", "E", "U")
MinVal <- c(89.5, 79.5, 69.5, 59.5, 49.5, 39.5, 0)
MaxVal <- c (100, 89.49, 79.49, 69.49, 59.49, 49.49, 39.49)

LetterBoundaries <- 
  cbind.data.frame(Letters, MinVal, MaxVal) %>%
    setNames(c("Letter", "Bottom", "Top"))

LetterGradesTable <-
  LetterBoundaries  %>%
    setNames(c("Letter Grade", "Bottom Boundary", "Top Boundary")) %>%
    knitr::kable(caption = "RDFZ Letter Grade Boundaries", row.names = F) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) %>%
    footnote(general = "Note this includes rounding of grades like 79.5% up a grade boundary" )

LetterGradesTable
RDFZ Letter Grade Boundaries
Letter Grade Bottom Boundary Top Boundary
A* 89.5 100.00
A 79.5 89.49
B 69.5 79.49
C 59.5 69.49
D 49.5 59.49
E 39.5 49.49
U 0.0 39.49
Note:
Note this includes rounding of grades like 79.5% up a grade boundary

If we assign grade boundaries based on these we can see the following distribution of grades

GB1Title <- paste(Class, Eval, "Letter Grades (Raw)", sep = " - ")
  
  
IRTotals %<>%
  mutate(Letter = cut(x= IRTotals$Grade, 
                      breaks = c(LetterBoundaries$Bottom,100), 
                      labels= map_df(LetterBoundaries,rev)$Letter)) %>%
  mutate(Letter = factor(Letter, levels =c("U", "E", "D", "C", "B", "A", "A*")))
    

ggplot(data=IRTotals, aes(x=Letter, fill = Letter)) +
  geom_bar(stat = "count", position = pd, width = 0.8) +
  scale_x_discrete(drop = FALSE) +
  scale_fill_manual(values = (c(rev(GradeColors[1:7]))  )) +  #Janky because of missing grade boundaries (no students in bins)
  labs(x="Letter Grade", y="Count") +
  theme_alan() +
  ggtitle(wrapper(GB1Title, width = 45))

ggsave(here(paste(GB1Title, ".png", sep = "")), plot = last_plot(), device = NULL, path = NULL,
       width = 8, height = 6, units = c("in", "cm", "mm"),
       dpi = 600)


LetterCounts <- count(IRTotals$Letter)

LetterBoundaries2 <-
  LetterBoundaries  %>%
    mutate(Letter = factor(Letter, levels =c("U", "E", "D", "C", "B", "A", "A*")))  %>%
    mutate(Count = plyr::mapvalues(Letter, from = LetterCounts$x, to = LetterCounts$freq))  %>%
    mutate(Count = as.numeric(as.character(Count)))  %>%
    mutate(Count = replace_na(Count, 0))

The distribution here actually doesn’t look terrible, other than too few students being A* and too many being E/Ustudents. THis should be fairly easily curved.

LetterBoundaries2  %>%
    setNames(c("Letter Grade", "Bottom Boundary", "Top Boundary", "Count")) %>%
    knitr::kable(caption = "RDFZ Letter Grade Boundaries", row.names = F) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) 
RDFZ Letter Grade Boundaries
Letter Grade Bottom Boundary Top Boundary Count
A* 89.5 100.00 5
A 79.5 89.49 20
B 69.5 79.49 14
C 59.5 69.49 18
D 49.5 59.49 12
E 39.5 49.49 6
U 0.0 39.49 6

Between Class Performance

How do the three classes for AS Global Perspectives compare to one another on Cambridge Individual Report?

Below, you can see a histogram of performance between the two classes.

BetweenTitle <- paste(Class, Eval, "Histogram of Grades by Class", sep = " - ")

#Histograms by Class
AlanGrades<- subset(IRTotals, Teacher == "Alan")
MoGrades <- subset(IRTotals, Teacher == "Mo")
SuzanaGrades <- subset(IRTotals, Teacher == "Suzana")

ggplot(data=IRTotals, aes(x=Grade, fill = Teacher)) +
  geom_histogram(aes(y=..density..), alpha = 0.75, position = "identity") +
  labs(x="Grade (Percentage)", y="Density") +
  scale_fill_manual(values= c(RDFZRed4, RDFZRed2, RDFZRed1)) +
  scale_x_continuous(limits = c(0,100)) +
  theme_alan() + 
  facet_wrap(~Teacher, ncol = 1) +
  ggtitle(wrapper(BetweenTitle, width = 45))

ggsave(here(paste(BetweenTitle, ".png", sep = "")), plot = last_plot(), device = NULL, path = NULL,
       width = 8, height = 10, units = c("in", "cm", "mm"),
       dpi = 600)

MeanScores <- 
  tapply(IRTotals$Grade, IRTotals$Teacher, mean) %>%
  cbind(tapply(IRTotals$Grade, IRTotals$Teacher, sd)) %>%
  data.frame() %>%
  setNames(c("Mean", "sd")) %>%
  mutate(Mean = round(Mean,1)) %>%
  mutate(sd = round(sd, 1)) %>%
    knitr::kable(caption = "Mean and Standard Deviation of Grades by Class", row.names = T) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) 

MeanScores
Mean and Standard Deviation of Grades by Class
Mean sd
Alan 51.3 25.8
Mo 70.3 15.8
Suzana 69.3 17.8

Suzana and Mo have really even scores here around where they should be, whereas my scores are a fair bit lower.

I actually feel I was pretty reasonable with my grading, and even compared it to Mo’s on a few papers.

However I simply had far too many students who handed in almost nothing, or didn’t take advice on previous revisions, or submitted blatant plagiarism. I am therefor not going to curve my class any more than the other two classes.

Grade Correction

There are broadly two ways that we can correct grades on these tests. First, we can correct for any mistakes we made as teachers - either by giving unfair questions, questions that are too hard, or by not preparing our students adequately for certain types of questions. The second is to curve the entire grade upward to be in line with achievement goals for the course and to align our results with what students could expect from their actual CAIE exams.

Applying an Overall Curve

count(IRTotals$Grade) %>%
    knitr::kable(caption = "AS GP Grade Counts", "html", row.names = F) %>%
    kable_styling(full_width = F) 
AS GP Grade Counts
x freq
0.0 2
11.4 2
25.7 1
28.6 2
34.3 1
40.0 2
42.9 1
45.7 1
48.6 2
51.4 2
54.3 4
57.1 6
60.0 6
62.9 5
65.7 2
68.6 5
71.4 6
74.3 6
77.1 2
80.0 5
82.9 7
85.7 3
88.6 5
91.4 3
94.3 1
97.1 1

Above you can see the counts of actual grades, which we can use to try to set boundaries as we have done before. For the Individual report we have grades from 84 students.

First, we should deal with our students who have a zero. In the case of Tony Cheng, this is because of flagrant plagiarism. The student handed in a draft where I flagged up this plagiarism adn told them it was unacceptable. Rather than choosing to change their essay they simply submitted the same essay for their final, including all the same plagiarism. I will not curve this student’s grade past anything more than a zero. So this leaves us with 82 students.

First we set a U boundary. Let’s aim for 3 students again, which meaning all students under 26% get a U

Next are Es. Lets give 5 of those. So students between 26 and 42 get an E

Next our A-stars. Lets give 10 of those, so all students over 88% (which is close to our standard grade scheme)

Lets give 15 students an A - so all students from 80 - 88 (again similar to the mark scheme)

We want less Ds than As, so lets give 10 of them - all the kids who scored from 42 to 55

This leaves us with 38 students between B and C. These split neatly in the middle C = 57 - 66 , B= 66 - 79

So we end up with these for grade divisions (number of scores is in brackets):

Letters <- c("A*", "A", "B", "C", "D", "E", "U")
MinVal <- c(88.5, 79.5, 66, 57, 42, 26, 0)
MaxVal <- c(100, 87, 79, 66, 55, 42, 26)

CambridgeBoundaries <- 
  cbind.data.frame(Letters, MinVal, MaxVal) %>%
    setNames(c("Letter", "Bottom", "Top")) 

CambridgeGradesTable <-
  CambridgeBoundaries  %>%
    setNames(c("Letter Grade", "Bottom Boundary", "Top Boundary")) %>%
    knitr::kable(caption = "Cambridge Letter Grade Boundaries", row.names = F) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) 

CambridgeGradesTable
Cambridge Letter Grade Boundaries
Letter Grade Bottom Boundary Top Boundary
A* 88.5 100
A 79.5 87
B 66.0 79
C 57.0 66
D 42.0 55
E 26.0 42
U 0.0 26

When we apply these new curved grade boundaries our grades look like this

#Curving and Assigning Letter Boundaries
IRTotalsCurved <-
  IRTotals %>%
    mutate(`Letter (Revised- Cambridge)` = cut(x= Grade, 
                      breaks = c(CambridgeBoundaries$Bottom,100), 
                      labels= map_df(CambridgeBoundaries,rev)$Letter)) %>%
    mutate(`Letter (Revised- Cambridge)` = factor(`Letter (Revised- Cambridge)`, levels =c("U", "E", "D", "C", "B", "A", "A*"))) 


#Plotting Distribution of Letter Grades
CurveTitle2 <- paste(Class, Eval, "Distribution of Grades after Curving", sep = " - ")
  

ggplot(data=IRTotalsCurved, aes(x=`Letter (Revised- Cambridge)`, fill = `Letter (Revised- Cambridge)`)) +
  geom_bar(stat = "count", position = pd, width = 0.8) +
  scale_x_discrete(drop = FALSE) +
  scale_fill_manual(values = (c(rev(GradeColors[1:7]))  )) +  
  labs(x="Letter Grade  (Curved)", y="Count") +
  theme_alan() +
  ggtitle(wrapper(CurveTitle2, width = 45))

ggsave(here(paste(CurveTitle2, ".png", sep = "")), plot = last_plot(), device = NULL, path = NULL,
       width = 8, height = 6, units = c("in", "cm", "mm"),
       dpi = 600)

CambridgeCounts <- count(IRTotalsCurved$`Letter (Revised- Cambridge)`)

CambridgeBoundaries2 <-
  CambridgeBoundaries  %>%
    mutate(Letter = factor(Letter, levels =c("U", "E", "D", "C", "B", "A", "A*")))  %>%
    mutate(Count = plyr::mapvalues(Letter, from = CambridgeCounts$x, to = CambridgeCounts$freq))  %>%
    mutate(Count = as.numeric(as.character(Count)))  %>%
    mutate(Count = replace_na(Count, 0))

CambridgeBoundaries2 %>%
    setNames(c("Letter Grade", "Bottom Boundary", "Top Boundary", "Count")) %>%
    knitr::kable(caption = "RDFZ Letter Grade Boundaries", row.names = F) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) 
RDFZ Letter Grade Boundaries
Letter Grade Bottom Boundary Top Boundary Count
A* 88.5 100 10
A 79.5 87 15
B 66.0 79 19
C 57.0 66 19
D 42.0 55 10
E 26.0 42 5
U 0.0 26 3

We can also break this down to take a look by class:

#Plotting Distribution of Letter Grades
CurveTitle3 <- paste(Class, Eval, "Distribution of Grades after Final Curve", sep = " - ")

ggplot(data=IRTotalsCurved, aes(x=`Letter (Revised- Cambridge)`, fill = `Letter (Revised- Cambridge)`)) +
  geom_bar(stat = "count", position = pd, width = 0.8) +
  scale_x_discrete(drop = FALSE) +
  scale_fill_manual(values = (c(rev(GradeColors[1:7]))  )) +  
  labs(x="Letter Grade  (Curved)", y="Count") +
  theme_alan() +
  facet_wrap(~ Teacher, ncol = 1) +
  ggtitle(wrapper(CurveTitle3, width = 45))

ggsave(here(paste(CurveTitle3, ".png", sep = "")), plot = last_plot(), device = NULL, path = NULL,
       width = 8, height = 10, units = c("in", "cm", "mm"),
       dpi = 600)

This looks… okay? I think probably still not great to have so much clumping in one class (i.e. currently Mo has only a single A and no A*s), but otherwise the distribution looks sensible.

Curving Round 2

So now I just need to assign percentage grades to each individual grade that will be input into engage
These transform our curved letter grades back to the standard grade scheme of RDFZ - e.g. A* = 90, A = 80
And they reintroduce some variation - e.g. here 63, 66, and 70 are all Bs, but this will transform them into the range between 70-80 for RDFZ B grades

## ALAN PLEASE WRITE A BETTER AND PARAMETERIZED WAY TO DO THIS IN THE FUTURE
## SHOULD BE PRETTY EASY WITH A FOR LOOP AT THE VERY LEAST

################################
## A STARs
RevisedAStars <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "A*")
Levels <- levels(factor(RevisedAStars$Grade))
Ranks <- c(1:length(Levels))

RevisedAStars %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 89 + (Increment * Rank))

################################
## As
RevisedAs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "A")
Levels <- levels(factor(RevisedAs$Grade))
Ranks <- c(1:length(Levels))

RevisedAs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 79 + (Increment * Rank))

   
################################
## Bs
RevisedBs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "B")
Levels <- levels(factor(RevisedBs$Grade))
Ranks <- c(1:length(Levels))

RevisedBs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 69 + (Increment * Rank))

   
################################
## Cs
RevisedCs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "C")
Levels <- levels(factor(RevisedCs$Grade))
Ranks <- c(1:length(Levels))

RevisedCs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 59 + (Increment * Rank))

   
################################
## Ds
RevisedDs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "D")
Levels <- levels(factor(RevisedDs$Grade))
Ranks <- c(1:length(Levels))

RevisedDs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 49 + (Increment * Rank))

   
################################
## Es
RevisedEs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "E")
Levels <- levels(factor(RevisedEs$Grade))
Ranks <- c(1:length(Levels))

RevisedEs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 39 + (Increment * Rank))

   
################################
## Us
RevisedUs <- subset(IRTotalsCurved, `Letter (Revised- Cambridge)` == "U")
Levels <- levels(factor(RevisedUs$Grade))
Ranks <- c(1:length(Levels))

RevisedUs %<>%
  mutate(Increment = 10 / length(Levels)) %>%
  mutate(Rank = plyr::mapvalues(Grade, from = Levels, to = Ranks)) %>%
  mutate(`Grade (R)` = 29 + (Increment * Rank))


## ALL GRADES RECOMBINED

AllGrades <-
  RevisedAStars %>%
    rbind.data.frame(RevisedAs, RevisedBs, RevisedCs, RevisedDs, RevisedEs, RevisedUs) %>%
    mutate(PercR = round(pmax(Grade, `Grade (R)`)),2) %>%
    subset(select = c(Name, Teacher, sum, Grade, Letter, PercR, `Letter (Revised- Cambridge)`))%>%
      setNames(c("Student", "Teacher", "Raw Score", "Percentage", "Letter", "Percentage (R)", "Letter (R)")) %>%
      arrange(-`Percentage (R)`)


AllGrades  %>%
    knitr::kable(caption = "Mock Exam Curved Grades", row.names = F) %>%
    row_spec(0, bold = T, color = "white", background = RDFZRed3)%>%
    kable_styling(full_width = FALSE, 
                  bootstrap_options = c("striped", "hover", "condensed"),
                  fixed_thead = TRUE) 
Mock Exam Curved Grades
Student Teacher Raw Score Percentage Letter Percentage (R) Letter (R)
Shuyu Liu Suzana 34 97.1 A* 99 A*
Erica Zhao Suzana 33 94.3 A* 96 A*
Lavender Bian Alan 32 91.4 A* 94 A*
Sophia Xie Alan 32 91.4 A* 94 A*
Helena Gong Suzana 32 91.4 A* 94 A*
Frank Wu Alan 31 88.6 A 92 A*
Jaclyn Xu Mo 31 88.6 A 92 A*
Chelsea Wang Suzana 31 88.6 A 92 A*
Leonie Li Suzana 31 88.6 A 92 A*
Yun Liu Suzana 31 88.6 A 92 A*
Luisa Yu Mo 30 85.7 A 89 A
Richard Wang Mo 30 85.7 A 89 A
Xavier Zhang Mo 30 85.7 A 89 A
Bodi Chu Mo 29 82.9 A 86 A
Camille Sun Mo 29 82.9 A 86 A
Claire Claire Mo 29 82.9 A 86 A
Ella Mo 29 82.9 A 86 A
Joanna Liu Mo 29 82.9 A 86 A
Steven Yang Mo 29 82.9 A 86 A
Zoe Wang Suzana 29 82.9 A 86 A
Cathy Chen Mo 28 80.0 A 82 A
Esther Zhang Mo 28 80.0 A 82 A
Sky Guo Mo 28 80.0 A 82 A
Amy An Suzana 28 80.0 A 82 A
Nina Wang Suzana 28 80.0 A 82 A
Henry Henry Mo 27 77.1 B 79 B
Rika Chen Mo 27 77.1 B 79 B
Verna Xu Alan 26 74.3 B 76 B
Jason Li Mo 26 74.3 B 76 B
Max Liu Mo 26 74.3 B 76 B
May Ma Mo 26 74.3 B 76 B
Daphne Qin Suzana 26 74.3 B 76 B
Lemon Zhang Suzana 26 74.3 B 76 B
Arthur Li Alan 25 71.4 B 74 B
Jeremy Yin Alan 25 71.4 B 74 B
Bryan Huang Mo 25 71.4 B 74 B
Grace Mo 25 71.4 B 74 B
Haotian Ling Mo 25 71.4 B 74 B
Benjamin Xu Suzana 25 71.4 B 74 B
Liz Zheng Alan 24 68.6 C 72 B
Ellie Wang Mo 24 68.6 C 72 B
Eileen Min Suzana 24 68.6 C 72 B
Elena Chu Suzana 24 68.6 C 72 B
Kyle Cheng Suzana 24 68.6 C 72 B
Calvin Calvin Mo 23 65.7 C 69 C
Jessica Guo Mo 23 65.7 C 69 C
Franklin Franklin Mo 22 62.9 C 66 C
Leo Mo 22 62.9 C 66 C
Mei Chen Mo 22 62.9 C 66 C
Robert Robert Mo 22 62.9 C 66 C
Jacky Jiang Suzana 22 62.9 C 66 C
Caterina Chi Alan 21 60.0 C 64 C
Chloie Chen Alan 21 60.0 C 64 C
Liu Fei Mo 21 60.0 C 64 C
Miya Miya Mo 21 60.0 C 64 C
Jolin Ren Suzana 21 60.0 C 64 C
Molly Ye Suzana 21 60.0 C 64 C
Darcy Sun Alan 20 57.1 D 62 C
Aurora Aurora Mo 20 57.1 D 62 C
Bob Bo Mo 20 57.1 D 62 C
Krystal Shi Mo 20 57.1 D 62 C
Oliver Ruan Mo 20 57.1 D 62 C
Sam Ding Mo 20 57.1 D 62 C
Airlia Zhang Alan 19 54.3 D 59 D
Bill Han Suzana 19 54.3 D 59 D
Florence Fu Suzana 19 54.3 D 59 D
Lisa Lu Suzana 19 54.3 D 59 D
Shelia Cheng Alan 18 51.4 D 57 D
Ann Du Suzana 18 51.4 D 57 D
Priscilla Wang Alan 17 48.6 E 55 D
Joe Zhou Suzana 17 48.6 E 55 D
Sebastian Hei Alan 16 45.7 E 53 D
Frank Feng Alan 15 42.9 E 51 D
Mandy Wang Alan 14 40.0 E 49 E
Rex Ye Suzana 14 40.0 E 49 E
Michael Su Alan 12 34.3 U 46 E
Carlo Jiang Alan 10 28.6 U 42 E
Timon Suzana 10 28.6 U 42 E
George Hao Alan 9 25.7 U 39 U
Elven Luo Alan 4 11.4 U 34 U
Ilia Lu Alan 4 11.4 U 34 U

Tables

Tables for Engage

##Alan
AllGrades %>%
  subset(Teacher == "Alan") %>%
  separate(Student, into = c("First", "Last"), sep = " ") %>%
  arrange(Last) %>%
    write.csv(here("GPIR-Alan.csv"))


##Mo
AllGrades %>%
  subset(Teacher == "Mo") %>%
  arrange(Student) %>%
    write.csv(here("GPIR-Mo.csv"))

##Suzana
AllGrades %>%
  subset(Teacher == "Suzana") %>%
  arrange(Student) %>%
    write.csv(here("GPIR-Suzana.csv"))


##All
AllGrades %>%
  arrange(Student) %>%
    write.csv(here("GPIR-All.csv"))