Fréquence de terme / fréquence de document inverse (TF / IDF): pondération

12

J'ai un ensemble de données qui représente 1000 documents et tous les mots qui y apparaissent. Les lignes représentent donc les documents et les colonnes les mots. Ainsi, par exemple, la valeur dans la cellule représente la fois où le mot apparaît dans le document . Maintenant, je dois trouver des «poids» des mots, en utilisant la méthode tf / idf, mais je ne sais pas comment faire. Quelqu'un peut-il m'aider?(i,j)ji

abc
la source
La statistique tf-idf pour l'extraction de mots clés - joyofdata.de/blog/tf-idf-statistic-keyword-extraction
Raffael

Réponses:

12

Wikipedia a un bon article sur le sujet, complet avec des formules. Les valeurs dans votre matrice sont les termes fréquences. Il vous suffit de trouver l'idf: (log((total documents)/(number of docs with the term))et de multiplier les 2 valeurs.

Dans R, vous pouvez le faire comme suit:

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

Voici les jeux de données:

> 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

Vous pouvez également consulter l'idf de chaque terme:

> 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
la source
Merci de votre aide! Mais est-il possible d'obtenir une valeur pour chaque mot qui représente une certaine pondération (au lieu d'une matrice entière)? Nous avons maintenant toute une matrice de poids. Je fais une sélection de fonctionnalités et je veux utiliser tf / idf comme méthode de filtrage ...
ABC
@ABC tf-idf, par définition, fait référence à la matrice complète des poids. Peut-être que vous êtes intéressé par les poids idf seuls, que vous obtiendriez log((number of docs)/(number of docs containing the term)). Vous pouvez également simplement filtrer les termes peu fréquents.
Zach
Très clair! Vraiment apprécié.
ABC
13

il y a le paquet tm (text mining) http://cran.r-project.org/web/packages/tm/index.html qui devrait faire exactement ce dont vous avez besoin:

#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 est un langage fonctionnel, donc la lecture du code peut être délicate (par exemple x en termes)

xhudik
la source
2

Votre code contient une erreur: colSums calcule le nombre d'occurrences dans le corpus, pas le nombre de textes avec le mot.

Une version informatique telle serait:

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
la source
1

Il existe un nouveau package R qui peut le faire: textir: régression inverse pour l'analyse de texte

La commande appropriée est tfidf, l'exemple du manuel:

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

Je suis en retard à cette fête, mais je jouais avec les concepts de tc-idf (je veux souligner le mot `` concept '' parce que je n'ai suivi aucun livre pour les calculs réels; ils peuvent donc être quelque peu décalés, et certainement plus facile à réaliser avec des packages tels que {tm: Text Mining Package}, comme mentionné), et je pense que ce que j'ai obtenu peut être lié à cette question, ou, en tout cas, cela peut être un bon endroit pour le poster.


SET-UP: J'ai un corpus de 5longs paragraphes tirés de la presse écrite, à text 1travers 5tels que le New York Times . Apparemment, c'est un très petit "corps", une petite bibliothèque, pour ainsi dire, mais les entrées de cette bibliothèque "numérique" ne sont pas aléatoires: les première et cinquième entrées traitent du football (ou "soccer" pour "club social" (?) ici), et plus précisément sur la plus grande équipe d'aujourd'hui. Ainsi, par exemple, text 1commence comme ...

"Au cours des neuf dernières années, Messi a mené le FC Barcelone à des titres nationaux et internationaux tout en battant des records individuels d'une manière qui semble étrange ..."

Très agréable! D'un autre côté, vous voudrez certainement ignorer le contenu des trois entrées intermédiaires. Voici un exemple ( text 2):

"En l'espace de quelques heures à travers le Texas, M. Rubio a suggéré que M. Trump avait uriné dans son pantalon et utilisé des immigrants illégaux pour exploiter ses messages Twitter incessants ..."

Alors, que faire pour éviter à tout prix de "surfer" du text 1au text 2, tout en continuant à se réjouir de la littérature sur le tout puissant FC Barcelone en text 5?


TC-IDF: J'ai isolé les mots dans chacun texten longs vecteurs. Puis compté la fréquence de chaque mot, créant cinq vecteurs (un pour chacun text) dans lesquels seuls les mots rencontrés dans le correspondant textont été comptés - tous les autres mots, appartenant à d'autres texts, ont été évalués à zéro. Dans le premier extrait de text 1, par exemple, son vecteur aurait un compte de 1 pour le mot "Messi", tandis que "Trump" aurait 0. C'était la partie tc .

La partie idf a également été calculée séparément pour chacun text, et a abouti à 5 "vecteurs" (je pense que je les ai traités comme des trames de données), contenant les transformations logarithmiques des décomptes de documents (malheureusement, juste de zéro à cinq, étant donné notre petite bibliothèque ) contenant un mot donné comme dans:

Journal(Nombre de documents1+Nombre de documents contenant un mot) . Le nombre de documents est de 5. Voici la partie qui peut répondre au PO: pour chaque calcul idf, le textsous-considéré a été exclu du décompte . Mais si un mot apparaît dans tous les documents, son idf est toujours égal à grâce au du dénominateur - par exemple, le mot «le» a une importance de 0, car il est présent dans tous les al.01text

La multiplication en entrée de pour chaque était l'importance de chaque mot pour chacun des éléments de la bibliothèque - des mots répandus localement et rares à l'échelle mondiale .tc×idftext


COMPARAISONS: Maintenant, il s'agissait simplement de réaliser des produits scalaires parmi ces "vecteurs d'importance des mots".

On pouvait s'y attendre, le produit scalaire de text 1with text 5était 13.42645, alors que text 1v. text2Était seulement 2.511799.

Le code R maladroit (rien à imiter) est ici .

Encore une fois, c'est une simulation très rudimentaire, mais je pense que c'est très graphique.

Antoni Parellada
la source