Ease students into developer mindset.
Emphasize unit-testing and documentation frameworks.
Practice with code review of others’ code.
Easier to performance check code.
Grading via automatic tests.
Create a skeleton R package on GitHub.
Include pre-written unit tests.
Include vignette-style function demos.
Duplicate unit tests with a new dataset for your own use in grading.
Require code review as part of assignment.
Complete the functions.
Document the functions.
Build the package.
Run the unit tests.
Render the demo document.
A link to their GitHub of the package. I recommend GitHub Classroom for this.
A rendered html, showing the functions being run on data.
A code review of someone else’s package.
Four grading elements to combine:
Does the package properly build and install or are there errors?
Does the package pass the provided unit tests?
Do the package functions behave as expected in the demo document?
Are the peer code reviews positive?
Does the package pass the secret unit tests?
Make a folder somewhere called “Package Assignment Practice” or similar.
Run usethis::create_package("YOUR PATH/Package Assignment Practice/example_skeleton_package")
. This will open a new R Project.
In the new project, run usethis::use_r("simple_linear_regression")
Run usethis::use_test("simple_linear_regression")
New File > Quarto > slr_demo.qmd
Include code that you hope will work properly…
… and code you hope will error in a reasonable way.
How much detail you provide depends on the level of your students.
For the first pass, I recommend:
Include all the roxygen
-style documentation
Include the inputs and names.
Give them a few lines to get started.
Use comments to show where you want them to edit.
Provide the object structure of the expected output.
Make sure the code is all runnable as-is. (But not fully correct yet!)
#' Implements simple linear regression by hand
#'
#' @param dat A data frame
#' @param response The name of a response variable in the data frame.
#' @param explanatory The name of the explanatory variable in the data frame.
#'
#' @return A data frame of coefficients
#'
#' @import dplyr
#'
#' @export
simple_linear_regression <- function(dat, response, explanatory){
x <- dat[[explanatory]]
y <- dat[[response]]
x_bar <- mean(x)
y_bar <- mean(y)
### Edit code after here
sd_x <- 1
sd_y <- 1
beta_0 <- 1
beta_1 <- 1
results <- tibble::tibble(
Intercept = 0,
Slope = 1
)
return(results)
}
test_that("simple linear regression is correct", {
my_result <- simple_linear_regression(mtcars, "mpg", "hp")
mass_result <- lm(mpg ~ hp, data = mtcars)
expect_equal(coef(mass_result)[['hp']], my_result$Slope,
tolerance = 0.05)
expect_equal(coef(mass_result)[[1]], my_result$Intercept,
tolerance = 0.05)
})
Make a copy of the tests
folder in your package.
Save that copy outside the package folder, in your “Package Assignment Practice” folder.
Change the unit test code to use a different dataset or different variables.
Open the script “testthat.R” …
library(testthat)
library(example_skeleton_package)
test_check("example_skeleton_package")
… and run it!
If student work is on GitHub rather than in folders…
remotes::install_github("imastudent/example_skeleton_package")
… and loop through each.
urls <- # list of student repos
all_test_results <- tibble()
all_time_results <- tibble()
for (url in submissions){
unloadNamespace("example_skeleton_package")
remove.packages("example_skeleton_package", ".")
remotes::install_github(submissions)
library(example_skeleton_package)
lr_test <- testthat::test_dir("./tests/testthat",
reporter = "minimal",
stop_on_failure = FALSE)
## save output however you prefer
}
Remove inputs and output from scaffolding
Require tidy eval and unquoted variables (Starting Point)
Emphasize code review (“Code Smells and Feels” talk)
Emphasize responsible use of Git and GitHub
Don’t provide roxygen2
documentation; require students do it.
Require additional unit tests
Require multiple .R
files and multiple functions per file.
Practice CRAN submission?
Matrix operations, e.g. for multiple regression
Iteration to convergence, e.g. kmeans
Model fitting via gradient descent
Emphasize speed/efficiency
big_dat <- #some large dataset
for (url in submissions){
unloadNamespace("example_skeleton_package")
remove.packages("example_skeleton_package", ".")
remotes::install_github(submissions)
library(example_skeleton_package)
benches <- bench::mark(
slr = safely(simple_linear_regression)(big_dat, price, gearbox),
memory = FALSE,
check = FALSE,
time_unit = "ms",
min_time = Inf,
max_iterations = 20,
filter_gc = FALSE
) %>%
select(
expression, median
)
}