删除基于列的重复项,该行取决于不同列中的NA数量

时间:2018-10-28 20:27:37

标签: r dplyr

我想根据其他列中NA的数量删除列a具有重复值的行。与此类似,但是我无法获得计算在内的NA来解决那里的解决方案。

  

Deleting rows that are duplicated in one column based on the conditions of another column

这是我的玩具数据集:

df1 <- data.frame(a = c("x","y","y","z","x", "z"), b = c(1,2,NA,4,8,3), c = c(NA,2,2,NA,NA,4), d= c(1:4,NA,NA))

给出:

  a  b  c  d
1 x  1 NA  1
2 y  2  2  2
3 y NA  2  3
4 z  4 NA  4
5 x  8 NA NA
6 z  3  4 NA

我只想在a列中保留具有唯一值的行,而在cols b和c中仅保留NA数量最少的行(忽略d列中的NA)

这是我想出的代码:

df1 %>%
 mutate(NAs= apply(is.na(cbind(b,c)), 1, sum)) %>%     
 group_by(a) %>%
 top_n(n=1, -NAs)

我的问题是,如果有平局,top_n返回的行多。如果是平局,我只希望返回第一行。比起cbind,还有一种更好的方法来选择mutate中的列。我也不需要使用mutate创建的“ NAs”变量。我想要的输出是这样:

  a  b  c  d
  x  1 NA  1
  y  2  2  2
  z  3  4 NA

3 个答案:

答案 0 :(得分:3)

@markus建议这也可以作为答案。也许是对的,因为在dplyr的情况下,使代码简短可能会有所帮助,否则,您通常可能会得到非常冗长的脚本。

但是,我认为的主要部分是rowSums,因为它已经被突出显示了。

df1 %>% 
arrange(a, rowSums(is.na(.[, c("b", "c")]))) %>% 
distinct(a, .keep_all = TRUE)

  a b  c  d
1 x 1 NA  1
2 y 2  2  2
3 z 3  4 NA

P.S。如果您关心速度,那么实际上我会尝试使用尽可能少的dplyr动词,例如仅使用arrangedistinct的方法比使用groupslicetop_nfilter等的其他方法快3倍。

答案 1 :(得分:2)

这是一个选择

library(dplyr)
df1 %>%
  mutate(NAs = rowSums(is.na(.[, c("b", "c")]))) %>%
  group_by(a) %>%
  top_n(n = 1, -NAs) %>% 
  slice(1) %>% 
  select(-NAs)
# A tibble: 3 x 4
# Groups:   a [3]
#  a         b     c     d
#  <fct> <dbl> <dbl> <int>
#1 x         1    NA     1
#2 y         2     2     2
#3 z         3     4    NA

rowSumsapply(..., 1, sum)的更有效替代。


您也可以尝试data.table。下面的解决方案应该非常快(但可读性可能较低)。

library(data.table)
setDT(df1)
df1[df1[order(a, df1[, rowSums(is.na(.SD)), .SDcols = c("b", "c")]), .I[1], by = "a"]$V1]
#   a b  c  d
#1: x 1 NA  1
#2: y 2  2  2
#3: z 3  4 NA

答案 2 :(得分:1)

一个稍微不同的dplyr选项:

df1 %>%
  mutate(miss = rowSums(is.na(cbind(b,c)))) %>%
  group_by(a) %>%
  filter(miss == min(miss)) %>%
  slice(1) %>%
  select(-miss) %>%
  ungroup()

或者:

df1 %>%
  mutate(miss = rowSums(is.na(cbind(b,c)))) %>%
  group_by(a) %>%
  mutate(dupl = seq_along(a)) %>%
  filter(miss == min(miss)) %>%
  filter(dupl == min(dupl)) %>%
  select(-miss, -dupl) %>%
  ungroup()