将包含 NA 的数据框中的行组合成完整的行

combine rows in data frame containing NA to make complete row

我知道这是一个重复的问题,但我似乎无法再次找到 post

使用以下数据

df <- data.frame(A=c(1,1,2,2),B=c(NA,2,NA,4),C=c(3,NA,NA,5),D=c(NA,2,3,NA),E=c(5,NA,NA,4))

  A  B  C  D  E
  1 NA  3 NA  5
  1  2 NA  2 NA
  2 NA NA  3 NA
  2  4  5 NA  4

A 分组,我想要使用 tidyverse 解决方案

的以下输出
  A  B  C  D  E
  1  2  3  2  5
  2  4  5  3  4

我在 A 中有很多组。我想我看到了一个使用 coalesce 的答案,但不确定如何让它发挥作用。我想要一个 也适用于 characters 的解决方案。谢谢!

不是tidyverse,但这是一个基本的 R 解决方案

df <- data.frame(A=c(1,1),B=c(NA,2),C=c(3,NA),D=c(NA,2),E=c(5,NA))
sapply(df, function(x) x[!is.na(x)][1])
#A B C D E 
#1 2 3 2 5 

有更新的数据

do.call(rbind, lapply(split(df, df$A), function(a) sapply(a, function(x) x[!is.na(x)][1])))
#  A B C D E
#1 1 2 3 2 5
#2 2 4 5 3 4

我还没有想出如何将 coalesce_by_column 函数放入 dplyr 管道中,但这行得通:

coalesce_by_column <- function(df) {
  return(coalesce(df[1], df[2]))
}

df %>%
  group_by(A) %>%
  summarise_all(coalesce_by_column)

##       A     B     C     D     E
##   <dbl> <dbl> <dbl> <dbl> <dbl>
## 1     1     2     3     2     5
## 2     2     4     5     3     4

编辑:将@Jon Harmon 的解决方案添加到超过 2 个组的成员中

# Supply lists by splicing them into dots:
coalesce_by_column <- function(df) {
  return(dplyr::coalesce(!!! as.list(df)))
}

df %>%
  group_by(A) %>%
  summarise_all(coalesce_by_column)

#> # A tibble: 2 x 5
#>       A     B     C     D     E
#>   <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1     1     2     3     2     5
#> 2     2     4     5     3     4

我们可以使用fill来填充所有缺失值。然后为每个组过滤一行。

library(dplyr)
library(tidyr)

df2 <- df %>%
  group_by(A) %>%
  fill(everything(), .direction = "down") %>%
  fill(everything(), .direction = "up") %>%
  slice(1)

感谢@Roger-123,上面的代码可以进一步简化如下。

df2 <- df %>%
  group_by(A) %>%
  fill(everything(), .direction = "downup") %>%
  slice(1)

这里有一个更通用的解决方案(使用 uniquena.omit 排序创建 coalesce),它可以处理具有重叠信息的两行以上。超级简单又前卫。

> df <- data.frame(A=c(1,1,2,2,2),B=c(NA,2,NA,4,4),C=c(3,NA,NA,5,NA),D=c(NA,2,3,NA,NA),E=c(5,NA,NA,4,4))

> df
  A  B  C  D  E
1 1 NA  3 NA  5
2 1  2 NA  2 NA
3 2 NA NA  3 NA
4 2  4  5 NA  4
5 2  4 NA NA  4

> df %>% group_by(A) %>% summarise_all(funs( na.omit(unique(.)) ))
# A tibble: 2 x 5
      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     2     3     2     5
2     2     4     5     3     4

另一种 tidyverse 可能性是:

df %>%
 gather(var, val, -A, na.rm = TRUE) %>%
 group_by(A, var) %>%
 distinct(val) %>%
 spread(var, val)

      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     2     3     2     5
2     2     4     5     3     4

在这里,它首先执行从宽到长的数据转换,排除 "A" 列并删除缺失值。其次,它按 "A" 列和变量名称分组。第三,它删除了重复值。最后,它 returns 将数据转换为原始宽格式。

这在功能上与@Oriol Mirosa 的答案相同,无需自定义函数:

编辑:根据@thelatemail 的评论,必须省略 NA。 @MrFlick 在上面链接的重复线程中也给出了这个答案。

df %>% group_by(A) %>% summarise_all(~first(na.omit(.)))

我想补充一点,因为它似乎经常出现在我面前,而且我已经多次重访此话题。 @Oriol Mirosa 的回答有效,但我很抗拒它,因为它太复杂以至于难以记住(因此我对这个线程 return)。

就我个人而言,我也不喜欢编写不需要的小自定义函数。尝试将 coalesce_by_column 替换为实际的 coalesce 调用会导致类型错误(我觉得这很奇怪,因为行之间没有交互,但无论如何)。这可以通过首先执行 mutate_all(as.character) 来解决,但我的目标是尽量减少语法,以便在运行时轻松记住它。

此外,这种替换改变了行为,使得列中的 non-identical 值会引发错误(我不明白为什么有时函数中的行为会略有不同)。这种行为在某些情况下可能是首选,但在那种情况下我会推荐@Jerry T 的解决方案,因为没有自定义函数,并且使用的函数是熟悉的、可读的,并且它们的顺序(na.omitunique) 不相关。

一种简单的方法是 summarise,方法是 max 每个组的所有列 tidyverse:

library(tidyverse)

df %>% 
  group_by(A) %>% 
  summarise(across(everything(), ~ max(., na.rm = T)))

输出

      A     B     C     D     E
  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     2     3     2     5
2     2     4     5     3     4

使用 na.omitunique 的另一种选择:

df %>% 
  group_by(A) %>% 
  summarise(across(everything(), ~ na.omit(unique(.))))