跳至主要内容

使用 DataCamp 的自动评分器教授 R

了解 DataCamp 的自动评分器如何助力教授 R,以及如何发挥 testwhat 函数的作用。
Filip Schouwenaars
Filip Schouwenaars

创建您的免费账户

实践并学习路径你的进步。

使用 Google 继续继续即表示您接受我们的 使用条款,我们的 隐私政策 ,并且您的数据存储在美国。

自我们推出第一门课程以来,即时、个性化的反馈一直是 DataCamp 学习体验的核心。如果学生提交的代码有错误,系统会指出他们出错的位置,以及如何修正。您可以在我们的免费 R 入门课程中亲自体验。下方截图来自我们的R 进阶课程。

为了检查提交并生成反馈,DataCamp 上的每个练习都配有所谓的“提交正确性测试”(Submission Correctness Test,简称 SCT)。

SCT 是一组自定义测试脚本,用于评估学生提交的代码,以及他们通过代码生成的输出和变量。对于我们在 DataCamp 上教授的每种语言,我们都构建了相应的开源包,以便轻松核验学生提交的各个要素。对于 R 练习,这个包名为 testwhat。多年来,我们添加了大量函数,用于:

  • 检查变量赋值
  • 检查函数调用及其返回结果
  • 检查 if 语句、for 循环和 while 循环
  • 检查函数定义
  • 检查是否正确加载所需的包
  • 检查学生生成的输出
  • 检查 ggplot 和 ggvis 作图调用
  • ……

当这些检查函数发现错误时,会自动生成有意义的反馈消息,指引学生找到并修正错误。您也可以指定自定义反馈消息,以覆盖这些自动生成的消息。

过去,testwhat 与我们用于在 DataCamp 服务器上执行 R 代码的专有后端紧密耦合。尽管 testwhat 一直是开源的,但如果没有这个自定义后端,实际效果并不好,因此 testwhat 只能在 DataCamp 的语境中使用。然而现在,testwhat 已可独立使用,并支持其他用例。即使您的教学形式与 DataCamp 大不相同,您也可以利用 testwhat 的全部功能来测试学生提交。您可以从 GitHub 安装该包:

library(remotes) # devtools works fine too
install_github('datacamp/testwhat')

做个快速演示:假设您要求学生创建一个变量 x,其值等于 5。理想解的字符串版本类似于:

solution_code <- 'x <- 5'

现在设想学生提交的脚本中,x 被错误地设为一个包含两个值的向量,可如下编码:

student_code <- 'x <- c(4, 5)'

testwhat 提供一个名为 setup_state() 的函数,它会在相互独立的环境中分别执行学生提交和参考解脚本,并捕获学生代码产生的输出和错误。所有这些信息都存储在所谓的练习状态中,您可以使用 ex() 访问:

library(testwhat)
setup_state(stu_code = student_code,
            sol_code = solution_code)
ex()
## <RootState>

该练习状态可以配合 magrittr 的管道语法传递给 testwhat 提供的各类检查函数。要检查学生是否定义了变量 x,您可以使用 check_object():

ex() %>% check_object('x')
## <ObjectState>

这段代码可以正常运行,因为学生确实定义了变量 x。要进一步检查学生是否正确地定义了变量 x,您可以使用 check_equal():

ex() %>% check_object('x') %>% check_equal()
## Error in check_that(is_equal(student_obj, solution_obj, eq_condition), : The contents of the variable `x` aren't correct. It has length 2, while it should have length 1.

这会抛出错误。check_equal() 发现学生环境中的 x(向量)与参考解环境中的 x(单个数字)不匹配。请注意,错误信息包含一段可读性很强的文字,说明了这个错误。

上面的示例是交互式的:您先设置状态,然后继续输入串联的检查函数。在更高层级,您可以使用 run_until_fail() 来封装检查代码。它会执行整组测试,直到出现失败为止:

library(testwhat)
setup_state(stu_code = 'x <- 4',
            sol_code = 'x <- 5')
res <- run_until_fail({
  ex() %>% check_object('x') %>% check_equal()
})
res$correct
## [1] FALSE
res$message
## [1] "The contents of the variable `x` aren't correct."

这种通过学生提交与参考解构建练习状态、再用 run_until_fail() 执行整组测试的基本方法,可以很容易嵌入到符合您需求的自动评分流程中。假设您的学生以 R 脚本的形式提交作业,您已将其下载到计算机上的 submissions 文件夹中(我们已预置了一些随机数据)。submissions 文件夹中有一批 R 脚本,每个脚本都包含一份学生提交:

dir('submissions')
##  [1] "student_a.R" "student_b.R" "student_c.R" "student_d.R" "student_e.R"
##  [6] "student_f.R" "student_g.R" "student_h.R" "student_i.R" "student_j.R"
readLines('submissions/student_a.R')
## [1] "x <- 4"
readLines('submissions/student_b.R')
## [1] "x <- 5"

现在我们可以遍历所有这些提交,并生成一个 data.frame,用于标注每位学生的提交是否正确:

library(testwhat)

folder_name <- 'submissions'
all_files <- file.path(folder_name, dir(folder_name))
student_results <- sapply(all_files, function(file) {
  student_code <- readLines(file)
  setup_state(sol_code = 'x <- 5',
              stu_code = student_code)
  res <- run_until_fail({
    ex() %>% check_object('x') %>% check_equal()
  })
  res$correct
})
results_df <- data.frame(name = all_files, correct = student_results,
                         row.names = NULL, stringsAsFactors = FALSE)
results_df
##                       name correct
## 1  submissions/student_a.R   FALSE
## 2  submissions/student_b.R    TRUE
## 3  submissions/student_c.R    TRUE
## 4  submissions/student_d.R    TRUE
## 5  submissions/student_e.R   FALSE
## 6  submissions/student_f.R    TRUE
## 7  submissions/student_g.R   FALSE
## 8  submissions/student_h.R   FALSE
## 9  submissions/student_i.R    TRUE
## 10 submissions/student_j.R    TRUE

这个小演示还比较粗糙,但希望已经传达了要点:通过一些 R 代码来构建 setup_state() 所需的输入,您就可以充分利用 testwhat 的各类检查函数。您在 run_until_fail() 中指定的测试既能核验学生的作业,也能生成有价值的反馈,指引他们发现并纠正错误。

如需进一步了解 testwhat,您可以访问其GitHub 仓库或包文档(由 pkgdown 生成)。其中既包含介绍如何使用某些检查函数的示例文档,也包含描述可用参数的函数参考。欢迎在 GitHub 提交 issue,或通过 [email protected] 与我们联系,我们非常重视反馈!祝教学顺利!

本文通过 RMarkdown 生成。您可以在此处查看源码。

主题