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 每行恰好一个 TRUE
或 FALSE
。在 str_detect
中,我们正在寻找 a
或 b
的精确匹配,方法是将它们包装在 ^$
中。我们可以在 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))
}
我有一个带有 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 每行恰好一个 TRUE
或 FALSE
。在 str_detect
中,我们正在寻找 a
或 b
的精确匹配,方法是将它们包装在 ^$
中。我们可以在 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))
}