tidytext, quanteda и tm возвращают разные оценки tf-idf

Я пытаюсь работать с взвешенным корпусом tf-idf (где я ожидаю, что tf будет пропорцией по документу, а не простым подсчетом). Я ожидаю, что все классические библиотеки интеллектуального анализа текста будут возвращать одни и те же значения, но я получаю разные значения. Есть ли ошибка в моем коде (например, мне нужно транспонировать объект?) или параметры счетчиков tf-idf по умолчанию различаются в разных пакетах?

library(tm)
library(tidyverse) 
library(quanteda)
df <- as.data.frame(cbind(doc = c("doc1", "doc2"), text = c("the quick brown fox jumps over the lazy dog", "The quick brown foxy ox jumps over the lazy god")), stringsAsFactors = F)

df.count1 <- df %>% unnest_tokens(word, text) %>% 
  count(doc, word) %>% 
  bind_tf_idf(word, doc, n) %>% 
  select(doc, word, tf_idf) %>% 
  spread(word, tf_idf, fill = 0) 

df.count2 <- df %>% unnest_tokens(word, text) %>% 
  count(doc, word) %>% 
  cast_dtm(document = doc,term = word, value = n, weighting = weightTfIdf) %>% 
  as.matrix() %>% as.data.frame()

df.count3 <- df %>% unnest_tokens(word, text) %>% 
  count(doc, word) %>% 
  cast_dfm(document = doc,term = word, value = n) %>% 
  dfm_tfidf() %>% as.data.frame()

   > df.count1
# A tibble: 2 x 12
  doc   brown    dog    fox   foxy    god jumps  lazy  over     ox quick   the
  <chr> <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl>
1 doc1      0 0.0770 0.0770 0      0          0     0     0 0          0     0
2 doc2      0 0      0      0.0693 0.0693     0     0     0 0.0693     0     0

> df.count2
     brown       dog       fox jumps lazy over quick the foxy god  ox
doc1     0 0.1111111 0.1111111     0    0    0     0   0  0.0 0.0 0.0
doc2     0 0.0000000 0.0000000     0    0    0     0   0  0.1 0.1 0.1

> df.count3
     brown     dog     fox jumps lazy over quick the    foxy     god      ox
doc1     0 0.30103 0.30103     0    0    0     0   0 0.00000 0.00000 0.00000
doc2     0 0.00000 0.00000     0    0    0     0   0 0.30103 0.30103 0.30103

person Radim    schedule 15.02.2018    source источник


Ответы (1)


Вы наткнулись на различия в вычислении частот терминов.

Стандартные определения:

TF: частота терминов: TF(t) = (количество раз, когда термин t появляется в документе) / (общее количество терминов в документе).

IDF: обратная частота документов: IDF (t) = log (общее количество документов / количество документов с термином t в нем)

Масса Tf-idf есть произведение этих величин TF * IDF

Выглядит просто, но это не так. Давайте посчитаем tf_idf для слова dog в doc1.

Первый TF для собаки: это 1 термин / 9 терминов в документе = 0,11111

1/9 = 0.1111111

Теперь ИДФ для собак: журнал (2 документа / 1 триместр). Теперь есть несколько возможностей, а именно: log (или натуральный log), log2 или log10!

log(2) = 0.6931472
log2(2) = 1
log10(2) = 0.30103

#tf_idf on log:
1/9 * log(2) = 0.07701635

#tf_idf on log2:
1/9 * log2(2)  = 0.11111

#tf_idf on log10:
1/9 * log10(2) = 0.03344778

Теперь становится интересно. Tidytext дает вам правильный вес на основе журнала. tm возвращает tf_idf на основе log2. Я ожидал от Quanteda значения 0,03344778, потому что их база равна log10.

Но глядя на Quanteda, он возвращает результат правильно, но использует счет по умолчанию вместо пропорционального счета. Чтобы все получилось как надо, попробуйте код следующим образом:

df.count3 <- df %>% unnest_tokens(word, text) %>% 
  count(doc, word) %>% 
  cast_dfm(document = doc,term = word, value = n)


dfm_tfidf(df.count3, scheme_tf = "prop", scheme_df = "inverse")
Document-feature matrix of: 2 documents, 11 features (22.7% sparse).
2 x 11 sparse Matrix of class "dfm"
      features
docs   brown        fox        god jumps lazy over quick the      dog     foxy       ox
  doc1     0 0.03344778 0.03344778     0    0    0     0   0 0        0        0       
  doc2     0 0          0              0    0    0     0   0 0.030103 0.030103 0.030103

Это выглядит лучше, и это основано на log10.

Если вы используете quanteda с корректировками параметров, вы можете получить результат tidytext или tm, изменив параметр base.

# same as tidytext the natural log
dfm_tfidf(df.count3, scheme_tf = "prop", scheme_df = "inverse", base = exp(1))

# same as tm
dfm_tfidf(df.count3, scheme_tf = "prop", scheme_df = "inverse", base = 2)
person phiver    schedule 15.02.2018
comment
Большое спасибо за такой обстоятельный ответ. Я надеялся, что это просто несоответствие такого рода. Я действительно потратил много часов, пытаясь понять это самостоятельно, но либо документация в пакетах была такой же скудной, как мои матрицы, либо слишком загадочной, чтобы я мог понять их метод по умолчанию для подсчета tf-idf. - person Radim; 16.02.2018
comment
Хороший ответ @phiver. В Quanteda мы используем основание 10 и считаем tf, потому что так это делается в IIR. Но существуют варианты изменить это по желанию пользователя. Примечание: все это можно сделать в одной (конвейерной) операции как corpus(df) %>% dfm() %>% dfm_tfidf(). - person Ken Benoit; 17.02.2018