样本数据,
df <- data.frame(ID=c(1,1,1,2,2,2,3,3,3), v1=c(NA,2,3,1,2,3,1,2,3), v2=c(4,5,6,4,5,6,4,NA,6))
看起来,
ID v1 v2
1 1 NA 4
2 1 2 5
3 1 3 6
4 2 1 4
5 2 2 5
6 2 3 6
7 3 1 4
8 3 2 NA
9 3 3 6
我想获得,
ID v1 v2
4 2 1 4
5 2 2 5
6 2 3 6
我试过了,
library(dplyr)
df2 <- df %>%
group_by(ID) %>%
filter(!anyNA(c(v1,v2)))
df2
这会产生预期的结果,但是:
您可以将
rowSums
用于 is.na(df[-1])
,将 ave
与 any
一起使用并按 df$ID
分组,并使用 FALSE
取行。你可以覆盖df
.
df <- df[!ave(rowSums(is.na(df[-1])) > 0L, df$ID, FUN=any), ]
df
# ID v1 v2
#4 2 1 4
#5 2 2 5
#6 2 3 6
基准
library(tidyverse)
library(data.table)
df <- data.frame(ID=c(1,1,1,2,2,2,3,3,3), v1=c(NA,2,3,1,2,3,1,2,3), v2=c(4,5,6,4,5,6,4,NA,6))
dt <- copy(df)
bench::mark(check = FALSE,
ave = df[!ave(rowSums(is.na(df[-1])) > 0L, df$ID, FUN=any), ],
"Chris Ruehlemann" = {df %>%
group_by(ID) %>%
filter(if_all(starts_with("v"), ~!anyNA(.)))},
"TarJae1" = {df %>%
group_by(ID) %>%
mutate(row=row_number()) %>%
pivot_longer(-c(ID, row)) %>%
filter(!any(is.na(value))) %>%
pivot_wider(names_from=name, values_from = value) %>%
select(-row)},
"TarJae2" = {df %>%
anti_join(df %>%
group_by(ID) %>%
filter(if_any(everything(), ~is.na(.))) , by = "ID")},
"TarJae3" = {df %>%
filter(!if_any(everything(), ~anyNA(.)), .by=ID) },
"ThomasIsCoding" = setDT(dt)[ID == dt[, !anyNA(.SD), ID][V1 == TRUE, ID]]
)
结果
expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
<bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
1 ave 114.16µs 120.22µs 8185. 105.02KB 44.8 3653 20
2 Chris Ruehlemann 3ms 3.06ms 319. 3.36MB 36.8 130 15
3 TarJae1 13.06ms 13.21ms 74.5 1.26MB 58.8 19 15
4 TarJae2 3.56ms 3.62ms 271. 413.95KB 38.0 107 15
5 TarJae3 3.39ms 3.46ms 284. 60.04KB 40.5 112 16
6 ThomasIsCoding 1.41ms 1.53ms 650. 2.83MB 15.1 258 6
在这种情况下
ave
比第二个快大约12倍并且TarJae3
分配最低的额外内存。
这是一个
tidyverse
解决方案:
df %>%
group_by(ID) %>%
filter(if_all(starts_with("v"), ~!anyNA(.)))
在这里,我们使用
starts_with
通过它们都以“v”开头的字母来处理有问题的列。有多个 dplyr
函数可以在不对名称进行硬编码的情况下对列进行寻址;查看 ends_with
、contains
和(我最喜欢的)matches
,它对正则表达式敏感
更新2
受@Chris Ruehlemann 使用
anyNA
的启发,我找到了这个最终版本:
library(dplyr)
df %>%
filter(!if_any(everything(), ~anyNA(.)), .by=ID)
ID v1 v2
1 2 1 4
2 2 2 5
3 2 3 6
Update1改进代码: 一个
dplyr
唯一的解决方案:
df %>%
anti_join(df %>%
group_by(ID) %>%
filter(if_any(everything(), ~is.na(.))) , by = "ID")
ID v1 v2
1 2 1 4
2 2 2 5
3 2 3 6
第一个答案: 这是
tidyverse
方法:
library(dplyr)
library(tidyr)
df %>%
group_by(ID) %>%
mutate(row=row_number()) %>%
pivot_longer(-c(ID, row)) %>%
filter(!any(is.na(value))) %>%
pivot_wider(names_from=name, values_from = value) %>%
select(-row)
ID v1 v2
<dbl> <dbl> <dbl>
1 2 1 4
2 2 2 5
3 2 3 6
这里有一些
data.table
选项:
> setDT(df)[ID == df[, !anyNA(.SD), ID][, ID[V1]]
ID v1 v2
1: 2 1 4
2: 2 2 5
3: 2 3 6
或
> rbindlist(Filter(Negate(anyNA), split(df, ~ID)))
ID v1 v2
1: 2 1 4
2: 2 2 5
3: 2 3 6
基本 R 选项
subset(df, ID %in% names(which(!sapply(split(df[-1], ~ ID), anyNA))))
-输出
ID v1 v2
4 2 1 4
5 2 2 5
6 2 3 6