【问题标题】:R mapply function running slowlyR mapply函数运行缓慢
【发布时间】:2015-06-04 00:08:09
【问题描述】:

我正在计算一系列多项选择考试的项目统计信息。我有一个使用 mapply 的解决方案,该解决方案在技术上可行,但需要几个小时来计算其中一个更复杂的统计数据。我拥有的第一个数据集是每个学生在每次评估中回答的每个问题都包含一个单独的行。

df <- data.frame(c(rep("s1", 5), rep("s2", 5), rep("s3", 5),rep("s4", 5)),"a1", c("i1", "i2", "i3", "i4", "i5"), c(1, 0), 1)

colnames(df) <- c("student", "assessment", "item", "score", "points.possible")

我要做的第一步(并且只做一次)是创建一个包含所有独特项目的表格。在这种情况下,这很简单,因为只有一个评估和 5 个项目。

unique <- subset(df[,c("assessment", "item")], !duplicated(df[,c("assessment", "item")]))

然后我需要为这些项目中的每一项计算一个统计数据。然而,棘手的部分是计算需要计算学生在整个评估中获得的总分。这是我为此编写的函数。

fun1 <- function(a.id, i.id) {
  # subset original dataframe for just one assessment
  subsetdf <- df[df$assessment == a.id,]

  # generate list of students that got the item right and wrong
  correct <- subsetdf$student[subsetdf$item==i.id & subsetdf$score==1]
  wrong <- subsetdf$student[subsetdf$item==i.id & subsetdf$score==0]

  # scores by student
  scores <- aggregate(score ~ student, data=subsetdf,sum)/aggregate(points.possible ~ student, data=subsetdf, sum)  

  # average scores for students that got item right/wrong
  x.1 <- sum(subsetdf$score[subsetdf$student %in% correct])/sum(subsetdf$points.possible[subsetdf$student %in% correct])
  x.0 <- sum(subsetdf$score[subsetdf$student %in% wrong])/sum(subsetdf$points.possible[subsetdf$student %in% wrong])

  # percent of students that got item right
  p <- length(correct)/(length(correct)+length(wrong))

  # final stat calculation
  r <- ((x.1-x.0)*sqrt(p*(1-p)))/sd(scores[,2])
  print(r)
}

然后我使用 mapply 在整个原始数据集上循环这个函数,同时使用较小的数据集作为输入。

unique$r <- mapply(fun1, unique$assessment, unique$item)

我很高兴我能够让它工作,但是当我使用更大的数据集(“df”约 700 万行,“unique”约 2000 行)时,需要相当长的时间(几个小时)。有关解决此问题的其他更有效方法的任何提示?我了解到一个问题是我的函数每次循环时都会创建原始大型数据集的副本,但我不知道如何没有那个问题。

我仍然认为自己是这种 R 用法的初学者,所以任何建议都将不胜感激!

【问题讨论】:

  • 想法:(1)不要子集整个df,使用suba &lt;- df$assessment == a.idsubi &lt;- df$item == i.id,也许(如果真的是二进制)subs &lt;- df$score == 0,并重用你知道的df 上的这些逻辑向量是什么? (2) 存储...$student %in% correct的列表同理,无需重新计算; (3)如果数据真的很大,也许data.tabledplyr或者其中一个SQL包会提供更好的性能。
  • 这是否适合您?我在mapply 中收到警告。 aggregate 很可能是您的主要瓶颈,其次是所有子集。我会切换到 dplyr
  • @rawr 你刚才说...(我引用)“我会切换到 dplyr”?
  • @r2evans 我在我完成的谷歌搜索中一直在 data.table 中运行。我以前没用过,但也许我会试一试。
  • @rawr 很好,完全忘记了这一点。你让我担心了一分钟。

标签: r memory data.table memory-efficient mapply


【解决方案1】:

当你执行时

scores <- aggregate(score ~ student, data=subsetdf,sum)/aggregate(points.possible ~ student, data=subsetdf, sum)  

结果不是严格的数字,结果是一个数据框(例如a.id = 'a1', i.id = 'i1'):

> aggregate(score ~ student, data=subsetdf,sum)
      student score
1      s1     3
2      s2     2
3      s3     3
4      s4     2

所以当你将两者相除时,'s1'/'s1' 的结果不是数字并引发警告。

  1. 无需创建correctwrong。将该列的值视为一个指标,告诉您学生是对还是错。

相反,请执行以下操作:

scores <- aggregate(subsetdf[,c('score', 'points.possible')], by = list(subsetdf$student), sum)
names(scores) <- c('student', 'score','points.possible')
scores$avg.score <- scores$score/scores$points.possible

我会为x.0x.1 做同样的事情。如果您通过i.id 创建一个子集,然后聚合该数据帧的子集,这也应该可以为您节省几个步骤。 您要检查每个学生是否在 correctwrong 两次(对于 scorepoints.possible)这一事实也非常昂贵。

【讨论】:

    猜你喜欢
    • 2017-09-02
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 2019-04-27
    • 1970-01-01
    • 2021-11-25
    相关资源
    最近更新 更多