【问题标题】:Calculating grades in r在 r 中计算成绩
【发布时间】:2014-05-07 23:42:48
【问题描述】:

我正在计算一门课程的最终平均成绩。大约有 500 名学生,成绩被组织到一个.csv 文件中。列标题包括:

Name, HW1, ..., HW10, Quiz1, ..., Quiz5, Exam1, Exam2, Final

每个的权重都不同,这不应该是编程问题。但是,每个学生的最低 2 HW 和最低测验将被丢弃。我怎么能在 r 中编程呢?请注意,每个学生放弃的硬件/测验可能不同(即学生 A 放弃了 HW2、HW5、Quiz2,学生 B 放弃了 HW4、HW8、Quiz1)。

【问题讨论】:

  • 尝试创建small reproducible example,展示您尝试过的内容以及遇到的问题。
  • 一般策略是使用reshape::melt 以“整洁”格式获取数据(每个学生和作业一行),然后使用dplyr 删除最低值并执行任何操作您需要的其他权重/平均值。 dplyr 有很好的文档。
  • 感谢您对 dplyr 的提示。我会调查一下(我有 Excel 中的数据,所以我认为它的格式已经很整洁了)。
  • 我现在正在研究 dplyr 解决方案。它往往比 do.call 更直观(也更快)
  • @KirkFogg 从 dplyr 的作者那里阅读了一些关于“整洁数据”的文章:vita.had.co.nz/papers/tidy-data.pdf,这是思考数据结构的好方法。跨度>

标签: r


【解决方案1】:

这是一个更简单的解决方案。 sum_after_drop 函数采用向量 x 并删除 i 最低分数并将剩余分数相加。我们为数据集中的每一行调用这个函数。 ddply 对这份工作来说太过分了,但让事情变得简单。您应该可以使用 apply 执行此操作,但您必须将最终结果转换为数据框。

然后可以在dd2 上进行实际成绩计算。请注意,将cut 函数与breaks 结合使用是一种从总分中获取字母成绩的简单方法。

library(plyr)
sum_after_drop <- function(x, i){
  sum(sort(x)[-(1:i)])
}

dd2 = ddply(dd, .(Name), function(d){
  hw = sum_after_drop(d[,grepl("HW", nms)], 1)
  qz = sum_after_drop(d[,grepl("Quiz", nms)], 1)
  data.frame(hw = hw, qz = qz)
})

【讨论】:

    【解决方案2】:

    这是一个关于如何使用 reshape2 包和基本函数来处理它的草图。

    #sample data
    set.seed(734)
    dd<-data.frame(
        Name=letters[1:20],
        HW1=rpois(20,7),
        HW2=rpois(20,7),
        HW3=rpois(20,7),
        Quiz1=rpois(20,15),
        Quiz2=rpois(20,15),
        Quiz3=rpois(20,15)
    )
    

    现在我将其转换为长格式并拆分字段名称

    require(reshape2)
    mm<-melt(dd, "Name")
    mm<-cbind(mm,
        colsplit(gsub("(\\w+)(\\d+)","\\1:\\2",mm$variable, perl=T), ":",
        names=c("type","number"))
    )
    

    现在我可以使用 by() 为每个名称获取一个 data.frame 并进行其余的计算。在这里,我只是放弃最低的家庭作业和最低的测验,我给家庭作业的权重为 0.2,测验的权重为 0.8(假设所有家庭作业的价值为 15 分,测验为 25 分)。

    grades<-unclass(by(mm, mm$Name, function(x) {
        hw <- tail(sort(x$value[x$type=="HW"]), -1);
        quiz <- tail(sort(x$value[x$type=="Quiz"]), -1);
        (sum(hw)*.2 + sum(quiz)*.8) / (length(hw)*15*.2+length(quiz)*25*.8)
    }))
    attr(grades, "call")<-NULL   #get rid of crud from by()
    grades;
    

    让我们检查一下我们的工作。看学生“c”

       Name HW1 HW2 HW3 Quiz1 Quiz2 Quiz3
          c   6   9   7    21    20    14
    

    他们的成绩应该是

    ((9+7)*.2+(21+20)*.8) / ((15+15)*.2 + (25+25)*.8) = 0.7826087
    

    事实上,我们看到了

    grades["c"] == 0.7826087
    

    【讨论】:

      【解决方案3】:

      这是dplyr 的解决方案。它按学生和作业类型对分数进行排名(即计算学生 1 的所有作业的排名顺序等),然后过滤掉最低的 1(或 2,或其他)。 dplyr 的语法非常直观——您应该能够相当轻松地浏览代码。

      # Load libraries
      library(reshape2)
      library(dplyr)
      
      # Sample data
      grades <- data.frame(name=c("Sally", "Jim"),
                           HW1=c(10, 9),
                           HW2=c(10, 5),
                           HW3=c(5, 10),
                           HW4=c(6, 9),
                           HW5=c(8, 9),
                           Quiz1=c(9, 5),
                           Quiz2=c(9, 10),
                           Quiz3=c(10, 8),
                           Exam1=c(95, 96))
      
      # Melt into long form
      grades.long <- melt(grades, id.vars="name", variable.name="graded.name") %.%
        mutate(graded.type=factor(sub("\\d+","", graded.name)))
      grades.long
      
      # Remove the lowest scores for each graded type
      grades.filtered <- grades.long %.%
        group_by(name, graded.type) %.%
        mutate(ranked.score=rank(value, ties.method="first")) %.%  # Rank all the scores
        filter((ranked.score > 2 & graded.type=="HW") |  # Ignore the lowest two HWs
               (ranked.score > 1 & graded.type=="Quiz") |  # Ignore the lowest quiz
               (graded.type=="Exam"))
      grades.filtered
      
      # Calculate the average for each graded type
      grade.totals <- grades.filtered %.%
        group_by(name, graded.type) %.%
        summarize(total=mean(value))
      grade.totals
      
      # Unmelt, just for fun
      final.grades <- dcast(grade.totals, name ~ graded.type, value.var="total")
      final.grades
      

      从技术上讲,您可以将summarize(total=mean(value)) 添加到grades.filtered 数据框,而不是制作单独的grade.totals 数据框——出于教学原因,我将它们分成多个数据框。

      【讨论】:

      • 例如,当我收到以下错误消息时是什么意思: (graded.type=="Exam")) eval 中的错误(expr, envir, enclos) : index out of bounds
      • filter 命令实际上只是一个很长的条件语句,用换行符分隔以提高可读性。确保所有内容都正确嵌套:filter((stuff for HW) | (stuff for quizzes) | (stuff for exams) | (stuff for other things, like the final)).
      • 如果您按原样运行代码(在进行自己的修改之前),它是否工作?它应该给this output
      • 您的代码工作正常,当我的样本量较小时,它对我有用。但是,当我的数据集是 500 名学生的整个班级名册时,它就不起作用了。我会回去看看是否所有的格式都正确。谢谢!
      • 又出现了一个问题,因为我有 HW11 和 HW12。当我使用 gsub 时,它只删除第一个“1”,而不是第二个数字(我认为)。所以在grades.long中,这些仍然被列为“HW1”,而其他的都被列为“HW”,这就是我最终想要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2019-08-25
      • 2017-03-01
      • 1970-01-01
      • 2015-05-31
      相关资源
      最近更新 更多