除非列中存在NA值,否则要删除重复的行

问题描述 投票:1回答:1

我有一个包含4列的数据表:ID,Name,Rate1,Rate2。

我想删除IS,Rate 1和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

提前致谢!

编辑:我知道可以只取一个数据表的子集,其中Rate是NA,然后删除剩下的重复项,然后再添加NA行 - 但是,我宁愿避免这种策略。这是因为实际上有很多对联率我想连续做到这一点。

EDIT2:为了清晰起见,在示例中添加了更多行。

r duplicates data.table condition distinct
1个回答
4
投票

base R选项是在数据集的子集上使用duplicated而不使用'Name'列,即列索引2来创建逻辑向量,否定(! - TRUE变为FALSE,反之亦然),因此TRUE将是非重复行。与此一起创建rowSumson的另一个条件是逻辑矩阵(is.na(df1[3:4]) - Rate列)以获得全部为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

或者来自Reducebase 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,]
     }    

-testing

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

data

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))
© www.soinside.com 2019 - 2024. All rights reserved.