如何按多列合并行,取多列中的最后一个非空值?

How can I merge rows by multiple columns, taking the last non-null value in multiple columns?

我正在学习如何使用 R 进行聚合操作,但有一种我经常使用的特定类型的操作,我希望它在 R 或其附加组件中有一个内置操作,或者在至少有比我想出的更好的实现。我不确定这是否有专门的术语,但我称其为 MUSH 操作。在这里,您基本上可以将脏数据集与随机位置的一堆缺失数据混搭成一个干净的数据集。有时我需要第一个非空值,有时是最后一个,有时是最后一个值,不管它是否为空。为了简单起见,这是一个我只担心最后一个非空值的示例。

假设我有一份学生名单,其中包含他们的考试成绩、科目和主持考试的老师。数据录入人员有点粗心(纯属假设),漏掉了一些老师的名字。另外,有少数学生在刚开始考试时缺席,不得不在以后的时间参加考试。

这是一个示例数据集:

   STUDENTID SUBJECT TEACHER SCORE
1:       100     Art    <NA>    96
2:       100     Art   Smith    NA
3:       100 Science   Jones    75
4:       101     Art   Smith    NA
5:       101     Art   Smith    50
6:       101 Science   Jones    75
7:       102     Art    <NA>    80
8:       102     Art   Smith    NA

这是设置数据框的代码:

# Setup data
a<-data.table(cbind(
  "STUDENTID"=c("100","100","101","102")
  ,"SUBJECT"=c("Art","Science","Art","Art"))
  ,"TEACHER"=c("Smith","Jones","Smith","Smith")
  ,"SCORE"=c(NA,75,50,NA)
)
b<-data.table(
  "STUDENTID"=c("100","101","101","102")
  ,"SUBJECT"=c("Art","Art","Science","Art")
  ,"TEACHER"=c(NA,"Smith","Jones",NA)
  ,"SCORE"=c(96,NA,75,80)
)
# Merge data
d <- merge(a, b, by = NULL, all = TRUE)
# Show output
d

我想通过合并所有基于 STUDENTID 和 SUBJECT 的行来清理这个数据集。我想为其他每一行取第一个非空值。结果输出应如下所示:

   STUDENTID SUBJECT TEACHER SCORE
1:       100     Art   Smith    96
2:       100 Science   Jones    75
3:       101     Art   Smith    50
4:       101 Science   Jones    75
5:       102     Art   Smith    80

以下代码完成此任务:

# dplyr to get last non null values
library(dplyr)
d <- d %>%
  group_by(STUDENTID, SUBJECT) %>% 
  mutate(
    bestTeacherRow = dplyr::last(na.omit(TEACHER)),
    bestScoreRow = dplyr::last(na.omit(SCORE))
  )
# Replace values with non-nulls
d$TEACHER <- d$bestTeacherRow
d$SCORE <- d$bestScoreRow
# Remove duplicates
d <- unique(d)
#Show output
d

有没有更优雅的方法来做到这一点?使用 dplyr 或其他附加组件并不重要。

更重要的是,有没有办法在不指定每个 header/variable 名称的情况下执行此操作?例如,如果在未来的某个时间将执行测试的 DATE 添加到数据集中,我可以 运行 相同的代码并获得相同的结果。我经常不得不在我的数据集中添加或删除变量,并且不得不在转换数据的整个过程中返回并手动更改它们变得非常快。

我们可以按组对 NA 元素执行 order 并删除 na.omit

NA
library(data.table)
na.omit(d[, lapply(.SD, function(x) x[order(is.na(x))]), .(STUDENTID, SUBJECT)])

-输出

    STUDENTID SUBJECT TEACHER SCORE
1:       100     Art   Smith    96
2:       100 Science   Jones    75
3:       101     Art   Smith    50
4:       101 Science   Jones    75
5:       102     Art   Smith    80

另一个 data.table 选项,它只查找第一个非 NA(如果存在):

d[, lapply(.SD, function(z) z[!is.na(z)][1]), by = .(STUDENTID, SUBJECT)]
#    STUDENTID SUBJECT TEACHER SCORE
#       <char>  <char>  <char> <num>
# 1:       100     Art   Smith    96
# 2:       100 Science   Jones    75
# 3:       101     Art   Smith    50
# 4:       101 Science   Jones    75
# 5:       102     Art   Smith    80