Реализация схемы взвешивания TFIDF

#python #text #tf-idf

#python #текст #tf-idf

Вопрос:

Моя цель — сравнить текст txt с каждым элементом в корпусе ниже, используя схему взвешивания TFIDF.

корпус=[‘школьник читает’, ‘кто читает комикс?’, ‘маленький мальчик читает’]

txt=’Школьник Джеймс всегда занят чтением’

Вот моя реализация:

TFIDF = частота терминов -обратная частота документов = tf * log (n / df) n = количество документов в корпусе — в данном случае 3

 import collections
from collections import Counter
from math import log

txt2=Counter(txt.split())
corpus2=[Counter(x.split()) for x in corpus]
def tfidf(doc,_corpus):
    dic=collections.defaultdict(int)
    for x in _corpus:
       for y in x:
          dic[y]  =1
    for x in doc:
       if x not in dic:dic[x]=1.
    return {x : doc[x] * log(3.0/dic[x])for x in doc}

txt_tfidf=tfidf(txt2, corpus2)
corpus_tfidf=[tfidf(x, corpus2) for x in corpus2]
 

Результаты

 print txt_tfidf
    {'boy': 0.4054651081081644, 'school': 1.0986122886681098, 'busy': 1.0986122886681098, 'James': 1.0986122886681098,
     'is': 0.0, 'always': 1.0986122886681098, 'the': 0.4054651081081644, 'reading': 0.0}
for x in corpus_tfidf:
    print x
{'boy': 0.4054651081081644, 'the': 0.4054651081081644, 'reading': 0.0, 'school': 1.0986122886681098, 'is': 0.0}
{'a': 1.0986122886681098, 'is': 0.0, 'who': 1.0986122886681098, 'comic?': 1.0986122886681098, 'reading': 0.0}
{'boy': 0.4054651081081644, 'the': 0.4054651081081644, 'reading': 0.0, 'little': 1.0986122886681098, 'is': 0.0}
 

Я не совсем уверен, прав ли я, потому что редкие термины, такие как Джеймс и комикс, должны иметь более высокие веса TFIDF, чем обычный термин, такой как школа.

Любые предложения будут оценены.

Комментарии:

1. Хотя ‘school’, возможно, более распространено в английском языке, ‘school’ имеет то же распределение, что и ‘comic?’ в вашем наборе данных, поэтому, похоже, их оценки должны быть одинаковыми

2. @confuser, но school появляется дважды, а comic появляется один раз, или это просто о корпусе? И, пожалуйста, дайте мне знать, верна ли моя реализация, и любые предложения о том, как провести сравнение. Спасибо.

3. А, понятно. Я считаю , что все предложения (включая txt ) должны быть частью корпуса, чтобы TF-IDF действительно имел смысл. (Может быть, кто-то более осведомленный в этом может подтвердить это?)

Ответ №1:

Прежде всего, как сказал @confuser в комментариях, давайте поместим txt в корпус и избавимся от этого кода:

 for x in doc:
   if x not in dic:dic[x]=1.
 

После этого я хочу добавить a . в ваш код, потому что точка в кодировании — это как соль в кулинарии. 😉

     for y in x:
        dic[y]  = 1.
 

О, я также вижу некоторые магические числа в вашем коде. Извините, но они заставляют меня нервничать, поэтому у нас есть:

 return {x: doc[x] * log(len(_corpus) / dic[x]) for x in doc}
 

Со всеми этими небольшими изменениями мы можем видеть результат кода ниже:

 import collections
from collections import Counter
from math import log

corpus = ['the school boy is reading', 'who is reading a comic?', 'the little boy is reading',
          'James the school boy is always busy reading']

txt = corpus[-1]

txt2 = Counter(txt.split())
corpus2 = [Counter(x.split()) for x in corpus]


def tfidf(doc, _corpus):
    dic = collections.defaultdict(int)
    for x in _corpus:
        for y in x:
            dic[y]  = 1.
    return {x: doc[x] * log(len(_corpus) / dic[x]) for x in doc}


txt_tfidf = tfidf(txt2, corpus2)
corpus_tfidf = [tfidf(x, corpus2) for x in corpus2]

print txt_tfidf
 

Мне кажется нормальным 'boy' иметь гораздо меньше tf_idf, чем 'busy' . Вы согласны?

 {'boy': 0.28768207245178085, 'school': 0.6931471805599453, 'busy': 1.3862943611198906, 'James': 1.3862943611198906, 'is': 0.0, 'always': 1.3862943611198906, 'the': 0.28768207245178085, 'reading': 0.0}
 

Комментарии:

1. большое спасибо за ваш вклад, ваш ответ и объяснение просто идеальны. Я получил точно такой же ответ около часа назад.