Термин частота / обратная частота документа (TF / IDF): взвешивание

12

У меня есть набор данных, который представляет 1000 документов и все слова, которые появляются в нем. Таким образом, строки представляют документы, а столбцы представляют слова. Так, например, значение в ячейке обозначает время, когда слово встречается в документе(i,j)j . Теперь я должен найти «веса» слов, используя метод tf / idf, но на самом деле я не знаю, как это сделать. Может кто-нибудь помочь мне?i

азбука
источник
Tf-idf-статистика для извлечения ключевых слов - joyofdata.de/blog/tf-idf-statistic-keyword-extraction
Рафаэль

Ответы:

12

В Википедии есть хорошая статья на эту тему, дополненная формулами. Значения в вашей матрице - это термин частоты. Вам просто нужно найти idf: (log((total documents)/(number of docs with the term))и умножить 2 значения.

В R вы можете сделать это следующим образом:

set.seed(42)
d <- data.frame(w=sample(LETTERS, 50, replace=TRUE))
d <- model.matrix(~0+w, data=d)

tf <- d
idf <- log(nrow(d)/colSums(d))
tfidf <- d

for(word in names(idf)){
  tfidf[,word] <- tf[,word] * idf[word]
}

Вот наборы данных:

> colSums(d)
wA wC wD wF wG wH wJ wK wL wM wN wO wP wQ wR wS wT wV wX wY wZ 
 3  1  3  1  1  1  1  2  4  2  2  1  1  3  2  2  2  4  5  5  4 
> head(d)
  wA wC wD wF wG wH wJ wK wL wM wN wO wP wQ wR wS wT wV wX wY wZ
1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0
2  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0
3  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
4  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0
5  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0
6  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0
> head(round(tfidf, 2))
  wA wC wD wF wG   wH wJ wK wL wM   wN wO wP   wQ wR wS wT   wV  wX  wY wZ
1  0  0  0  0  0 0.00  0  0  0  0 0.00  0  0 0.00  0  0  0 0.00 2.3 0.0  0
2  0  0  0  0  0 0.00  0  0  0  0 0.00  0  0 0.00  0  0  0 0.00 0.0 2.3  0
3  0  0  0  0  0 3.91  0  0  0  0 0.00  0  0 0.00  0  0  0 0.00 0.0 0.0  0
4  0  0  0  0  0 0.00  0  0  0  0 0.00  0  0 0.00  0  0  0 2.53 0.0 0.0  0
5  0  0  0  0  0 0.00  0  0  0  0 0.00  0  0 2.81  0  0  0 0.00 0.0 0.0  0
6  0  0  0  0  0 0.00  0  0  0  0 3.22  0  0 0.00  0  0  0 0.00 0.0 0.0  0

Вы также можете посмотреть на idf каждого термина:

> log(nrow(d)/colSums(d))
      wA       wC       wD       wF       wG       wH       wJ       wK       wL       wM       wN       wO       wP       wQ       wR       wS       wT       wV       wX       wY       wZ 
2.813411 3.912023 2.813411 3.912023 3.912023 3.912023 3.912023 3.218876 2.525729 3.218876 3.218876 3.912023 3.912023 2.813411 3.218876 3.218876 3.218876 2.525729 2.302585 2.302585 2.525729 
Zach
источник
Спасибо за вашу помощь! Но возможно ли получить какое-то значение для каждого слова, которое представляет некоторый вес (вместо целой матрицы)? Теперь у нас есть целая матрица весов. Я делаю выбор некоторых функций и хочу использовать tf / idf в качестве метода фильтра ...
ABC
@ABC tf-idf по определению относится к полной матрице весов. Возможно, вас интересуют одни веса IDF, которые вы бы получили log((number of docs)/(number of docs containing the term)). Вы также можете просто отфильтровать редкие термины.
Зак
Очень ясно! Очень ценится
ABC
13

есть пакет tm (text mining) http://cran.r-project.org/web/packages/tm/index.html, который должен делать именно то, что вам нужно:

#read 1000 txt articles from directory data/txt
corpus  <-Corpus(DirSource("data/txt"), readerControl = list(blank.lines.skip=TRUE));
#some preprocessing
corpus <- tm_map(corpus, removeWords, stopwords("english"))
corpus <- tm_map(corpus, stripWhitespace)
corpus <- tm_map(corpus, stemDocument, language="english")
#creating term matrix with TF-IDF weighting
terms <-DocumentTermMatrix(corpus,control = list(weighting = function(x) weightTfIdf(x, normalize = FALSE)))

#or compute cosine distance among documents
dissimilarity(tdm, method = "cosine")

R - функциональный язык, поэтому чтение кода может быть сложным (например, х в терминах)

xhudik
источник
2

В вашем коде есть ошибка: colSums вычисляет количество вхождений в корпусе, а не количество текстов со словом.

Версия вычислений такая:

tfidf=function(mat){
  tf <- mat
  id=function(col){sum(!col==0)}
  idf <- log(nrow(mat)/apply(mat, 2, id))
  tfidf <- mat
  for(word in names(idf)){tfidf[,word] <- tf[,word] * idf[word]}
  return(tfidf)
  }
user46661
источник
1

Я опаздываю на эту вечеринку, но я играл с концепциями tc-idf (я хочу подчеркнуть слово «концепция», потому что я не следовал никаким книгам для реальных вычислений; так что они могут быть несколько непонятными, и определенно это легче сделать с помощью таких пакетов, как {tm: Text Mining Package}, как уже упоминалось), и я думаю, что то, что я получил, может быть связано с этим вопросом, или, в любом случае, это может быть хорошим местом для публикации.


SET-UP: У меня есть корпус из 5длинных пунктов , взятых из печатных средств массовой информации, text 1через 5такие , как The New York Times . Предположительно, это очень маленькое «тело», крошечная библиотека, так сказать, но записи в этой «цифровой» библиотеке не случайны: первая и пятая записи имеют отношение к футболу (или «футбол» для «социального клуба») (?) где-то здесь), а точнее о самой великой команде сегодня. Так, например, text 1начинается как ...

«За последние девять лет Месси вывел« Барселону »на национальные и международные титулы, побив индивидуальные рекорды способами, которые кажутся потусторонними ...»

Очень хорошо! С другой стороны, вы определенно хотите пропустить содержимое трех записей между ними. Вот пример ( text 2):

«В течение нескольких часов по всему Техасу г-н Рубио предположил, что г-н Трамп мочился в его штаны и использовал нелегальных иммигрантов, чтобы прослушивать его непрерывные сообщения в Твиттере ...»

Так что же делать, чтобы любой ценой избежать «серфинга» от text 1до text 2, продолжая радоваться литературе о всемогущем «Барселоне» text 5?


TC-IDF: Я выделил слова в каждом textв длинные векторы. Затем посчитали частоту каждого слова, создав пять векторов (по одному для каждого text), в которых textбыли подсчитаны только слова, встречающиеся в соответствующем - все остальные слова, принадлежащие другим texts, были оценены в ноль. В первом фрагменте text 1, например, его вектор будет иметь значение 1 для слова «Месси», а для «Трампа» - 0. Это была часть tc .

Часть idf также рассчитывалась отдельно для каждого text, и в результате было получено 5 «векторов» (я думаю, что я рассматривал их как фреймы данных), содержащих логарифмические преобразования количества документов (к сожалению, от нуля до пяти, учитывая нашу небольшую библиотеку ) содержащее данное слово как в:

log(No. documents1+No. docs containing a word)text01text

tc×idftext


СРАВНЕНИЯ: Теперь это был просто вопрос выполнения точечных произведений среди этих «векторов важности слова».

Как и ожидалось, скалярное произведение text 1с text 5было 13.42645, а text 1v text2было только 2.511799.

Неуклюжий код R (нечего имитировать) находится здесь .

Опять же, это очень элементарная симуляция, но я думаю, что это очень наглядно.

Антони Пареллада
источник