Estoy tratando de adaptar este código (fuente encontrada aquí) para iterar a través de un directorio de archivos, en lugar de tener la entrada codificada.

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import division, unicode_literals
import math
from textblob import TextBlob as tb

def tf(word, blob):
    return blob.words.count(word) / len(blob.words)

def n_containing(word, bloblist):
    return sum(1 for blob in bloblist if word in blob)

def idf(word, bloblist):
    return math.log(len(bloblist) / (1 + n_containing(word, bloblist)))

def tfidf(word, blob, bloblist):
    return tf(word, blob) * idf(word, bloblist)


document1 = tb("""Today, the weather is 30 degrees in Celcius. It is really hot""")

document2 = tb("""I can't believe the traffic headed to the beach. It is really a circus out there.'""")

document3 = tb("""There are so many tolls on this road. I recommend taking the interstate.""")

bloblist = [document1, document2, document3]
for i, blob in enumerate(bloblist):
    print("Document {}".format(i + 1))
    scores = {word: tfidf(word, blob, bloblist) for word in blob.words}
    sorted_words = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    for word, score in sorted_words:
        score_weight = score * 100 
        print("\t{}, {}".format(word, round(score_weight, 5)))

Me gustaría utilizar un archivo txt de entrada en un directorio, en lugar de cada document codificado.

Por ejemplo, imagine que tenía un directorio foo que contiene tres archivos file1, file2, file3.

El archivo 1 contiene el contenido que document1 contiene, es decir

Archivo1:

Today, the weather is 30 degrees in Celcius. It is really hot

El archivo 2 contiene el contenido que document2 contiene, es decir

I can't believe the traffic headed to the beach. It is really a circus out there.

El archivo 3 contiene el contenido que document3 contiene, es decir

There are so many tolls on this road. I recommend taking the interstate.

Sin embargo, tengo que usar glob para lograr el resultado deseado, y se me ocurrió la siguiente adaptación de código, que identifica correctamente los archivos, pero no los procesa individualmente, como lo hace el código original:

file_names = glob.glob("/path/to/foo/*")
files =  map(open,file_names)
documents = [file.read() for file in files]
[file.close() for file in files]


bloblist = [documents]
for i, blob in enumerate(bloblist):
    print("Document {}".format(i + 1))
    scores = {word: tfidf(word, blob, bloblist) for word in blob.words}
    sorted_words = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    for word, score in sorted_words:
        score_weight = score * 100 
        print("\t{}, {}".format(word, round(score_weight, 5)))

¿Cómo puedo mantener los puntajes para cada archivo individual usando glob?

El resultado deseado después de usar los archivos en un directorio como entrada sería el mismo que el código original [resultados truncados a los 3 primeros por espacio]:

Document 1
    Celcius, 3.37888
    30, 3.37888
    hot, 3.37888
Document 2
    there, 2.38509
    out, 2.38509
    headed, 2.38509
Document 3
    on, 3.11896
    this, 3.11896
    many, 3.11896

Una pregunta similar aquí no resolvió completamente el problema. Me preguntaba cómo puedo llamar a los archivos para calcular el idf pero mantenerlos por separado para calcular el tf-idf completo.

0
loropro 10 dic. 2015 a las 15:02

3 respuestas

La mejor respuesta

En su primer ejemplo de código, completa bloblist con resultados de tb(), y en su segundo ejemplo, con entradas para tb() (solo cadenas).

Intente reemplazar bloblist = [documents] con bloblist = map(tb, documents).

También puede ordenar la lista de nombres de archivo como esta file_names = sorted(glob.glob("/path/to/foo/*")) para hacer coincidir las salidas de ambas versiones.

0
alexisrozhkov 11 dic. 2015 a las 08:35

No estoy seguro de qué es exactamente lo que quieres lograr. Podría tener una matriz y agregar los resultados a esa matriz:

scores = []
bloblist = [documents]
for i, blob in enumerate(bloblist):
  ... do your evaluation ..
  scores.append(score_weight)

print scores
0
Randrian 10 dic. 2015 a las 12:44

@AnnaBonazzi proporciona un fragmento de código aquí, https://gist.github.com/sloria/6407257,

import os, glob
folder = "/path/to/folder/"
os.chdir(folder)
files = glob.glob("*.txt") # Makes a list of all files in folder
bloblist = []
for file1 in files:
    with open (file1, 'r') as f:
        data = f.read() # Reads document content into a string
        document = tb(data.decode("utf-8")) # Makes TextBlob object
        bloblist.append(document)

Lo modifiqué para mi uso (Python 3):

import os, glob
bloblist = []

def make_corpus(input_dir):
    """ Based on code snippet from https://gist.github.com/sloria/6407257 """

    global doc                              ## used outside this method
    input_folder = "input"
    os.chdir(input_folder)
    files = glob.glob("*.*")                ## or "*.txt", etc.
    for doc in files:
        # print('doc:', doc)                ## prints filename (doc)
        with open (doc, 'r') as f:
            data = f.read()                 ## read document content into a string
            document = tb(data)             ## make TextBlob object
            bloblist.append(document)
    # print('bloblist:\n', bloblist)        ## copious output ...
    print('len(bloblist):', len(bloblist))


make_corpus('input')                        ## input directory 'input'

Actualización 1:

Personalmente, no tengo más que dificultades para usar el módulo global de Python, ya que a menudo (i) tengo nombres de archivo sin extensiones (por ejemplo, 01) y (ii) quiero recurrir a directorios anidados.

A primera vista, el enfoque "global" parece ser una solución simple. Sin embargo, cuando intento iterar sobre los archivos devueltos por glob, a menudo encuentro errores (por ejemplo)

IsADirectoryError: [Errno 21] Is a directory: ...

Cuando el bucle encuentra un nombre de directorio (no un archivo) que fue devuelto por glob.

En mi opinión, con un pequeño esfuerzo adicional, el siguiente enfoque es mucho más robusto:

import os
bloblist = []

def make_corpus(input_dir):
    for root, subdirs, files in os.walk(input_dir):
        for filename in files:
            f = os.path.join(root, filename)
            print('file:', f)
            with open(os.path.join(root, filename)) as f:
                for line in f:
                    # print(line, end='')
                    bloblist.append(line)
    # print('bloblist:\n', bloblist)
    print('len(bloblist):', len(bloblist), '\n')

make_corpus('input')       ## 'input' = input dir

Actualización 2:

Un último enfoque (comando de shell de Linux find, adaptado para su uso en Python 3):

import sh     ## pip install sh

def make_corpus(input_dir):
    '''find (here) matches filenames, excludes directory names'''

    corpus = []
    file_list = []
    #FILES = sh.find(input_dir, '-type', 'f', '-iname', '*.txt')    ## find all .txt files
    FILES = sh.find(input_dir, '-type', 'f', '-iname', '*')         ## find any file
    print('FILES:', FILES)                                          ## caveat: files in FILES are '\n'-terminated ...
    for filename in FILES:
        #print(filename, end='')
        # file_list.append(filename)                                ## when printed, each filename ends with '\n'
        filename = filename.rstrip('\n')                            ## ... this addresses that issue
        file_list.append(filename)
        with open(filename) as f:
            #print('file:', filename)
            # ----------------------------------------
            # for general use:
            #for line in f:
                #print(line)
                #corpus.append(line)
            # ----------------------------------------
            # for this particular example (Question, above):
            data = f.read()
            document = tb(data)
            corpus.append(document)
    print('file_list:', file_list)
    print('corpus length (lines):', len(corpus))

    with open('output/corpus', 'w') as f:                           ## write to file
        for line in corpus:
            f.write(line)
1
Victoria Stuart 8 dic. 2017 a las 23:04