Pros Cons
———————– —————————–
Open Source Provides a specific tool-set
Highly reproducible Not always intuitive
Free to use Variance in quality
Rich package ecosystem Performance can be slower
ggplot2 package in R is often referred to as the ‘gold standard’ for data visualization

Run button for R files)
Console tab shows the R console allowing you to issue and view the output of R commandsTerminal tab gives access to the system terminal
Environment tab shows all the variables in your current environment and allows you to view themHistory tab shows all the commands you have run this sessionConnections tab allows you to connect to a database
File tab shows files in the current directory and allow for moving, copying, deleting, and renaming filesPackages tab allows for the installation, loading, and unloading of packagesHelp tab provides searchable access to the help files for all installed packagesPlots and Viewer tabs display certain objects and files, they are automatically selected when an object to be plotted or viewed is generatedFile > New Project... > New Directory > New ProjectCreate ProjectFile > New File > R Script"Hello world"hello_worldRun > Run AllConsole tab below## [1] "Hello world"
= or <-)
.) or underscore (_)<- as an assignment operator is traditional in R, but most other languages do not follow this convention# Create a variable for your favorite number
fav_num = 7
fav.num <- 7
# ...or your favorite color
fav_color = "blue"
fav_color
## [1] "blue"
# Save a vector (a group) of numbers
evens = c(2, 4, 6, 8, 10)
# Arithmetic
(2 + 2) * 4^2
## [1] 64
# Use a function
mean(evens)
## [1] 6
# Use variables
fav_num + 3
## [1] 10
fav_num + evens
## [1] 9 11 13 15 17
Environment tab or by entering the name of the variable as a commandc() function and hold any number of the same type of object (e.g all numbers or all text)c() function (e.g. c(1, 2, 3) or c("a", "b", "c"))1 or "a")
1 is the same as c(1)1 or 5.37)
1)5.37)"hello" or 'hello')TRUE or FALSE)c() function can append one vector to anotherc(c(1,2), c(3,4))
## [1] 1 2 3 4
pets = c("dog", "fish", "cat", "frog", "hamster")
first_pet = pets[1]
first_pet
## [1] "dog"
mammals = pets[c(1,3,5)]
mammals
## [1] "dog" "cat" "hamster"
as.logical(c(1, 0, 0, 1))
## [1] TRUE FALSE FALSE TRUE
as.logical(c("TRUE", "FALSE", "F", "T"))
## [1] TRUE FALSE FALSE TRUE
as.numeric(c(TRUE, FALSE, F, T))
## [1] 1 0 0 1
as.numeric(c("1", "0", "0", "1"))
## [1] 1 0 0 1
as.character(c(TRUE, FALSE, F, T))
## [1] "TRUE" "FALSE" "FALSE" "TRUE"
as.character(c(1, 0, 0, 1))
## [1] "1" "0" "0" "1"
sum(c(T, F, T, F))
## [1] 2
seq() function
by argument specifies the amount to increment, by default it is 1: operator1:5
## [1] 1 2 3 4 5
seq(1, 5)
## [1] 1 2 3 4 5
seq(1, 10, by = 3)
## [1] 1 4 7 10
paste() which concatenates (combines) multiple character vectorsstringr tidyverse package (named after ‘strings’ which is what many other languages call a single-length character vectors)
stringr functions are likely str_sub() which returns a portion of a character vector and str_split() which splits a character vector into multiple parts by a sequence of characterspaste("hello", "there", "friends")
## [1] "hello there friends"
paste("hello", "there", "friends", sep = "_")
## [1] "hello_there_friends"
str_sub("grades_2020", start = 8)
## [1] "2020"
str_split("grades_2020", pattern = "_")
## [[1]]
## [1] "grades" "2020"
==, !=, <, >, <=, >=, %in%1:5 <= 3
## [1] TRUE TRUE TRUE FALSE FALSE
1:5 == 4
## [1] FALSE FALSE FALSE TRUE FALSE
c("hello", "there", "friend") == "there"
## [1] FALSE TRUE FALSE
"hello" %in% c("hello", "there", "friend")
## [1] TRUE
&) returns TRUE when two both values evaluate to TRUE|) returns TRUE when either of two values evaluate to TRUE!) returns true when a value evaluates to FALSEc(TRUE & TRUE, TRUE & FALSE)
## [1] TRUE FALSE
c(TRUE | TRUE, TRUE | FALSE)
## [1] TRUE TRUE
c(!TRUE, !FALSE)
## [1] FALSE TRUE
rep(TRUE, 5) & rep(TRUE, 5)
## [1] TRUE TRUE TRUE TRUE TRUE
typeof() functiontypeof("hello")
## [1] "character"
typeof(6)
## [1] "double"
typeof(TRUE)
## [1] "logical"
tidyverse package
as_tibble() functiontidyverse package
Packages tab there is an Install button, click that and type ‘tidyverse’ and install itlibrary() function, run library(tidyverse) from a script or the console to load the tidyverse packagetidyverse package is actually several data science packages that follow a common ‘tidy’ analysis philosophy and are all installed and loaded together
tidyverse package is loaded we can begin to use tibbles for storing data
tibble() function to create a new tibbletibble(grades = c("A", "B", "A", "D"), age = age_vector))
Environment tabmy_tibble$col_name, select(my_tibble, "col_name"), my_tibble["col_name"], or my_tibble[col_index]
mean(), you may need to use unlist() to convert a single-columned tibble to a vectormy_tibble[row_index, ] (notice the comma after row_index)my_tibble[row_index, col_index]nrow(my_tibble) for the number of rows and ncol(my_tibble) for the number of columnsiris data-set gives different measurements for 150 different irises of 3 speciesas_tibble(iris) with the variable name iris_datatable() function might be useful)iris_data = as_tibble(iris)
mean(iris_data$Sepal.Length)
## [1] 5.843333
median(iris_data$Petal.Width)
## [1] 1.3
mean((iris_data$Petal.Width * iris_data$Petal.Length))
## [1] 5.794067
table(iris_data$Species)
##
## setosa versicolor virginica
## 50 50 50
Help tab or, if you know the name, use the ? function (e.g. ?iris)iris data-set contains a special kind of vector for the species variable - called a factor
my_function(a, b, c) executing my_function(1, 2, 3) defines a as 1, b as 2, and c as 3)my_function(b = 2, c = 3, a = 1) is the same as before, even though the order does not match the function’s definition)a_1 = function_one(x)
a_2 = function_two(a_2)
a_3 = function_three(a_3)
a = function_one(x)
a = function_two(a)
a = function_three(a)
a = function_three(function_two(function_one(x)))
%>%) directs a variable or the output of a function into the input arguments of another functionSo our previous example of
a = function_three(function_two(function_one(x)))
…becomes…
a = x %>% function_one() %>% function_two() %>% function_three()
round() function (which rounds numbers to a specified amount of decimal places)?Simple!
round(pi, 3)
## [1] 3.142
…becomes…
pi %>% round(3)
## [1] 3.142
.) tells the pipe where else to pipe the data into, in addition to the first argumentiris_data %>% select(Sepal.Length) %>% as_vector() %>% tibble(Standardized.Length = (. / mean(.)) ) %>% head(1)
## # A tibble: 1 x 2
## . Standardized.Length
## <dbl> <dbl>
## 1 5.1 0.873
std_var = . %>% as_vector() %>% tibble(Standardized.Var = (. / mean(.)) )
iris_data %>% select(Sepal.Length) %>% std_var() %>% head(1)
## # A tibble: 1 x 2
## . Standardized.Var
## <dbl> <dbl>
## 1 5.1 0.873
{}) around a function, causes the data to ONLY be piped to the dotsiris_data %>% select(Sepal.Length) %>% as_vector() %>% {tibble(Standardized.Length = (. / mean(.)) )} %>% head(1)
## # A tibble: 1 x 1
## Standardized.Length
## <dbl>
## 1 0.873
magrittr packagepivot_wider() converts from long data to wide datapivot_longer() converts from wide to long dataunite() combines multiple columns into a single new columnseparate() splits a column into multiple new columnsstringr package can be used to modify character vectors before or after this processpivot_wider()table2 %>% head(4)
## # A tibble: 4 x 4
## country year type count
## <chr> <int> <chr> <int>
## 1 Afghanistan 1999 cases 745
## 2 Afghanistan 1999 population 19987071
## 3 Afghanistan 2000 cases 2666
## 4 Afghanistan 2000 population 20595360
table2 %>% pivot_wider(names_from = "type", values_from = "count") %>% head(4)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
pivot_longer()table1 %>% head(4)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
table1 %>% mutate_all(as.character) %>% pivot_longer(cols = everything()) %>% head(4)
## # A tibble: 4 x 2
## name value
## <chr> <chr>
## 1 country Afghanistan
## 2 year 1999
## 3 cases 745
## 4 population 19987071
unite()sep argument (e.g. sep = "_")table1 %>% head(4)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
table1 %>% unite("info", cases, population) %>% head(4)
## # A tibble: 4 x 3
## country year info
## <chr> <int> <chr>
## 1 Afghanistan 1999 745_19987071
## 2 Afghanistan 2000 2666_20595360
## 3 Brazil 1999 37737_172006362
## 4 Brazil 2000 80488_174504898
separate()sep argument (e.g. sep = "/")table3 %>% head(4)
## # A tibble: 4 x 3
## country year rate
## <chr> <int> <chr>
## 1 Afghanistan 1999 745/19987071
## 2 Afghanistan 2000 2666/20595360
## 3 Brazil 1999 37737/172006362
## 4 Brazil 2000 80488/174504898
table3 %>% separate(rate, c("cases", "population")) %>% head(4)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <chr> <chr>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
-) is used to specify ‘all but this’, as with
my_vector[-5] would return all the items in the vector except the one at index 5my_tibble %>% select(-age) would return a tibble with every variable from my_tibble except agetable1 %>% head(4)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Afghanistan 1999 745 19987071
## 2 Afghanistan 2000 2666 20595360
## 3 Brazil 1999 37737 172006362
## 4 Brazil 2000 80488 174504898
table1 %>% pivot_longer(cols = -c(country, year), names_to = "type", values_to = "count",) %>% head(4)
## # A tibble: 4 x 4
## country year type count
## <chr> <int> <chr> <int>
## 1 Afghanistan 1999 cases 745
## 2 Afghanistan 1999 population 19987071
## 3 Afghanistan 2000 cases 2666
## 4 Afghanistan 2000 population 20595360
filter() function takes a comparison of a variable and a value and returns each row where that comparison is TRUEtable1 %>% filter(country == "China")
## # A tibble: 2 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 China 1999 212258 1272915272
## 2 China 2000 213766 1280428583
table1 %>% filter(cases > 3000)
## # A tibble: 4 x 4
## country year cases population
## <chr> <int> <int> <int>
## 1 Brazil 1999 37737 172006362
## 2 Brazil 2000 80488 174504898
## 3 China 1999 212258 1272915272
## 4 China 2000 213766 1280428583
mutate() functionmutate() function takes a name for a variable and any code to generate that variabletable1 %>% mutate(rate = cases / population)
## # A tibble: 6 x 5
## country year cases population rate
## <chr> <int> <int> <int> <dbl>
## 1 Afghanistan 1999 745 19987071 0.0000373
## 2 Afghanistan 2000 2666 20595360 0.000129
## 3 Brazil 1999 37737 172006362 0.000219
## 4 Brazil 2000 80488 174504898 0.000461
## 5 China 1999 212258 1272915272 0.000167
## 6 China 2000 213766 1280428583 0.000167
table1 %>% mutate(year = year %>% as.character() %>% str_sub(3))
## # A tibble: 6 x 4
## country year cases population
## <chr> <chr> <int> <int>
## 1 Afghanistan 99 745 19987071
## 2 Afghanistan 00 2666 20595360
## 3 Brazil 99 37737 172006362
## 4 Brazil 00 80488 174504898
## 5 China 99 212258 1272915272
## 6 China 00 213766 1280428583
readr, haven, and readxl packages for importing different file types directly to tibbles in R
readr includes functions to read standard delimited file types such as CSV and TSV fileshaven includes functions to read files from other stats programs such as Stata, SPSS, and SASreadxl includes functions to read Excel filessf provides tools for working with GIS data such as shapefilesjsonlite adds functionality for reading and writing to JSONshere() function in the here package makes specifying a file path much easier
NAna.omit() function will remove any rows that have any missing valueshere packagereadr package (learn more about the package on the tidyverse website)mean() function)aggregate() function for bonus points)library(here)
student_data = read_csv(here("student_data.csv"))
gathered_student_data = student_data %>% select(-age, -grade_level) %>%
pivot_longer(c(-first, -middle, -last, -school), names_to = "key", values_to = "grade") %>%
separate(key, c("class", "year"), sep = "__")
gathered_student_data %>% head(4)
## # A tibble: 4 x 7
## first middle last school class year grade
## <chr> <chr> <chr> <chr> <chr> <chr> <dbl>
## 1 Krimhilde Yuri Hierro Oakwood math_grade 2015 NA
## 2 Krimhilde Yuri Hierro Oakwood english_grade 2015 NA
## 3 Krimhilde Yuri Hierro Oakwood science_grade 2015 NA
## 4 Krimhilde Yuri Hierro Oakwood social_studies_grade 2015 NA
final_student_data = gathered_student_data %>% pivot_wider(names_from = "class", values_from = "grade") %>%
mutate(gpa = (math_grade + english_grade + science_grade + social_studies_grade) / 4)
final_student_data %>% head(4)
## # A tibble: 4 x 10
## first middle last school year math_grade english_grade science_grade
## <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 Krim~ Yuri Hier~ Oakwo~ 2015 NA NA NA
## 2 Krim~ Yuri Hier~ Oakwo~ 2016 72 64 64
## 3 Krim~ Yuri Hier~ Oakwo~ 2017 72 58 63
## 4 Krim~ Yuri Hier~ Oakwo~ 2018 65 57 62
## # ... with 2 more variables: social_studies_grade <dbl>, gpa <dbl>
# the easy way, repeat for each school
final_student_data %>% filter(year == 2018, school == "Oakwood") %>% select(gpa) %>% unlist() %>% mean()
## [1] 60.55165
# the harder way
final_student_data %>% filter(year == 2018) %>% {aggregate(.$gpa, by = list(school = .$school), mean)}
## school x
## 1 Oakwood 60.55165
## 2 Pine Field 62.76240
## 3 Shady Willow 66.17926
graphics package, such as the plot() and hist() functions
ggplot2 a data visualization package which follows a ‘grammar of graphics’
ggplot2 is one of the world’s most popular data visualization tools, used by world renowned organisations such as the New York Times and FiveThirtyEight, and has been downloaded millions of timesggplot2 Basicsggplot2 layers components on top of each other to build a visualizationggplot() function which specifies a data source and any default aesthetic mappings or settings to use
ggplot() accepts data in data frame or tibble formaes() function, connect a dimension of data to a dimension of the visualization+)x, y, color, and fill are the most common, but some geometries will use more or only a portion of thesegeom_ (e.g. geom_point())displ_year_plot = ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()
displ_year_plot
color in an aesthetic maps changes in color based to a variable, creating a legendcolor in a geometry simply sets the same color for all elements in that geometrydispl_year_plot = ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class))
displ_year_plot
displ_year_plot + geom_smooth(color = "grey60")
scale_ can alter the scale used for a specific dimension
scale_radius() adjusts the size aesthetic by the radius, the default scale_size() adjusts the arealabs() function allows for the definition of labelsmpg %>% arrange(-cyl) %>% ggplot(aes(x = displ, y = hwy)) +
geom_point(aes(color = class, size = cyl)) +
geom_smooth(color = "grey60") +
scale_radius(range = c(2, 7)) +
facet_wrap(~ year) + theme(legend.position = "bottom") +
labs(title = "Cars with Larger Engines get Worse Fuel Efficiency", subtitle = "At highway speeds in both 1999 and 2008",
x = "Engine Displacement (L)", y = "Highway Fuel Efficiency (mpg)", color = "Vehicle \nClass", size = "Number of \nCylinders")
R Markdown from the File > New File... menu
Ctrl + Alt + i or on macOS Cmd + Opt + i{r code-block-name})Run menu
#) followed by a space (e.g. # My Big Fancy Header)
# Main Heading 1
## Sub-heading 1.1
### Sub-heading 1.1.1
-), asterisk, (*), or addition signs (+) followed by a space1.) followed by a space
*italics* or _also italics_)**bold** or __also bold__)
__*bold and italics*__)~~strikethrough~~)[A Link to Google](http://google.com)) A Link to Google)

Create an R Markdown document knitted into HTML with visualizations to answer the following questions:
final_student_data %>% ggplot(aes(x = school)) + geom_bar()
final_student_data %>% ggplot(aes(x = math_grade, y = english_grade, color = school)) + geom_point() + geom_smooth(color = "grey60")
final_student_data %>% ggplot(aes(x = school, y = gpa, fill = school)) + geom_violin()
gathered_student_data %>% ggplot(aes(x = school, y = grade, fill = school)) + geom_violin() + facet_wrap(~class)
print() function may be necessary to print to the console
print() function outputs to the console, in contrast to the rest of#)if Statementif statement executes code if a logical condition evaluates to truegrade_level = 12
if (grade_level > 8) {
print("high school")
}
## [1] "high school"
if else Statementif else statement executes a section of code if a logical condition evaluates to true, or a different section of code if it does not
grade_level = 7
if (grade_level > 8) {
print("high school")
} else if(grade_level > 5) {
print("middle school")
} else {
print("elementary school")
}
## [1] "middle school"
while Loopswhile loops continue to run a section of code as long as a logical condition continues to evaluate to true
while loopsgrade_level = 6
while (grade_level < 12) {
print(grade_level)
grade_level = grade_level + 1
}
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
## [1] 11
for Loopsfor loops in R are technically for each loops which loop a section of code for every element in a vector
for (cur_grade_level in 6:12) {
print(cur_grade_level)
}
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
## [1] 11
## [1] 12
apply Familyapply family corresponds to the apply(), lapply(), and sapply() functions which execute a function for each element in a vector or listfor loops in Rfor loops in R, while intuitive due to their similarity to other languages, are not the best for performanceapply function should be usedlibrary(tictoc)
rand_numbers = runif(10^6, 0, 10) %>% matrix(ncol = 1000) %>% as_tibble()
## Warning: `as_tibble.matrix()` requires a matrix with column names or a `.name_repair` argument. Using compatibility `.name_repair`.
## This warning is displayed once per session.
tic()
output = double(nrow(rand_numbers))
for (i in 1:nrow(rand_numbers)) {
output[i] = rand_numbers[i, ] %>% unlist() %>% mean()
}
toc()
## 1.53 sec elapsed
tic()
output = apply(rand_numbers, 1, mean)
toc()
## 0.06 sec elapsed
tic()
output = rowMeans(rand_numbers)
toc()
## 0.02 sec elapsed
function() (e.g. function_name = function())my_function = function(argument1, argument2) {
# adds argument1 and argument2
result = argument1 + argument2
}
= operator...) operator allows unlimited additional arguments to be passed into the functiona = 3
# ... passes additional arguments to the print function
do_something_weird = function(a, ..., b = 5) {
print(rep(a, times = b), ...)
}
do_something_weird(1)
## [1] 1 1 1 1 1
do_something_weird(9, 7)
## [1] 9 9 9 9 9
do_something_weird(5.63, digits = 1)
## [1] 6 6 6 6 6
print() functionreturn() function, which also exits the function when it is called# now returns the value instead of printing it to the console
do_something_weird = function(a, ..., b = 5) {
return(rep(a, times = b, ...))
}
ones = do_something_weird(1)
ones
## [1] 1 1 1 1 1
do_something_weird(9, 7) %>% sum()
## [1] 63
Vectorize() functionwarning() or stop() functions to convey that something has gone wrong, or the message() when something else has happenedconvert_grade = function(grade, input_type = "default") {
grade = as.vector(unlist(grade))
if (min(grade) < 0 | !is.numeric(grade)) stop("Invalid grade")
if (input_type == "default") {
if (max(grade) < 4) {
input_type = "four"
message("Converting to 4 point scale")
} else {
input_type = "hundred"
message("Converting to 100 point scale")
}
}
conv_gr = Vectorize(function(grade, input_type) {
if (input_type == "hundred") {
if (grade > 100) stop("Invalid grade")
if (grade > 89) return("A")
if (grade > 79) return("B")
if (grade > 69) return("C")
if (grade > 59) return("D")
return("F")
}
if (input_type == "hundred") {
if (grade > 4) stop("Invalid grade")
if (grade > 3.7) return("A")
if (grade > 2.7) return("B")
if (grade > 1.7) return("C")
if (grade > 1.0) return("D")
return("F")
}
})
return(conv_gr(grade, input_type))
}
student_data %>% select(math_grade__2018) %>% convert_grade()
## Converting to 100 point scale
## [1] "D" "C" "D" "B" "D" "F" "B" "C" "D" "D" "D" "C" "D" "F" "C" "D" "C" "C"
## [19] "C" "D" "D" "B" "C" "D" "D" "C" "C" "F" "D" "D" "D" "C" "C" "D" "B" "C"
## [37] "D" "C" "C" "B" "D" "D" "C" "D" "C" "C" "F" "C" "B" "D" "A" "C" "F" "F"
## [55] "F" "D" "C" "F" "F" "B" "D" "C" "C" "C" "F" "D" "D" "D" "C" "D" "C" "C"
## [73] "B" "B" "D" "C" "C" "C" "F" "D" "C" "C" "D" "F" "D" "C" "C" "D" "B" "B"
## [91] "B" "C" "D" "C" "F" "D" "B" "C" "C" "D" "C" "D" "D" "C" "A" "B" "D" "C"
## [109] "D" "D" "B" "C" "B" "F" "D" "C" "B" "B" "D" "C" "B" "D" "C" "D" "C" "C"
## [127] "C" "F" "F" "D" "C" "C" "D" "C" "C" "B" "C" "F" "C" "C" "C" "B" "C" "D"
## [145] "D" "F" "B" "B" "B" "C" "D" "D" "D" "C" "C" "B" "C" "D" "C" "B" "B" "A"
## [163] "C" "D" "D" "C" "D" "D" "C" "B" "C" "A" "D" "D" "C" "D" "F" "C" "D" "F"
## [181] "C" "C" "D" "D" "D" "D" "C" "F" "C" "D" "C" "C" "C" "B" "B" "C" "D" "D"
## [199] "D" "F" "C" "C" "D" "C" "C" "C" "C" "D" "B" "C" "B" "C" "D" "C" "F" "C"
## [217] "D" "F" "A" "B" "D" "D" "C" "C" "F" "D" "D" "C" "C" "D" "F" "C" "C" "D"
## [235] "C" "D" "D" "B" "F" "B" "F" "D" "D" "C" "C" "C" "D" "C" "B" "C" "D" "F"
## [253] "C" "F" "C" "D" "D" "D" "C" "D" "D" "C" "D" "D" "C" "D" "C" "C" "C" "C"
## [271] "D" "D" "C" "D" "C" "A" "D" "D" "F" "D" "C" "D" "C" "F" "D" "C" "D" "C"
## [289] "B" "C" "A" "C" "D" "C" "F" "F" "D" "C" "A" "C" "B" "B" "C" "D" "F" "B"
## [307] "C" "C" "D" "D" "C" "D" "B" "B" "C" "C" "C" "D" "D" "B" "D" "C" "C" "C"
## [325] "C" "B" "B" "C" "B" "D" "B" "D" "D" "B" "F" "C" "F" "B" "F" "B" "D" "D"
## [343] "C" "D" "C" "D" "D" "A" "D" "F" "C" "A" "C" "C" "C" "D" "D" "A" "C" "D"
## [361] "C" "C" "F" "C" "F" "A" "D" "C" "C" "D" "F" "F" "C" "C" "B" "D" "C" "B"
## [379] "B" "B" "F" "B" "C" "D" "C" "C" "B" "B" "A" "B" "C" "C" "C" "D" "F" "B"
## [397] "B" "C" "C" "C" "C" "D" "D" "D" "D" "B" "B" "C" "D" "D" "C" "B" "D" "C"
## [415] "D" "C" "B" "C" "F" "D" "D" "D" "D" "D" "F" "C" "D" "C" "F" "D" "C" "B"
## [433] "C" "D" "F" "B" "D" "D" "C" "D" "D" "C" "C" "D" "B" "C" "C" "D" "C" "C"
## [451] "C" "D" "D" "D" "D" "C" "D" "C" "F" "F" "D" "F" "C" "B" "D" "D" "B" "D"
## [469] "B" "C" "C" "C" "B" "F" "A" "C" "D" "C" "F" "F" "D" "F" "D" "C" "C" "D"
## [487] "B" "C" "C" "C" "D" "B" "C" "C" "D" "C" "F" "C" "C" "C"
convert_grade(103)
## Converting to 100 point scale
## Error in (function (grade, input_type) : Invalid grade