Término frecuencia / frecuencia de documento inversa (TF / IDF): ponderación

12

Tengo un conjunto de datos que representa 1000 documentos y todas las palabras que aparecen en él. Entonces las filas representan los documentos y las columnas representan las palabras. Entonces, por ejemplo, el valor en la celda representa las veces que la palabra aparece en el documento . Ahora, tengo que encontrar 'pesos' de las palabras, usando el método tf / idf, pero en realidad no sé cómo hacer esto. ¿Puede alguien ayudarme?(yo,j)jyo

A B C
fuente
La estadística tf-idf para extracción de palabras clave - joyofdata.de/blog/tf-idf-statistic-keyword-extraction
Raffael

Respuestas:

12

Wikipedia tiene un buen artículo sobre el tema, completo con fórmulas. Los valores en su matriz son las frecuencias del término. Solo necesita encontrar el idf: (log((total documents)/(number of docs with the term))y multiplicar los 2 valores.

En R, puede hacerlo de la siguiente manera:

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

Aquí están los conjuntos de datos:

> 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

También puede ver el idf de cada término:

> 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
fuente
¡Gracias por tu ayuda! Pero, ¿es posible obtener algún valor para cada palabra que represente alguna ponderación (en lugar de una matriz completa)? Ahora tenemos toda una matriz de pesas. Estoy haciendo una selección de funciones y quiero usar tf / idf como método de filtro ...
ABC
@ABC tf-idf, por definición, se refiere a la matriz completa de pesos. Tal vez esté interesado solo en los pesos idf, que podría superar log((number of docs)/(number of docs containing the term)). También podría simplemente filtrar los términos poco frecuentes.
Zach
¡Muy claro! Muy apreciado.
ABC
13

hay paquete tm (minería de texto) http://cran.r-project.org/web/packages/tm/index.html que debe hacer exactamente lo que necesita:

#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 es un lenguaje funcional, por lo que leer código puede ser complicado (por ejemplo, x en términos)

xhudik
fuente
2

Su código tiene un error: colSums calcula el número de ocurrencias en el corpus, no el número de textos con la palabra.

Una versión informática tal sería:

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)
  }
usuario46661
fuente
1

Hay un nuevo paquete R que puede hacer esto: textir: Regresión inversa para análisis de texto

El comando relevante es tfidf, el ejemplo del manual:

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

Llegué tarde a esta fiesta, pero estaba jugando con los conceptos de tc-idf (quiero enfatizar la palabra 'concepto' porque no seguí ningún libro para los cálculos reales; por lo tanto, pueden estar algo fuera de lugar, y definitivamente se lleva a cabo más fácilmente con paquetes como {tm: Text Mining Package}, como se mencionó), y creo que lo que obtuve puede estar relacionado con esta pregunta o, en cualquier caso, este puede ser un buen lugar para publicarlo.


SET-UP: Tengo un corpus de 5largos párrafos tomados de los medios de comunicación impresos, text 1a través 5tales como The New York Times . Supuestamente, es un "cuerpo" muy pequeño, una pequeña biblioteca, por así decirlo, pero las entradas en esta biblioteca "digital" no son aleatorias: las entradas primera y quinta se refieren al fútbol (o 'fútbol' para 'club social' (?) por aquí), y más específicamente sobre el mejor equipo de hoy. Entonces, por ejemplo, text 1comienza como ...

"En los últimos nueve años, Messi ha llevado al FC Barcelona a títulos nacionales e internacionales y ha batido récords individuales de maneras que parecen de otro mundo ..."

¡Muy agradable! Por otro lado, definitivamente querrás omitir el contenido de las tres entradas intermedias. Aquí hay un ejemplo ( text 2):

"En el lapso de unas pocas horas en todo Texas, el Sr. Rubio sugirió que el Sr. Trump había orinado en sus pantalones y usado inmigrantes ilegales para aprovechar sus incesantes mensajes de Twitter ..."

Entonces, ¿qué hacer para evitar a toda costa "surfear" del text 1al text 2, sin dejar de regocijarnos en la literatura sobre el todopoderoso FC Barcelona en text 5?


TC-IDF: aislé las palabras en todos texten vectores largos. Luego contó la frecuencia de cada palabra, creando cinco vectores (uno para cada uno text) en los que solo textse contaron las palabras encontradas en las correspondientes ; todas las demás palabras, pertenecientes a otras texts, se valoraron en cero. En el primer fragmento de text 1, por ejemplo, su vector tendría un recuento de 1 para la palabra "Messi", mientras que "Trump" tendría 0. Esta era la parte tc .

La parte idf también se calculó por separado para cada uno texty dio como resultado 5 "vectores" (creo que los traté como marcos de datos), que contienen las transformaciones logarítmicas de los recuentos de documentos (lamentablemente, solo de cero a cinco, dada nuestra pequeña biblioteca ) que contiene una palabra dada como en:

Iniciar sesión(No. documentos1+No. documentos que contienen una palabra)text0 01text

tc×idftext


COMPARACIONES: Ahora era solo cuestión de realizar productos de punto entre estos "vectores de importancia de palabras".

Como era de esperar, el producto punto de text 1with text 5era 13.42645, mientras que text 1v. text2Era solo 2.511799.

El código R torpe (nada que imitar) está aquí .

Nuevamente, esta es una simulación muy rudimentaria, pero creo que es muy gráfica.

Antoni Parellada
fuente