将包含 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)
这里有一个更通用的解决方案(使用 unique
、na.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.omit
和 unique
) 不相关。
一种简单的方法是 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.omit
和 unique
的另一种选择:
df %>%
group_by(A) %>%
summarise(across(everything(), ~ na.omit(unique(.))))
我知道这是一个重复的问题,但我似乎无法再次找到 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)
这里有一个更通用的解决方案(使用 unique
、na.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.omit
和 unique
) 不相关。
一种简单的方法是 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.omit
和 unique
的另一种选择:
df %>%
group_by(A) %>%
summarise(across(everything(), ~ na.omit(unique(.))))