我有一个数据框(df)
V1 V2
1 "BCC" Yes
2 "ABB" Yes
我想查找所有包含特定字符序列的字符串,而不管其顺序如何。 例如,如果我有字符串“ CBC”或“ CCB”,我想得到
V1 V2
1 "BCC" Yes
我尝试过使用grep,但是它只能找到匹配的模式
>df[grep("CBC", df$V1),]
1 V1 V2
<0 rows> (or 0-length row.names)
>df[grep("BCC", df$V1),]
V1 V2
1 "BCC" Yes
答案 0 :(得分:4)
我们可以通过拆分列来创建逻辑索引
i1 <- sapply(strsplit(df$V1, ""), function(x) all(c("B", "C") %in% x))
df[i1, , drop = FALSE]
# V1 V2
#1 BCC Yes
如果我们有两个数据集,一个是查找表('df2'),则将该列拆分为字符,paste
sort
个元素,然后使用%in%
创建逻辑vector
用于过滤行
v1n <- sapply(strsplit(df1$v1, ""), function(x) paste(sort(x), collapse=""))
v1l <- sapply(strsplit(df2$v1, ""), function(x) paste(sort(x), collapse=""))
df1[v1n %in% v1l, , drop = FALSE]
df1 <- data.frame(v1 = c("BCC", "CAB" , "ABB", "CBC", "CCB", "BAB", "CDB"),
stringsAsFactors = FALSE)
df2 <- data.frame(v1 = c("CBC", "ABB"), stringsAsFactors = FALSE)
答案 1 :(得分:3)
在注释中,您提到了一个查找表。如果是这种情况,一种方法可能是将两个集合连接在一起,然后使用regex by Wiktor Stribiżew来指示哪些有效
在我加入数据集时,我将使用data.table
library(data.table)
## dummy data, and a lookup table
dt <- data.frame(V1 = c("BCC", "ABB"))
dt_lookup <- data.frame(V1 = c("CBC","BAB", "CCB"))
## convert to data.table
setDT(dt); setDT(dt_lookup)
## add some indexes to keep track of rows from each dt
dt[, idx := .I]
dt_lookup[, l_idx := .I]
## create a column to join on
dt[, key := 1L]
dt_lookup[, key := 1L]
## join EVERYTHING
dt <- dt[
dt_lookup
, on = "key"
, allow.cartesian = T
]
#regex
dt[
, valid := grepl(paste0("^[",i.V1,"]+$"), V1)
, by = 1:nrow(dt)
]
# V1 idx key i.V1 l_idx valid
# 1: BCC 1 1 CBC 1 TRUE
# 2: ABB 2 1 CBC 1 FALSE
# 3: BCC 1 1 BAB 2 FALSE
# 4: ABB 2 1 BAB 2 TRUE
# 5: BCC 1 1 CCB 3 TRUE
# 6: ABB 2 1 CCB 3 FALSE
一种内存效率更高的方法可能是使用Jaap的this technique,因为它避免了“ join all”步骤,而是一次“按每个i”(行)将其联接。
dt_lookup[
dt,
{
valid = grepl(paste0("^[",i.V1,"]+$"), V1)
.(
V1 = V1[valid]
, idx = i.idx
, match = i.V1
, l_idx = l_idx[valid]
)
}
, on = "key"
, by = .EACHI
]
# key V1 idx match l_idx
# 1: 1 CBC 1 BCC 1
# 2: 1 CCB 1 BCC 3
# 3: 1 BAB 2 ABB 2
答案 2 :(得分:2)
这是使用sapply
,table
和identical
的一种方法。
# construct a named vector of integers with names in
# alphabetical order: your match
myVal <- c("B"=1L, "C"=2L)
# run through character variable, perform check
sapply(strsplit(dat$V1, ""), function(x) identical(c(table(x)), myVal))
[1] TRUE FALSE
与identical
的使用和table
的输出有关的两个关键点:
order
,names
和[
之后进行。< / li>
此外,我不是将table
的输出包装在c
中以剥离不想要的属性,同时保留名称。
答案 3 :(得分:2)
您可以使用stringi::stri_count_regex
来查看字符串中出现的次数是否与table
的{{1}}相匹配。最后一个strsplit(str_to_find, '')
表示它正在检查是否有任何匹配项,因此,如果要检查它是否与reduce("|")
中的所有字符串都匹配,请将|
更改为&
。
to.find
您也可以使用set.seed(0)
df <- data.frame(a = replicate(20, paste0(sample(LETTERS[1:3], 3, T), collapse = ''))
, stringsAsFactors = F)
to.find <- c("CBB", "CCB")
to.find <- strsplit(to.find, '')
library(tidyverse)
library(stringi)
df$b <-
sapply(df$a, function(x){
lapply(to.find, function(y){
imap(table(y), ~ .x == stri_count_regex(x, .y)) %>%
reduce(`&`)}) %>%
reduce(`|`)})
df
# a b
# 1 CAB FALSE
# 2 BCA FALSE
# 3 CCB TRUE
# 4 BAA FALSE
# 5 ACB FALSE
# 6 CBC TRUE
# 7 CBC TRUE
# 8 CAB FALSE
# 9 AAB FALSE
# 10 ABC FALSE
# 11 BBB FALSE
# 12 BAC FALSE
# 13 CCA FALSE
# 14 CBC TRUE
# 15 BCB TRUE
# 16 BCA FALSE
# 17 BCC TRUE
# 18 BCB TRUE
# 19 AAA FALSE
# 20 ABB FALSE
# 19 AAA FALSE
# 20 ABB FALSE
完成所有操作,但这很难阅读
map