R中的每个字符都匹配字符串

时间:2018-10-07 08:39:05

标签: r dataframe string-matching

我有一个包含字符串和ID行的数据框。我称之为历史。

history
ID   string
1.1  a b b b c c s d s ....
1.2  a b b b b c s s d ....
2.1  a c c s s d b d b ....
2.2  a c s c s d b d b ....
3.1  a z z x d b d d f ....
3.2  a z x z d d f b d ....
...

每行中的字符串都很长。属于相同编号(如1.1和1.2)的ID具有相似的字符串,但有细微差别。虽然1.1和2.2之间会有较大的差异。原始数据中大约有70行。

test
string
a c c c s s d b d b....

我的目标是,如果我有另一个包含“历史记录”中不存在的字符串的数据框,我想在“历史记录”中找到最匹配的ID。我知道有很多文本匹配方法可以做到这一点。我的问题来自以下事实:我无法将“测试”中的整个字符串与“历史记录”进行匹配。

整个过程的重点是看我是否可以找出“ test”中的字符串属于哪个ID,而不必匹配整个字符串。我想到的一个想法是在测试中进行更多匹配时过滤掉历史记录。

我的预期输出: 在这里,我假设匹配以“ test”中字符串的第一个字符开始,而不是“ history”中字符串的第一个字符开始。而且我们一个一个地走。这两个假设都不是固定的。同样,“历史”和“测试”中字符串的长度也可以不同。

“ test”中的第一个字符“ a”与“ history”中的所有人匹配。因此,在这种情况下不会进行过滤。

test
string
a 

结果:

history
    ID   string
    1.1  a b b b c c s d s ....
    1.2  a b b b b c s s d ....
    2.1  a c c s s d b d b ....
    2.2  a c s c s d b d b ....
    3.1  a z z x d b d d f ....
    3.2  a z x z d d f b d ....
    ...

第二个字符是“ c”。这里要确保我们没有匹配“历史”中某个地方的随机“ c”,我认为建立规则会有所帮助。如果是“ a”然后是“ c”,则会出现类似匹配的情况。

test
    string
    a c

结果:

history
    ID   string
    2.1  a c c s s d b d b ....
    2.2  a c s c s d b d b ....

这已将匹配范围缩小到历史记录ID 2.1和2.2。坦白说,我们甚至可以像我之前所说的那样在这儿停下来。因此,总之,一旦将历史记录过滤为一个ID,就应该输出与“测试”字符串最匹配的ID。

2 个答案:

答案 0 :(得分:1)

这里有两个tidyverse解决方案,它们将返回ID值,该值具有与测试字符串最大匹配数和最大匹配数:

df = data.frame(ID = c(1.1,1.2,2.1,2.2,3.1,3.2),
                string = c("a b b b c c s d s",
                           "a b b b b c s s d",
                           "a c c s s d b d b",
                           "a c s c s d b d b",
                           "a z z x d b d d f",
                           "a z x z d d f b d"), 
                stringsAsFactors = F)

library(tidyverse)

# string to test
test = "a c c c s s"

选项1 (考虑任意位置的匹配项)

df %>%
  separate_rows(string) %>%
  group_by(ID) %>%
  mutate(test = unlist(strsplit(test, split = " "))[row_number()]) %>%
  na.omit() %>%
  summarise(matches = sum(string == test)) %>%
  filter(matches == max(matches))

# # A tibble: 2 x 2
#      ID matches
#   <dbl>   <int>
# 1   2.1       4
# 2   2.2       4

选项2 (考虑连续的比赛)

df %>%
  separate_rows(string) %>%
  group_by(ID) %>%
  mutate(test = unlist(strsplit(test, split = " "))[row_number()]) %>%
  na.omit() %>%
  summarise(matches = sum(cumprod(string == test))) %>%
  filter(matches == max(matches))

# # A tibble: 1 x 2
#        ID matches
#     <dbl>   <dbl>
#   1   2.1       3

答案 1 :(得分:0)

以AntoniosK上面给出的出色示例为基础:

您可以为每列应用一些加权因子。因此,如果第1列非常重要,则将其乘以10.000,将第二列仅乘以1.000。然后将这些值逐行求和,找到最高的总和以获得最合适的字符串。

(又是a b c d e f比a b c d e f匹配得更好)

library(tidyverse)
df = data.frame(ID = c(1.1,1.2,2.1,2.2,3.1,3.2),
                string = c("a b b b c c s d s",
                           "a b b b b c s s d",
                           "a c c s s d b d b",
                           "a c s c s d b d b",
                           "a z z x d b d d f",
                           "a z x z d d f b d"), 
                stringsAsFactors = F)
# string to test
test <-  "a c c c s s"

weights <- c(1000,100,10,10,10,10,10,10,10)

df_answer <- df %>%
  separate_rows(string) %>%
  group_by(ID) %>%
  mutate(test = unlist(strsplit(test, split = " "))[row_number()]) %>% 
  mutate(scores = (string == test) * weights) %>% 
  summarise(scores = sum(scores, na.rm = TRUE)) %>%
  filter(scores == max(scores))

# A tibble: 2 x 2
#     ID scores
#  <dbl>  <dbl>
#1   2.1   1120
#2   2.2   1120