如何在R/exams的nops_eval结果中自动识别题型分组并计算分数?
我非常喜欢使用R/exams——在给约200名学生评分时,它是最节省时间的工具。
背景
我使用exams2nops()生成纸质试卷,每份试卷包含两部分:
- 授课模块(Lecture section)——30道加权单选题(分值1至4分)。
- 研讨模块(Seminar section)——15道加权单选题。
评分时我需要:
- 30道授课模块题目的总分,
- 15道研讨模块题目的总分,
- 通过公式
0.70 × 授课模块总分 + 0.30 × 研讨模块总分计算最终成绩。
问题
运行nops_eval()后,我得到一个数据框,每道题目对应一列(列名从points.1到points.45)。如何通过程序化方式识别哪些列属于授课模块、哪些属于研讨模块,而非硬编码列名?
当前(手动)解决方案:
library(dplyr) result <- data_frame %>% filter(exam == 25062200004) %>% # 选择某一试卷版本 select(starts_with("points."), name, id) %>% mutate( pct_seminar = rowSums(across(c(points.2, points.7, points.8, points.9, points.10, points.15, points.18, points.21, points.24, points.27, points.33, points.35, points.37, points.38, points.45))), total_points = rowSums(across(points.1:points.45)), pct_course = total_points - pct_seminar + if_else(id %in% exp_ids, 0.5, 0) )
程序化解决方案
核心思路是从试卷生成阶段的元数据中提取题目分组信息,彻底替代硬编码列名的操作:
1. 保留试卷生成时的元数据
调用exams2nops()时,务必保存完整返回结果,其中包含每道题的配置信息:
# 生成试卷时保存完整输出(含元数据) nops_output <- exams2nops( questions = your_question_list, # 你的题目列表 n = 1, # 按需调整试卷版本数量 dir = "output", meta = TRUE # 开启元数据保存 )
2. 构建题目分组与列名的映射
根据题目模块的划分规则(按顺序或标签),生成分组与points.*列的对应关系:
- 按题目顺序划分:如果前30道是授课模块、后15道是研讨模块,直接生成映射:
question_groups <- c(rep("lecture", 30), rep("seminar", 15)) points_cols <- paste0("points.", seq_along(question_groups)) group_col_map <- tibble( col_name = points_cols, group = question_groups )
- 按题目标签划分:如果出题时给每个题目添加了
tags: lecture/tags: seminar标签,直接从元数据提取:
# 从元数据中读取每道题的标签 question_tags <- sapply(nops_output$meta$question, function(q) q$tags) group_col_map <- tibble( col_name = paste0("points.", seq_along(question_tags)), group = question_tags )
3. 动态计算各模块总分
用映射结果筛选列,完成评分计算:
library(dplyr) # 提取两个模块对应的列名 lecture_cols <- group_col_map %>% filter(group == "lecture") %>% pull(col_name) seminar_cols <- group_col_map %>% filter(group == "seminar") %>% pull(col_name) result <- data_frame %>% filter(exam == 25062200004) %>% select(all_of(c(points_cols, "name", "id"))) %>% mutate( pct_seminar = rowSums(across(all_of(seminar_cols))), pct_course = rowSums(across(all_of(lecture_cols))) + if_else(id %in% exp_ids, 0.5, 0), final_score = 0.7 * pct_course + 0.3 * pct_seminar # 计算最终成绩 )
内容的提问来源于stack exchange,提问作者George G
相关产品推荐
相关产品推荐

