想要删除重复的行,除非列中存在 NA 值
Want to remove duplicated rows unless NA value exists in columns
我有一个包含 4 列的数据 table:ID、名称、Rate1、Rate2。
我想删除 ID、Rate1 和 Rate 2 相同的重复项,但是 如果它们都是 NA,我想保留两行。
基本上,我想有条件地删除重复项,但前提是条件!= NA。
比如我想要这样:
ID Name Rate1 Rate2
1 Xyz 1 2
1 Abc 1 2
2 Def NA NA
2 Lmn NA NA
3 Hij 3 5
3 Qrs 3 7
变成这样:
ID Name Rate1 Rate2
1 Xyz 1 2
2 Def NA NA
2 Lmn NA NA
3 Hij 3 5
3 Qrs 3 7
提前致谢!
编辑:我知道可以只取数据的一个子集 table,其中 Rates 为 NA,然后删除剩下的重复项,然后将 NA 行添加回去 - 但是,我宁愿避免这种策略。因为现实中有好几对率联我要连续做这个
EDIT2:为清楚起见,在示例中添加了更多行。
A base R
选项是在没有 'Name' 列的数据集子集上使用 duplicated
,即列索引 2 来创建逻辑向量,取反(!
- TRUE 变为 FALSE,反之亦然),因此 TRUE 将是非重复行。与此同时,在逻辑矩阵(is.na(df1[3:4])
- Rate 列)上使用 rowSums
创建另一个条件以获取全部为 NA 的行 - 此处我们将其与 2 进行比较 - 即数据集中 Rate 列的数量).这两个条件都由 |
加入以创建预期的逻辑索引
i1 <- !duplicated(df1[-2])| rowSums(is.na(df1[3:4])) == 2
df1[i1,]
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
或 Reduce
来自 base R
df1[Reduce(`&`, lapply(df1[3:4], is.na)) | !duplicated(df1[-2]), ]
将其包装在一个函数中
f1 <- function(dat, i, method ) {
nm1 <- grep("^Rate", colnames(dat), value = TRUE)
i1 <- !duplicated(dat[-i])
i2 <- switch(method,
"rowSums" = rowSums(is.na(dat[nm1])) == length(nm1),
"Reduce" = Reduce(`&`, lapply(dat[nm1], is.na))
)
i3 <- i1|i2
dat[i3,]
}
-测试
f1(df1, 2, "rowSums")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
f1(df1, 2, "Reduce")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
f1(df2, 2, "rowSums")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
#5 3 Hij 3 5
#6 3 Qrs 3 7
f1(df2, 2, "Reduce")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
#5 3 Hij 3 5
#6 3 Qrs 3 7
如果有多个 'Rate' 列(比如 100 或更多 - 第一个解决方案中唯一需要更改的是 2
应该更改为 'Rate' 列的数量)
或使用tidyverse
library(tidyvesrse)
df1 %>%
group_by(ID) %>%
filter_at(vars(Rate1, Rate2), any_vars(!duplicated(.)|is.na(.)))
# A tibble: 3 x 4
# Groups: ID [2]
# ID Name Rate1 Rate2
# <int> <chr> <int> <int>
#1 1 Xyz 1 2
#2 2 Def NA NA
#3 2 Lmn NA NA
df2 %>%
group_by(ID) %>%
filter_at(vars(Rate1, Rate2), any_vars(!duplicated(.)|is.na(.)))
# A tibble: 5 x 4
# Groups: ID [3]
# ID Name Rate1 Rate2
# <int> <chr> <int> <int>
#1 1 Xyz 1 2
#2 2 Def NA NA
#3 2 Lmn NA NA
#4 3 Hij 3 5
#5 3 Qrs 3 7
正如@Paul 在评论中提到的那样,2021 年 11 月 4 日更新后的 tidyverse
语法是
library(dplyr)
df2 %>%
group_by(ID) %>%
filter(if_any(cRate1, Rate2), ~ !duplicated(.)|is.na(.)))
数据
df1 <- structure(list(ID = c(1L, 1L, 2L, 2L), Name = c("Xyz", "Abc",
"Def", "Lmn"), Rate1 = c(1L, 1L, NA, NA), Rate2 = c(2L, 2L, NA,
NA)), class = "data.frame", row.names = c(NA, -4L))
df2 <- structure(list(ID = c(1L, 1L, 2L, 2L, 3L, 3L), Name = c("Xyz",
"Abc", "Def", "Lmn", "Hij", "Qrs"), Rate1 = c(1L, 1L, NA, NA,
3L, 3L), Rate2 = c(2L, 2L, NA, NA, 5L, 7L)), class = "data.frame",
row.names = c(NA, -6L))
我有一个包含 4 列的数据 table:ID、名称、Rate1、Rate2。
我想删除 ID、Rate1 和 Rate 2 相同的重复项,但是 如果它们都是 NA,我想保留两行。
基本上,我想有条件地删除重复项,但前提是条件!= NA。
比如我想要这样:
ID Name Rate1 Rate2
1 Xyz 1 2
1 Abc 1 2
2 Def NA NA
2 Lmn NA NA
3 Hij 3 5
3 Qrs 3 7
变成这样:
ID Name Rate1 Rate2
1 Xyz 1 2
2 Def NA NA
2 Lmn NA NA
3 Hij 3 5
3 Qrs 3 7
提前致谢!
编辑:我知道可以只取数据的一个子集 table,其中 Rates 为 NA,然后删除剩下的重复项,然后将 NA 行添加回去 - 但是,我宁愿避免这种策略。因为现实中有好几对率联我要连续做这个
EDIT2:为清楚起见,在示例中添加了更多行。
A base R
选项是在没有 'Name' 列的数据集子集上使用 duplicated
,即列索引 2 来创建逻辑向量,取反(!
- TRUE 变为 FALSE,反之亦然),因此 TRUE 将是非重复行。与此同时,在逻辑矩阵(is.na(df1[3:4])
- Rate 列)上使用 rowSums
创建另一个条件以获取全部为 NA 的行 - 此处我们将其与 2 进行比较 - 即数据集中 Rate 列的数量).这两个条件都由 |
加入以创建预期的逻辑索引
i1 <- !duplicated(df1[-2])| rowSums(is.na(df1[3:4])) == 2
df1[i1,]
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
或 Reduce
来自 base R
df1[Reduce(`&`, lapply(df1[3:4], is.na)) | !duplicated(df1[-2]), ]
将其包装在一个函数中
f1 <- function(dat, i, method ) {
nm1 <- grep("^Rate", colnames(dat), value = TRUE)
i1 <- !duplicated(dat[-i])
i2 <- switch(method,
"rowSums" = rowSums(is.na(dat[nm1])) == length(nm1),
"Reduce" = Reduce(`&`, lapply(dat[nm1], is.na))
)
i3 <- i1|i2
dat[i3,]
}
-测试
f1(df1, 2, "rowSums")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
f1(df1, 2, "Reduce")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
f1(df2, 2, "rowSums")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
#5 3 Hij 3 5
#6 3 Qrs 3 7
f1(df2, 2, "Reduce")
# ID Name Rate1 Rate2
#1 1 Xyz 1 2
#3 2 Def NA NA
#4 2 Lmn NA NA
#5 3 Hij 3 5
#6 3 Qrs 3 7
如果有多个 'Rate' 列(比如 100 或更多 - 第一个解决方案中唯一需要更改的是 2
应该更改为 'Rate' 列的数量)
或使用tidyverse
library(tidyvesrse)
df1 %>%
group_by(ID) %>%
filter_at(vars(Rate1, Rate2), any_vars(!duplicated(.)|is.na(.)))
# A tibble: 3 x 4
# Groups: ID [2]
# ID Name Rate1 Rate2
# <int> <chr> <int> <int>
#1 1 Xyz 1 2
#2 2 Def NA NA
#3 2 Lmn NA NA
df2 %>%
group_by(ID) %>%
filter_at(vars(Rate1, Rate2), any_vars(!duplicated(.)|is.na(.)))
# A tibble: 5 x 4
# Groups: ID [3]
# ID Name Rate1 Rate2
# <int> <chr> <int> <int>
#1 1 Xyz 1 2
#2 2 Def NA NA
#3 2 Lmn NA NA
#4 3 Hij 3 5
#5 3 Qrs 3 7
正如@Paul 在评论中提到的那样,2021 年 11 月 4 日更新后的 tidyverse
语法是
library(dplyr)
df2 %>%
group_by(ID) %>%
filter(if_any(cRate1, Rate2), ~ !duplicated(.)|is.na(.)))
数据
df1 <- structure(list(ID = c(1L, 1L, 2L, 2L), Name = c("Xyz", "Abc",
"Def", "Lmn"), Rate1 = c(1L, 1L, NA, NA), Rate2 = c(2L, 2L, NA,
NA)), class = "data.frame", row.names = c(NA, -4L))
df2 <- structure(list(ID = c(1L, 1L, 2L, 2L, 3L, 3L), Name = c("Xyz",
"Abc", "Def", "Lmn", "Hij", "Qrs"), Rate1 = c(1L, 1L, NA, NA,
3L, 3L), Rate2 = c(2L, 2L, NA, NA, 5L, 7L)), class = "data.frame",
row.names = c(NA, -6L))