我希望能够找到两个字符串之间的相似性,并用其反向文档频率(不是从那些字符串中获取这些频率)来加权每个标记(单词)。
使用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中单词的权重。
答案 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
其中item1
和item2
指的是我们之前创建的ID
列。
这个答案有些奇怪的警告。例如,请注意,我在您的ee
向量中添加了ss
令牌:当有一个带有单个令牌的文档时,pairwise_similarity
失败了。奇怪的行为,但希望可以帮助您入门。
答案 1 :(得分:0)
我在使用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
我希望这是您想要的:-)
答案 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