R: взвешенное сходство частоты (tfidf) обратной строки документа между строками - PullRequest
0 голосов
/ 29 мая 2019

Я хочу быть в состоянии найти сходство между двумя строками, взвешивая каждый токен (слово) по частоте обращения к документу (эти частоты не взяты из этих строк).

Используя 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")

с разрешением в форме (случайные числа для примера):

"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.

Ответы [ 3 ]

1 голос
/ 29 мая 2019

Вот решение 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 произошел, когда был один документ с одним токеном.Странное поведение, но, надеюсь, это поможет вам.

0 голосов
/ 30 мая 2019

Требуется функция textstat_simil() из quanteda .Вы должны добавить документ, который должен быть нацелен в корпус, а затем использовать аргумент 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
0 голосов
/ 29 мая 2019

У меня были проблемы с пакетами 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

Я надеюсь, что это то, что вы хотели: -)

...