R 中的复杂数据转换优化

Complex data transformation optimization in R

我写了一些非常非常难看的代码来执行数据转换。我知道它可能可以被矢量化并显着加速,但我不确定如何。

我的数据是这样的:

scores<-as.data.frame(cbind(c(1,2,3,3,1,2,3,1,2,1,2,3),c(5,5,5,5,6,6,6,7,7,8,8,8),
c(0,1,1,1,1,0,1,.5,"fickle",1,2.2,1),c(1,1,1,2,1,1,1,1,1,1,1,1)))
names(dat)<-c("name","question_id","correct","attempt")
ids<-c(5,7,8)

我想要的是创建一个 studentXquestion 矩阵,显示他们在包含在 ids 向量中的每个问题上的最终尝试分数。如果学生没有完成该问题,它也会给出 NA,如果 "correct" 列中出现 0 或 1 以外的某些值,它也会给出 99,因为有些数据有点难看。

下面是我目前的代码。

students<-unique(scores$name)
finaldat<-data.frame(matrix(ncol=length(ids),nrow=length(unique(students))))

for(i in 1:length(students)){
  for(j in 1:length(ids)){
    attempts<-which(scores$question_id==ids[j] &
                        scores$name==students[i])
    if(length(attempts)==0){finaldat[i,j]<-NA}
    else{
    last.score<-as.numeric(scores$correct[attempts[which(attempts==length(attempts))]])
     finaldat[i,j]<-99
      if(length(last.score)==0){finaldat[i,j]<-NA}
      else{if(last.score==0 | last.score==1){
        finaldat[i,j]<-last.score
      }
      }
    }
  }
}        

finaldat 

除了 运行 真的很慢,它不起作用,因为我无法绕过 last.score 行。我确定有一个整洁的 verse 解决方案,但我很难过。任何提示将不胜感激。

所以输出数据将是:

cbind(c(0,1,1),c(99,99,NA),c(1,99,1))

我们可以看到第6题已经被排除,所有非二进制都被转换为99,缺失值是NA,只保留最后的尝试。

这是使用 dplyrtidyr 的版本。

library(dplyr)
library(tidyr)

scores <- data.frame(name        = c(1, 2, 3, 3, 1, 2, 3, 1, 2, 1, 2, 3),
                     question_id = c(5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8),
                     correct     = c(0, 1, 1, 1, 1, 0, 1, .5 , "fickle", 1, 2.2, 1),
                     attempt     = c(1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1),
                     stringsAsFactors = FALSE)

result <- scores %>%
  group_by(name, question_id) %>% 
  filter(attempt == max(as.numeric(as.character(attempt)))) %>% 
  mutate(correct = if(correct != "1" && correct != "0") "99" else correct) %>%
  select(name, question_id, correct) %>%
  ungroup() %>%
  pivot_wider(names_from = question_id, values_from = correct)

result
#> # A tibble: 3 x 5
#>    name `5`   `6`   `7`   `8`  
#>   <dbl> <chr> <chr> <chr> <chr>
#> 1     1 0     1     99    1    
#> 2     2 1     0     99    99   
#> 3     3 1     1     <NA>  1  

只是为了添加另一个解决方案,我已经在努力

library(data.table)
library(dplyr)
library(forcats)

dt.scores <- data.table(scores)

dt.scores[, correct := as.integer(as.character(fct_other(correct, keep = c("0", "1"), other_level = "99"))) ]
dt.scores[, attempt := as.integer(as.character(attempt)) ]

dt.scores[,.(name, question_id, correct)] %>% pivot_wider(data = . , names_from = question_id, values_from = correct, values_fn = list(correct = max))