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.
3 respuestas
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.
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
@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)
Preguntas relacionadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.