如何填写 R 中按重复 ID 分组的各列的 NA
How to fill in NAs of various columns grouped by duplicated IDs in R
我有一个包含 id
、colA
和 colB
列的 table。数据包含重复的 id 列,其中某些行的 colA
或 colB
为空,但其重复的 id
具有有效值。我想清理数据,以便删除重复项,但有完整的数据。例如我的数据看起来像
id | colA | colB
1 NA X
1 Y X
2 Z NA
2 Z Y
3 Z Y
3 Z Y
4 NA NA
4 NA NA
我希望我的数据框看起来像
id | colA | colB
1 Y X
2 Z Y
3 Z Y
4 NA NA
我通常使用 ifelse
语句来替换缺失值,但我对如何在 id
重复的情况下使用它感到困惑。
创建数据框 - 如果您 post 制作样本数据的代码
会有所帮助
df <- data.frame(id = c(rep(seq(1:4), each =2)), colA = c(NA, "y", "z", "z", "z", "z", NA, NA), colB = c("x", "x", NA, "y", "y", "y", NA, NA))
删除具有单个 NA 的行
for(i in 1:nrow(df)){
if(is.na(df[i,]$colA) & !is.na(df[i,]$colB) | !is.na(df[i,]$colA) & is.na(df[i,]$colB)){
df <- df[-i,]
}
}
删除剩余的重复项(即双 NA 行)
df <- df[!duplicated(df), ]
输出
df
可能是一种计算效率更高的方法,但这应该可行。
首先添加一列,说明每行有多少 NA
。然后使用 dplyr
,首先删除重复的行,然后为每个 id 保留缺失值最少的行 -
df$test <- rowSums(is.na(df))
df %>%
filter(!duplicated(.)) %>%
arrange(id, test) %>%
group_by(id) %>%
filter(row_number() == 1) %>%
ungroup() %>%
select(-test)
# A tibble: 4 x 3
id colA colB
<int> <chr> <chr>
1 1 y x
2 2 z y
3 3 z y
4 4 <NA> <NA>
编辑:
实际上不需要先删除重复项。只保留每个 id 的缺失值最少的行也应该有效 -
df$test <- rowSums(is.na(df))
df %>%
arrange(id, test) %>%
group_by(id) %>%
filter(row_number() == 1) %>%
ungroup() %>%
select(-test)
数据-
df <- data.frame(
id = c(rep(seq(1:4), each =2)), colA = c(NA, "y", "z", "z", "z", "z", NA, NA),
colB = c("x", "x", NA, "y", "y", "y", NA, NA), stringsAsFactors = F)
这个答案在很大程度上取决于您的实际数据在结构上与您的示例数据相似。
您的数据:
df1 <- structure(list(id = c(1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L),
colA = c(NA, "Y", "Z", "Z", "Z", "Z", NA, NA),
colB = c("X", "X", NA, "Y", "Y", "Y", NA, NA)),
class = "data.frame",
row.names = c(NA, -8L))
假设,如您的示例所示,每个 id
出现两次,并且其中一个观察值是 NA
,它是 id
的第一个观察值,那么这是有效的:
library(dplyr)
library(tidyr)
df1 %>%
group_by(id) %>%
fill(colA, colB, .direction = "up") %>%
ungroup() %>%
distinct()
# A tibble: 4 x 3
id colA colB
<int> <chr> <chr>
1 1 Y X
2 2 Z Y
3 3 Z Y
4 4 NA NA
如果 id
的第二个观察值可以是 NA
,您可以尝试在第一个观察值之后添加第二个 fill
,但这次向下填充:
df1 %>%
group_by(id) %>%
fill(colA, colB, .direction = "up") %>%
fill(colA, colB, .direction = "down") %>%
ungroup() %>%
distinct()
我有一个包含 id
、colA
和 colB
列的 table。数据包含重复的 id 列,其中某些行的 colA
或 colB
为空,但其重复的 id
具有有效值。我想清理数据,以便删除重复项,但有完整的数据。例如我的数据看起来像
id | colA | colB
1 NA X
1 Y X
2 Z NA
2 Z Y
3 Z Y
3 Z Y
4 NA NA
4 NA NA
我希望我的数据框看起来像
id | colA | colB
1 Y X
2 Z Y
3 Z Y
4 NA NA
我通常使用 ifelse
语句来替换缺失值,但我对如何在 id
重复的情况下使用它感到困惑。
创建数据框 - 如果您 post 制作样本数据的代码
会有所帮助df <- data.frame(id = c(rep(seq(1:4), each =2)), colA = c(NA, "y", "z", "z", "z", "z", NA, NA), colB = c("x", "x", NA, "y", "y", "y", NA, NA))
删除具有单个 NA 的行
for(i in 1:nrow(df)){
if(is.na(df[i,]$colA) & !is.na(df[i,]$colB) | !is.na(df[i,]$colA) & is.na(df[i,]$colB)){
df <- df[-i,]
}
}
删除剩余的重复项(即双 NA 行)
df <- df[!duplicated(df), ]
输出
df
可能是一种计算效率更高的方法,但这应该可行。
首先添加一列,说明每行有多少 NA
。然后使用 dplyr
,首先删除重复的行,然后为每个 id 保留缺失值最少的行 -
df$test <- rowSums(is.na(df))
df %>%
filter(!duplicated(.)) %>%
arrange(id, test) %>%
group_by(id) %>%
filter(row_number() == 1) %>%
ungroup() %>%
select(-test)
# A tibble: 4 x 3
id colA colB
<int> <chr> <chr>
1 1 y x
2 2 z y
3 3 z y
4 4 <NA> <NA>
编辑: 实际上不需要先删除重复项。只保留每个 id 的缺失值最少的行也应该有效 -
df$test <- rowSums(is.na(df))
df %>%
arrange(id, test) %>%
group_by(id) %>%
filter(row_number() == 1) %>%
ungroup() %>%
select(-test)
数据-
df <- data.frame(
id = c(rep(seq(1:4), each =2)), colA = c(NA, "y", "z", "z", "z", "z", NA, NA),
colB = c("x", "x", NA, "y", "y", "y", NA, NA), stringsAsFactors = F)
这个答案在很大程度上取决于您的实际数据在结构上与您的示例数据相似。
您的数据:
df1 <- structure(list(id = c(1L, 1L, 2L, 2L, 3L, 3L, 4L, 4L),
colA = c(NA, "Y", "Z", "Z", "Z", "Z", NA, NA),
colB = c("X", "X", NA, "Y", "Y", "Y", NA, NA)),
class = "data.frame",
row.names = c(NA, -8L))
假设,如您的示例所示,每个 id
出现两次,并且其中一个观察值是 NA
,它是 id
的第一个观察值,那么这是有效的:
library(dplyr)
library(tidyr)
df1 %>%
group_by(id) %>%
fill(colA, colB, .direction = "up") %>%
ungroup() %>%
distinct()
# A tibble: 4 x 3
id colA colB
<int> <chr> <chr>
1 1 Y X
2 2 Z Y
3 3 Z Y
4 4 NA NA
如果 id
的第二个观察值可以是 NA
,您可以尝试在第一个观察值之后添加第二个 fill
,但这次向下填充:
df1 %>%
group_by(id) %>%
fill(colA, colB, .direction = "up") %>%
fill(colA, colB, .direction = "down") %>%
ungroup() %>%
distinct()