我们可以为filtering 使用Vectorized 选项。下面,有三个执行此操作的四个选项
1) 使用str_c 和reduce。我们select名称为starts_with'S'的列,使用reduce(来自purrr)连接为单个字符串(使用str_c),然后使用str_detect检查是否只有一个或多个 0 和 . ([0.]+) 从字符串的开头 (^) 和结尾 ($) 或 (|) 仅一个或多个 1 和 .。否定 (!) 逻辑表达式并保留其余行
library(dplyr)
library(stringr)
library(purrr)
file %>%
filter(!str_detect(reduce(select(cur_data(), starts_with('S')),
str_c, sep=""), '^([0.]+|[1.]+)$'))
# ID Pos S1 S2 S3 S4
#1 A 22 . 1 0 .
#2 B 21 1 0 . 1
2) 另一个选项是 if_all 到 filter 仅具有来自“S”列的元素 . 和 0 的行,将 setdiff 与原始数据一起使用获取剩余的行,应用第二个if_all 以生成逻辑表达式n,其中行只有. 和1,取反(!)以返回其余行
file %>%
filter(if_all(starts_with('S'), ~ . %in% c('.', 0))) %>%
setdiff(file, .) %>%
filter(!if_all(starts_with('S'), ~ . %in% c('.', 1)))
# ID Pos S1 S2 S3 S4
#1 A 22 . 1 0 .
#2 B 21 1 0 . 1
3) 我们可以通过在第一个 if_all 之后创建一个临时逻辑列 ('i1') 来避免 setdiff 步骤,并在 filter 和下一个 if_all 中使用它
file %>%
mutate(i1 = if_all(starts_with('S'), ~ . %in% c('.', 0))) %>%
filter(!(i1 | if_all(starts_with('S'), ~ . %in% c('.', 1)))) %>%
select(-i1)
# ID Pos S1 S2 S3 S4
#1 A 22 . 1 0 .
#2 B 21 1 0 . 1
4) 或者我们可以使用rowSums 来创建可以与& 连接在一起的复合逻辑表达式
file %>%
filter(rowSums(select(cur_data(), starts_with('S')) == '1') > 0 &
rowSums(select(cur_data(), starts_with('S')) == '0') > 0)
# ID Pos S1 S2 S3 S4
#1 A 22 . 1 0 .
#2 B 21 1 0 . 1
数据
file <- structure(list(ID = c("A", "B", "C", "D"), Pos = c(22L, 21L,
50L, 11L), S1 = c(".", "1", "0", "."), S2 = c("1", "0", ".",
"1"), S3 = c("0", ".", ".", "."), S4 = c(".", "1", ".", ".")),
class = "data.frame", row.names = c(NA,
-4L))