【问题标题】:R: weighted inverse document frequency (tfidf) similarity between stringsR:字符串之间的加权逆文档频率(tfidf)相似度
【发布时间】:2019-05-29 16:32:29
【问题描述】:

我希望能够找到两个字符串之间的相似性,用它的逆文档频率加权每个标记(单词)(这些频率不是从那些字符串中获取的)。

使用quanteda,我可以创建一个具有反转频率权重的dfm_tfidf,但不知道之后如何进行。

样本数据:

ss = c(
        "ibm madrid limited research", 
        "madrid limited research", 
        "limited research",
        "research"
    )
counts = list(ibm = 1, madrid = 2, limited = 3, research = 4)
cor = corpus(long_list_of_strings)  ## the documents where we take words from
df = dfm(cor, tolower = T, verbose = T)
dfi = dfm_tfidf(df)

我们的目标是找到一个函数similarity,它将使:

res = similarity(dfi, "ibm limited", similarity_scheme = "simple matching")

res 格式为(示例中为随机数):

"ibm madrid limited research"  0.445
"madrid limited research" 0.2
"limited research" 0.76
"research" 0.45

理想情况下,将像这样的函数应用于这些频率:

sim = sum(Wc) / sqrt(sum(Wi)*sum(Wj)) 

在哪里: Wc 是两个字符串共有的词的权重。
Wi 和Wj 是string1 和string2 中词的权重。

【问题讨论】:

  • 查看包 'tidytext' (tidytextmining.com)。不确定,但也许你找到了解决方案

标签: r similarity quanteda


【解决方案1】:

这是解决您问题的tidy 解决方案。

我使用tidytext 处理 nlp 数据,使用widyr 计算文档之间的余弦相似度。

注意,我将您原来的 ss 向量转换为带有 ID 列的 tidy 数据框。您可以随意创建该列,但我们最后会使用它来显示相似性。

library(tidytext)
library(widyr)

# turn your original vector into a tibble with an ID column
ss <- c(
  "ibm madrid limited research", 
  "madrid limited research", 
  "limited research",
  "research",
  "ee"
) %>% as.tibble() %>% 
  rowid_to_column("ID")


# create df of words & counts (tf-idf needs this)
ss_words <- ss %>% 
  unnest_tokens(words, value) %>% 
  count(ID, words, sort = TRUE)

# create tf-idf embeddings for your data
ss_tfidf <- ss_words %>% 
  bind_tf_idf(ID, words, n)

# return list of document similarity
ss_tfidf %>% 
  pairwise_similarity(ID, words, tf_idf, sort = TRUE)

上面的输出将是:


## A tibble: 12 x 3
#   item1 item2 similarity
#   <int> <int>      <dbl>
# 1     3     2      0.640
# 2     2     3      0.640
# 3     4     3      0.6  
# 4     3     4      0.6  
# 5     2     1      0.545
# 6     1     2      0.545
# 7     4     2      0.384
# 8     2     4      0.384
# 9     3     1      0.349
#10     1     3      0.349
#11     4     1      0.210
#12     1     4      0.210

其中item1 和item2 指的是我们之前创建的ID 列。

这个答案有一些奇怪的警告。例如,请注意我将ee 标记添加到您的ss 向量中:当一个文档只有一个标记时,pairwise_similarity 失败。奇怪的行为,但希望这能让你开始。

【讨论】:

    【解决方案2】:

    您需要 quanteda 的 textstat_simil() 函数。您应该将要定位的文档添加到语料库中,然后使用 selection 参数来关注它。 “简单匹配”是作为相似性方法之一实现的,但您应该注意,这会查找是否存在术语,因此 tf-idf 加权不会影响这一点。

    library("quanteda")
    ## Package version: 1.4.3
    ## 
    ss <- c(
      "ibm limited",
      "ibm madrid limited research",
      "madrid limited research",
      "limited research",
      "research"
    )
    ssdfm <- dfm(ss)
    ssdfm
    ## Document-feature matrix of: 5 documents, 4 features (40.0% sparse).
    ## 5 x 4 sparse Matrix of class "dfm"
    ##        features
    ## docs    ibm limited madrid research
    ##   text1   1       1      0        0
    ##   text2   1       1      1        1
    ##   text3   0       1      1        1
    ##   text4   0       1      0        1
    ##   text5   0       0      0        1
    dfm_tfidf(ssdfm)
    ## Document-feature matrix of: 5 documents, 4 features (40.0% sparse).
    ## 5 x 4 sparse Matrix of class "dfm"
    ##        features
    ## docs        ibm    limited  madrid   research
    ##   text1 0.39794 0.09691001 0       0         
    ##   text2 0.39794 0.09691001 0.39794 0.09691001
    ##   text3 0       0.09691001 0.39794 0.09691001
    ##   text4 0       0.09691001 0       0.09691001
    ##   text5 0       0          0       0.09691001
    

    在这里,你可以看到结果不受 tf-idf 加权的影响:

    dfm_tfidf(ssdfm) %>%
      textstat_simil(method = "simple matching", selection = "text1") %>%
      as.matrix()
    ##       text1
    ## text1  1.00
    ## text2  0.50
    ## text3  0.25
    ## text4  0.50
    ## text5  0.25
    
    ssdfm %>%
      textstat_simil(method = "simple matching", selection = "text1") %>%
      as.matrix()
    ##       text1
    ## text1  1.00
    ## text2  0.50
    ## text3  0.25
    ## text4  0.50
    ## text5  0.25
    

    【讨论】:

      【解决方案3】:

      我在使用 quanteda 和 qdap 包时遇到了问题,因此我构建了自己的代码来获取包含单个单词和频率计数的数据框。代码当然可以改进,但我认为它说明了如何去做。

      library(RecordLinkage)
      library(stringr)
      library(dplyr)
      
      searchstring = c(
        "ibm madrid limited research", 
        "madrid limited research", 
        "limited research",
        "research"
      )
      
      cleanInput <- function(x) {
        x <- tolower(x)
        x <- removePunctuation(x)
        x <- stripWhitespace(x)
        x <- gsub("-", "", x)
        x <- gsub(" ?(f|ht)tp(s?)://(.*)[.][a-z]+", "", x)
        x <- gsub("[[:digit:]]+", "", x)
      }
      
      searchstring <- cleanInput(searchstring)
      splitted <- str_split(searchstring, " ", simplify = TRUE)
      df <- as.data.frame(as.vector(splitted))
      df <- df[df$`as.vector(splitted)` != "", , drop = FALSE]
      colnames(df)[1] <- "string"
      result <- df %>%
        group_by(string) %>%
        summarise(n = n())
      result$string <- as.character(result$string)
      

      我首先清理字符串,然后用它构建一个 data.frame。

      在我收到data.frame 后,其中存在一个名为jarowinkler 的函数来自RecordLinkage 包,用于测量两个字符串之间的相似性。它是矢量化且快速的:-)

      > jarowinkler(result$string, "ibm limited")
      [1] 0.0000000 0.8303030 0.8311688 0.3383838 0.0000000
      

      我希望这是你想要的:-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-17
        • 1970-01-01
        • 2015-11-23
        • 1970-01-01
        • 2017-04-19
        • 1970-01-01
        • 2021-02-23
        相关资源
        最近更新 更多