str_split() 在 filter() 中

str_split() within filter()

我有一个带有 ID (V1) 的数据框和一些包含以逗号分隔值的字符串的列,即“a,b”(V2)。我想根据任何值向量与此字符串中任何值之间的匹配来过滤数据框。我还想保留任何其他列而不明确命名它们 (V3)。

df <- data.frame(V1 = 1:4, V2 = c("a,b", letters[1:3]), V3 = letters[20:23])
head(df)
  V1  V2 V3
1  1 a,b  t
2  2   a  u
3  3   b  v
4  4   c  w

输出应包含任何行,其中 V2 的至少一部分与值向量中的至少一个值匹配。即,如果标准是 c("a","b"),行 1:3 应该完整保留;如果标准是 c("a","d"),行 1:2 应该保留,等等。我还想在 V2 中保留完整的字符串。

我知道我可以使用 separate_rows,对分隔的行进行过滤,然后再次汇总(或聚合)数据框:

df %>% 
  separate_rows(V2, sep = ",") %>%
  group_by_at(setdiff(names(df), "V2")) %>%
  filter(V2 %in% c("a", "b")) %>%
  summarise(paste(V2, collapse=","))

# A tibble: 3 x 3
# Groups:   V1 [3]
     V1 V3    `paste(V2, collapse = ",")`
  <int> <chr> <chr>                      
1     1 t     a,b                        
2     2 u     a                          
3     3 v     b   

但是,这样做的缺点是无法保留原始字符串。当并非字符串的所有部分都包含在条件中时,这会有所不同:

df %>% 
  separate_rows(V2, sep = ",") %>%
  group_by_at(setdiff(names(df), "V2")) %>%
  filter(V2 %in% c("a", "d")) %>%
  summarise(paste(V2, collapse=","))

# A tibble: 2 x 3
# Groups:   V1 [2]
     V1 V3    `paste(V2, collapse = ",")`
  <int> <chr> <chr>                      
1     1 t     a                          
2     2 u     a  

我想知道是否有直接在 filter() 中使用 str_split() 或类似函数的解决方案。因为这将为字符串的每个部分 return 一个 TRUE/FALSE 值,所以我尝试使用 any() 将它们组合起来。但是,这不起作用,因为它应用于整个数据帧中由 str_split() 编辑的任何值 return:

# This does not include comma-separated values
df %>%
  filter(
    str_split(V2, ",") %in% c("a", "b")
  )
  V1 V2 V3
1  2  a  u
2  3  b  v

# This does not filter out non-matching values
df %>%
  filter(
    any(str_split(V2, ",") %in% c("a", "b"))
  )
  V1  V2 V3
1  1 a,b  t
2  2   a  u
3  3   b  v
4  4   c  w

也许这种方法可以帮助您处理原始数据(请注意,我更新了您在评论中提到的 df)。

想法是首先在 V2 上使用 str_split 将它变成一个列表,然后使用 map_lgl 循环遍历列表,其中包含 str_detect any 到 return 每行恰好一个 TRUEFALSE。在 str_detect 中,我们正在寻找 ab 的精确匹配,方法是将它们包装在 ^$ 中。我们可以在 filter 内完成所有这些,而无需添加新变量。

library(tidyverse)

df <- data.frame(V1 = 1:4, V2 = c("a,b", "a", "b", "aba"), V3 = letters[20:23])

df %>% 
  filter(map_lgl(str_split(V2, ","),
                 ~ any(str_detect(.x, "^a$|^b$"))))
#>   V1  V2 V3
#> 1  1 a,b  t
#> 2  2   a  u
#> 3  3   b  v

reprex package (v0.3.0)

于 2020-10-03 创建

我认为您可以考虑创建一个模式来过滤,而不是拆分字符串。

library(dplyr)
library(stringr)

get_rows <- function(data, vec) {
  data %>%
    filter(str_detect(V2, str_c(vec, collapse = '|')))
}

get_rows(df, c('a', 'b'))
#  V1  V2 V3
#1  1 a,b  t
#2  2   a  u
#3  3   b  v

get_rows(df, c('a', 'd'))
#  V1  V2 V3
#1  1 a,b  t
#2  2   a  u

你也可以用 base R 写这个:

get_rows <- function(data, vec) {
  subset(data, grepl(paste0(vec, collapse = '|'), V2))
}