R:字符串之间的加权逆文档频率(tfidf)相似度

时间:2019-05-29 16:32:30

标签: r similarity quanteda

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

使用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是两个字符串共有的单词的权重。
WiWj是string1和string2中单词的权重。

3 个答案:

答案 0 :(得分:1)

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

我将tidytext用于nlp,并使用widyr计算文档之间的余弦相似度。

请注意,我将您原来的ss向量转换为带有tidy列的ID数据帧。您可以将该列设置为任意值,但最后将使用它来显示相似性。

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

其中item1item2指的是我们之前创建的ID列。

这个答案有些奇怪的警告。例如,请注意,我在您的ee向量中添加了ss令牌:当有一个带有单个令牌的文档时,pairwise_similarity失败了。奇怪的行为,但希望可以帮助您入门。

答案 1 :(得分:0)

我在使用quantedaqdap软件包时遇到了问题,因此我建立了自己的代码来获取带有单个单词和频率计数的数据帧。该代码当然可以改进,但是我认为它演示了如何实现。

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

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

答案 2 :(得分:0)

您要使用 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