2015-03-30 4 views
2

Я работаю над вычислением статистики предметов для серии экзаменов по множественному выбору. У меня есть решение с использованием mapply, которое технически работает, но для вычисления одной из более сложных статистик требуется несколько часов. Первый набор данных, который у меня есть, - это тот, который содержит отдельную строку для каждого вопроса, который каждый студент ответил за каждую оценку.R 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) 

Я был счастлив, что я был в состоянии заставить его работать, но когда я делаю это с большими наборами данных (~ 7 миллионов строк для «ДФА», ~ 2000 строк для «уникального), это занимает довольно (несколько часов) .Какие советы по другим способам решения этой проблемы более эффективны? Я узнал, что одна проблема заключается в том, что моя функция создает копию оригинального большого набора данных каждый раз, когда он проходит, но я не знаю, знают, как сделать эту проблему без этого.

Я до сих пор считаю себя новичком для этого вида использования для R, поэтому любые советы были бы оценены!

+1

Мысли: (1) не подмножают весь 'df', используйте' suba <- df $ evaluation == a.id', 'subi <- df $ item == i.id' и, возможно (если по-настоящему двоичный) 'subs <- df $ score == 0' и повторно использовать вы-что-нибудь из этих логических векторов на' df'; (2) то же самое для хранения списка '...$ student% in% correct', нет необходимости пересчитывать его; (3) если данные действительно велики, возможно, 'data.table',' dplyr' или один из пакетов SQL обеспечит лучшую производительность. – r2evans

+1

действительно ли это работает правильно для вас? Я получаю предупреждения в 'mapply'. 'aggregate', скорее всего, является вашим основным узким местом, за которым следуют все подмножества. Я бы переключился на dplyr – rawr

+0

@rawr вы только что сказали ... (и я цитирую) «Я бы переключился на ** dplyr **»? –

ответ

0

Когда Вы выполняете

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. Нет необходимости создавать correct и wrong. Относитесь к значению этого столбца в качестве индикатора, чтобы рассказать вам, был ли ученик правильным или неправильным.

Вместо этого сделайте следующее:

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.0 и x.1. Если вы создаете подмножество на i.id и затем агрегируете это подмножество данных, это также должно сэкономить вам несколько шагов. Тот факт, что вы проверяете каждого учащегося независимо от того, находятся ли они в correct или wrong дважды (для score и points.possible), также довольно дорого.

Смежные вопросы