删除 R 中的行

Removing rows in R

我有一个大约有 24 列和许多行的文件,如下所示:

| ID| Pos| S1 | S2| S3| S4|  ...S24
|---|----|----|---|---|---|
| A | 22 | .  | 1 | 0 | . |
| B | 21 | 1  | 0 | . |1  |
| C | 50 | 0  | . | . |.  |
| D | 11 | .  | 1 | . |.  |

我想删除样本(从 S1 - S24)只有“.”的所有行。和“0”以及样本只有“。”的所有行和“1”,就像上面的虚拟 table 一样,C 行和 D 行将被删除,A 行和 B 行将被保留。

我尝试在 R 中使用 rowsums,但没有成功;

NEW_FILE <- file[rowSums(file == "." & file == "1") < 24, ]

我感谢任何关于 R 或其他方面的建议。

谢谢!

这是一个 dplyr 解决方案:

library(dplyr)
file %>% 
  rowwise() %>%
  filter(sum(!(c_across(-c(ID,Pos)) %in% c(".","0"))) > 0 &
         sum(!(c_across(-c(ID,Pos)) %in% c(".","1"))) > 0)
#  ID      Pos S1    S2    S3    S4   
#  <chr> <int> <chr> <chr> <chr> <chr>
#1 A        22 .     1     0     .    
#2 B        21 1     0     .     1    

我们可以使用 rowwise dplyr 动词处理每一行。然后 c_across 仅处理 S 列。我们可以检查是否所有都在 c(".","0") 中,然后对 c(".","1") 重复该过程。我们过滤(即保留)两个条件都为 TRUE.

的行

如果还有其他非“S”列,您可以改用 c_across(starts_with("S"))

数据:

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))

这是一个使用正则表达式的 base R 解决方案:

file[-which(grepl("^[0.]+$|^[1.]+$", apply(file[,-1], 1, paste, collapse = ""))),]
  ID S1 S2 S3 S4
1  A  .  1  0  .
2  B  1  0  .  1

在这里,我们首先使用 applypaste 将行折叠成字符串,然后我们对这些行 file 进行子集 which do not 匹配它们 包含 .1.0 从开始 ^ 结束 $

如果您更喜欢 dplyr 解决方案:

library(dplyr)
file %>%
  rowwise() %>%
  mutate(string = paste(c_across(starts_with('S')),collapse = "")) %>%
  filter(!grepl("^[0.]+$|^[1.]+$", string)) %>%
  select(-string)

数据:

file <- data.frame(
  ID = LETTERS[1:4],
  S1 = c(".", "1", "0", "."),
  S2 = c("1", "0", ".", "1"),
  S3 = c("0", ".", ".", "."),
  S4 = c(".", "1", ".", ".")
)

假设每一行都可以找到.,这也可以是另一种解决方案。但是,如果不是这种情况,我需要进行一些修改:

library(dplyr)
library(stringr)
library(purrr)

file %>%
  mutate(Con = pmap_lgl(file %>% 
                          select(starts_with("S")), ~ all(any(str_detect(c(...), "1")),
                          any(str_detect(c(...), "0"))))) %>%
  filter(Con) %>%
  select(-Con)

  ID Pos S1 S2 S3 S4
1  A  22  .  1  0  .
2  B  21  1  0  .  1

我们可以使用 Vectorized 选项进行 filtering。下面,有三个四个选项可以做到这一点

1) 使用 str_creduce。我们 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_allfilter 仅具有 .0 元素的行 'S' 列,将 setdiff 与原始数据一起使用以获得剩余的行,应用第二个 if_all 生成逻辑表达式,其中行只有 .1 , 否定 (!) 到 return 其余行

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))

希望这个使用 subset + apply + %in% 的基本 R 选项可以有所帮助(感谢 提供数据)

> subset(file, apply(file, 1, function(x) all(c("0", "1", ".") %in% x) | sum(x == ".") + 2 == length(x)))
  ID Pos S1 S2 S3 S4
1  A  22  .  1  0  .
2  B  21  1  0  .  1