【问题标题】:Shortest unique substrings of a vector向量的最短唯一子串
【发布时间】:2017-12-08 12:25:18
【问题描述】:

假设我有一个一定长度的字符向量,

vec <- c("man lives to work", "man works to live")

从头开始,我现在想在这个向量中找到最短的唯一子字符串(完整的单词)。 换句话说,我不是在寻找整体上最短的子字符串,但我希望 crop 在它变得唯一的单词之后的字符串,在这种情况下,在 worklives 之后,分别。

所以结果应该是,在这种情况下:

[1] "man lives" "man works"

字符串应该在 lives/works 之后被裁剪,因为这是它们变得唯一的最早点(在这种情况下)。 包括to 将是多余的,因为它们已经是唯一的。 仅包括 man 是不够的,因为 c("man", "man") 不是唯一的。

(我想用它来自动生成有效的 R 名称,其余的由base::make.names() 完成)。

我该怎么做?

我想,必须有一个已经这样做的包,但找不到它。

【问题讨论】:

  • 您能否再次澄清一下最短唯一子字符串的含义。在您的情况下,例如,就字符数而言,字符串“to”或“to work”将比“man living”短。因此,不太清楚你想要什么。
  • 感谢您的澄清。但是,您是否要切断仍然不是很清楚。您提到了有关“完整单词”的内容。 “to”也是一个完整的词。我猜您想在出现“to”、“and”等特定停用词时立即停止。或者你只是想在第二个学期结束后切断?想象像“投票权”或只有“投票权”这样的短语。这里期望的输出是什么。抱歉这么挑剔,但这些选择可能会产生重要影响。
  • 了解@ManuelBickel。我没有特别的停用词。我想在 lives/ words 之后停止,因为包括这些词,这两个字符串变得唯一。将更新问题。
  • 再次更新问题以解释截止@ManuelBickel。
  • 对于c("the right to vote", "right to vote"),所需的输出确实是c("the", "right")——尽管我可以看出这是多么粗略。如果有其他已建立/更好的方法以人类可读的方式缩写(可能很长)字符串,那也很好,但我猜这里超出了范围。

标签: r string substring


【解决方案1】:

作为一般策略,我会 a) 检查第一个单词是否唯一, b)如果不是,检查前两个单词是否唯一, c) 继续,直到找到每个字符串的唯一解。

您可以使用while 循环或使用递归来实现此功能。这是后者的示例(已更新以保留顺序):

library(stringi) ## makes string processing easier

vec <- c("man lives to work", "man works to live")

(word.mat <- stri_split_boundaries(vec,
                                   type = "word",
                                   skip_word_none = TRUE,
                                   simplify = TRUE))
##      [,1]  [,2]    [,3] [,4]  
## [1,] "man" "lives" "to" "work"
## [2,] "man" "works" "to" "live"

## function to extract unique words
unique_words <- function(x, # matrix of words
                         n = nrow(x), # number of original strings
                         nc=1 # number of columns (words) to use
                         ) {
    ## join the first nc words
    s <- stri_trim(apply(x[, 1:nc, drop = FALSE], 1, stri_join, collapse = " "))
    ## find non-duplicated word combinations, and store in column 1
    nodups <- !s %in% s[stri_duplicated(s)]
    x[nodups, 1] <- s[nodups]
    ## remove extra words from the matrix
    x[nodups, -1] <- ""
    ## if some strings are not unique, do it again, increasing nc by one
    if(any(x[, 2] != "")) {
        x <- unique_words(x = x, n = n, nc = nc + 1)
    ## otherwise, grab the unique sub-phrases from column 1    
    } else {
        x <- x[, 1]
    }
    ## return the result
    x
}    
## test it out
unique_words(word.mat)
## [1] "man lives" "man works"

## test it out with a more complicated example:
vec <- c("foo", "man lives to eat", "man eats to live",
         "woman lives to work", "woman works to live",
         "we like apples", "we like peaches",
         "they like plums", "they love peas", "bar")
unique_words(stri_split_boundaries(vec,
                                   type = "word",
                                   skip_word_none = TRUE,
                                   simplify = TRUE))
## [1] "foo"             "man lives"       "man eats"        "woman lives"    
## [5] "woman works"     "we like apples"  "we like peaches" "they like"      
## [9] "they love"       "bar"

【讨论】:

  • +1 用于递归使用函数的优雅解决方案(并使用更快的 stringi 包而不是 stringr)
  • 不幸的是,经过进一步检查,此代码在某些情况下会搞砸订单。例如。 vec &lt;- c("man lives to eat", "foo", "man eats to live") 产生 #"foo" "man lives" "man eats"
  • 感谢@maxheld 我更新了示例以保留顺序。
【解决方案2】:
df %>%  unnest_tokens(word ,words) %>%
  mutate(bigram = substr(word,1,2), 
         trigram = ifelse (nchar(word) >= 3,substr(word,1,3),NA) ,
         four_gram  = ifelse (nchar(word) >= 4, substr(word,1,4), NA), 
         five_gram  = ifelse (nchar(word) >= 5, substr(word,1,5), NA)) %>%
  group_by(bigram) %>%
  mutate(count_bigram = n()) %>%
  ungroup() %>%
  group_by(trigram) %>%
  mutate(count_trigram = n()) %>%
  ungroup() %>%
  group_by(four_gram) %>%
  mutate(count_four_gram = n()) %>%
  ungroup() %>%
  group_by(five_gram) %>%
  mutate(count_five_gram = n()) %>%
  ungroup()   %>% 
  summarise_each(funs(((function(x) {sum(x == 1)})(.))), 
                 count_bigram, count_trigram, 
                 count_four_gram, count_five_gram)



# # A tibble: 1 × 4
#    count_bigram count_trigram count_four_gram count_five_gram
#          <int>         <int>           <int>           <int>
#1            0             0               0               2

【讨论】:

  • 感谢@Mouad_S,但这只是给出了单词的频率和长度,对吗?也许我的问题表述不当,但这不是我想要的。
  • 我编辑了代码,基本上你遍历数字 2,3,4,5...等,这取决于你的数据,在这种情况下,寻找的字符串长度为 5
猜你喜欢
  • 2016-12-19
  • 2019-12-05
  • 2012-06-21
  • 2011-04-06
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
相关资源
最近更新 更多