首页 > 解决方案 > 如何按多列合并行,获取多列中的最后一个非空值?

问题描述

我正在学习如何使用 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 或其他附加组件都没有关系。

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

标签: rdplyrdata.tableaggregate

解决方案


另一个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

推荐阅读