【问题标题】:How can I merge rows by multiple columns, taking the last non-null value in multiple columns?如何按多列合并行,获取多列中的最后一个非空值?
【发布时间】:2021-10-25 19:06:58
【问题描述】:

我正在学习如何使用 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 添加到数据集中,我可以运行相同的代码并获得相同的结果。我经常不得不从我的数据集中添加或删除变量,并且不得不返回并在整个数据转换过程中手动更改它们变得非常快。

【问题讨论】:

    标签: r dplyr data.table aggregate


    【解决方案1】:

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

    【讨论】:

    • 谢谢!这可能是我的下一个问题,但你已经回答了。
    【解决方案2】:

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

    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
    

    【讨论】:

    • 哇,简洁优雅。很好的答案。另外,r 很棒!
    猜你喜欢
    • 2014-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多