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 visualizationRun
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 terminalEnvironment
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 databaseFile
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 Project
Create Project
File
> New File
> R Script
"Hello world"
hello_world
Run
> Run All
Console
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 FALSE
c(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_data
table()
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 age
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 %>% 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 TRUE
table1 %>% 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
NA
na.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![A Cat Picture](/r-crash-course/cat_picture.jpg)
)
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