Frequência de termo / frequência inversa de documento (TF / IDF): ponderação

12

Eu tenho um conjunto de dados que representa 1000 documentos e todas as palavras que aparecem nele. Portanto, as linhas representam os documentos e as colunas representam as palavras. Assim, por exemplo, o valor na célula (i,j) representa o horário em que a palavra ocorre no documento . Agora, tenho que encontrar 'pesos' das palavras, usando o método tf / idf, mas na verdade não sei como fazer isso. Alguém por favor pode me ajudar?ji

abc
fonte
O tf-idf-estatística para Keyword extração - joyofdata.de/blog/tf-idf-statistic-keyword-extraction
Raffael

Respostas:

12

A Wikipedia tem um bom artigo sobre o assunto, completo com fórmulas. Os valores em sua matriz são os termos frequências. Você só precisa encontrar o idf: (log((total documents)/(number of docs with the term))e múltiplos os 2 valores.

Em R, você pode fazer o seguinte:

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]
}

Aqui estão os conjuntos de dados:

> 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

Você também pode olhar para o idf de cada termo:

> 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
fonte
Obrigado pela ajuda! Mas é possível obter algum valor para cada palavra que represente alguma ponderação (em vez de uma matriz inteira)? Agora temos uma matriz inteira de pesos. Eu estou fazendo alguma seleção de recurso e quer usar tf / idf como um método do filtro ...
ABC
@ABC tf-idf, por definição, refere-se à matriz completa de pesos. Talvez você esteja interessado apenas nos pesos idf, pelos quais você conseguiria log((number of docs)/(number of docs containing the term)). Você também pode filtrar os termos não frequentes.
Zach
Muito claro! Realmente apreciado.
ABC
13

há o pacote tm (mineração de texto) http://cran.r-project.org/web/packages/tm/index.html que deve fazer exatamente o que você precisa:

#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 é uma linguagem funcional, portanto, a leitura do código pode ser complicada (por exemplo, x em termos)

xhudik
fonte
2

Seu código tem um erro: colSums calcula o número de ocorrências no corpus, não o número de textos com a palavra.

Uma versão que computa seria:

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
fonte
1

Há um novo pacote R que pode fazer isso: textir: Regressão inversa para análise de texto

O comando relevante é tfidfo exemplo do manual:

data(we8there)
## 20 high-variance tf-idf terms
colnames(we8thereCounts)[
order(-sdev(tfidf(we8thereCounts)))[1:20]]
vonjd
fonte
1

Estou atrasado para esta festa, mas estava brincando com os conceitos de tc-idf (quero enfatizar a palavra 'conceito' porque não segui nenhum livro para os cálculos reais; portanto, eles podem estar um pouco errados e definitivamente mais facilmente executada com pacotes {tm: Text Mining Package}como o mencionado) e acho que o que obtive pode estar relacionado a essa pergunta ou, de qualquer forma, esse pode ser um bom lugar para publicá-la.


SET-UP: Eu tenho um corpus de 5longos parágrafos retirados de mídia impressa, text 1através de 5tais como The New York Times . Alegadamente, é um "corpo" muito pequeno, uma pequena biblioteca, por assim dizer, mas as entradas nessa biblioteca "digital" não são aleatórias: a primeira e a quinta entradas tratam de futebol (ou 'futebol' para 'clube social'). (?) por aqui) e, mais especificamente, sobre o melhor time de hoje. Então, por exemplo, text 1começa como ...

"Nos últimos nove anos, Messi levou o FC Barcelona a títulos nacionais e internacionais, quebrando recordes individuais de maneiras que parecem de outro mundo ..."

Muito agradável! Por outro lado, você definitivamente gostaria de pular o conteúdo nas três entradas intermediárias. Aqui está um exemplo ( text 2):

"No período de algumas horas no Texas, Rubio sugeriu que Trump urinou em suas calças e usou imigrantes ilegais para extrair suas mensagens incessantes no Twitter ..."

Então, o que fazer para evitar a todo custo "surfar" do text 1para text 2, enquanto continua a se alegrar na literatura sobre o todo-poderoso Barcelona FC text 5?


TC-IDF: Eu isolei as palavras em todos os textvetores longos. Em seguida, contou a frequência de cada palavra, criando cinco vetores (um para cada text) nos quais apenas as palavras encontradas na correspondente textforam contadas - todas as outras palavras, pertencentes a outras texts, foram avaliadas em zero. No primeiro trecho de text 1, por exemplo, seu vetor teria uma contagem de 1 para a palavra "Messi", enquanto "Trump" teria 0. Essa era a parte tc .

A parte idf também foi calculada separadamente para cada uma texte resultou em 5 "vetores" (acho que os tratei como quadros de dados), contendo as transformações logarítmicas das contagens de documentos (infelizmente, apenas de zero a cinco, dada a nossa pequena biblioteca ) contendo uma determinada palavra como em:

registro(No. documentos1+Documentos com uma palavra). O número de documentos é 5. Aí vem a parte que pode responder ao PO: para cada cálculo de IDF, a textconsideração em questão foi excluída da contagem . Mas se uma palavra aparecesse em todos os documentos, seu idf ainda estava0 0 graças ao 1no denominador - por exemplo, a palavra "the" teve importância 0, porque estava presente em todos os texts.

A multiplicação de entrada de tc×idfpara cada textera a importância de cada palavra para cada um dos itens da biblioteca - palavras prevalentes localmente e globalmente raras .


COMPARAÇÃO: Agora, era apenas uma questão de executar produtos pontuais entre esses "vetores de importância das palavras".

Previsivelmente, o produto escalar de text 1with text 5era 13.42645, enquanto o text 1v. text2Era apenas 2.511799.

O código R desajeitado (nada a imitar) está aqui .

Novamente, esta é uma simulação muito rudimentar, mas acho que é muito gráfica.

Antoni Parellada
fonte