查找包含字符序列的字符串,无论r中的顺序如何

时间:2018-06-28 23:09:04

标签: r string dataframe pattern-matching permutation

我有一个数据框(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

4 个答案:

答案 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

方法1:加入所有内容

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

方法2:EACHI加入

一种内存效率更高的方法可能是使用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)

这是使用sapplytableidentical的一种方法。

# 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的输出有关的两个关键点:

  1. 匹配向量myVal必须为整数。
  2. 您要按字母顺序排列匹配向量,您可以提前进行,也可以在ordernames[之后进行。< / 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