2016-08-09 10 views
0

Im ziemlich sicher, dass im Ertrag nicht richtig mit:Python - Yield unsachgemäß Verwendung

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

import logging 
from gensim import corpora, models, similarities 
from collections import defaultdict 
from pprint import pprint # pretty-printer 
from six import iteritems 
import openpyxl 
import string 
from operator import itemgetter 

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) 

#Creating a stoplist from file 
with open('stop-word-list.txt') as f: 
    stoplist = [x.strip('\n') for x in f.readlines()] 

corpusFileName = 'content_sample_en.xlsx' 
corpusSheetName = 'content_sample_en' 

class MyCorpus(object): 
    def __iter__(self): 
     wb = openpyxl.load_workbook(corpusFileName) 
     sheet = wb.get_sheet_by_name(corpusSheetName) 
     for i in range(1, (sheet.max_row+1)/2): 
      title = str(sheet.cell(row = i, column = 4).value.encode('utf-8')) 
      summary = str(sheet.cell(row = i, column = 5).value.encode('utf-8')) 
      content = str(sheet.cell(row = i, column = 10).value.encode('utf-8')) 
      yield reBuildDoc("{} {} {}".format(title, summary, content)) 


def removeUnwantedPunctuations(doc): 
    "change all (/, \, <, >) into ' ' " 
    newDoc = "" 
    for l in doc: 
     if l == "<" or l == ">" or l == "/" or l == "\\": 
      newDoc += " " 
     else: 
      newDoc += l 
    return newDoc 

def reBuildDoc(doc): 
    """ 
    :param doc: 
    :return: document after being dissected to our needs. 
    """ 
    doc = removeUnwantedPunctuations(doc).lower().translate(None, string.punctuation) 
    newDoc = [word for word in doc.split() if word not in stoplist] 
    return newDoc 

corpus = MyCorpus() 

tfidf = models.TfidfModel(corpus, normalize=True) 

Im folgende Beispiel sehen Sie, ich versuchen, einen Korpus aus einer xlsx-Datei zu erstellen. Ich lese aus der xlsx-Datei 3 Zeilen, die Titel Zusammenfassung und Inhalt sind und sie in eine große Zeichenfolge anhängen. Meine reBuildDoc() und removeUnwantedPunctuations() Funktionen passen dann den Text an meine Bedürfnisse an und am Ende gibt eine große Liste von Wörtern zurück. (ZB: [hello, piano, computer, etc... ]) am Ende nachgeben ich das Ergebnis, aber ich erhalte die folgenden Fehler:

Traceback (most recent call last): 
    File "C:/Users/Eran/PycharmProjects/tfidf/docproc.py", line 101, in <module> 
    tfidf = models.TfidfModel(corpus, normalize=True) 
    File "C:\Anaconda2\lib\site-packages\gensim-0.13.1-py2.7-win-amd64.egg\gensim\models\tfidfmodel.py", line 96, in __init__ 
    self.initialize(corpus) 
    File "C:\Anaconda2\lib\site-packages\gensim-0.13.1-py2.7-win-amd64.egg\gensim\models\tfidfmodel.py", line 119, in initialize 
    for termid, _ in bow: 
ValueError: too many values to unpack 

Ich weiß, dass der Fehler aus der Ausbeute Linie ist, weil ich eine andere Ertragslinie hatte, die funktionierte. Es sah wie folgt aus:

yield [word for word in dictionary.doc2bow("{} {} {}".format(title, summary, content).lower().translate(None, string.punctuation).split()) if word not in stoplist] 

Es war abit chaotisch und schwer functionallity zu setzen, so habe ich geändert, wie Sie es im ersten Beispiel sehen kann.

+0

Randbemerkung: 'removeUnwantedPunctuations' ist unglaublich ineffizient implementiert, vor allem, da Sie sowieso einen 'translate'-Aufruf für das Ergebnis ausführen. Führen Sie einfach Folgendes auf der obersten Ebene Ihres Codes aus: "Unerwünscht_zu_Raum", deletepunc = string.maketrans (r '\/<>', ''), string.punctuation.translate (Keine, r '\/<>') ', Ändern Sie dann 'doc = removeUnwantedPunctuations (doc) .lower(). translate (None, string.punctuation)' in 'doc = doc.translate (unerwünschte_zu_Raum, deletepunc) .lower()'. In einfachen Tests reduziert dies die Laufzeit um einen Faktor von ~ 10-15x (höheres Ende für längere/weniger interpunktierte Strings). – ShadowRanger

+0

Und Sie könnten sogar noch mehr sparen, indem Sie 'ungewollten_Raum' mit 'string.maketrans (r '\/<>' + string.ascii_uppercase, '' + string.ascii_lowercase)' (wenn es nicht sichtbar ist, sollten vier Leerzeichen in sein die Zeichenfolge, die das zweite Argument führt), die es Ihnen ermöglicht, den Aufruf von "lower" wegzulassen (wenn die Eingabe nicht ASCII war, sollten Sie "nieder", aber für ASCII 'str', die Arbeit in die integrieren 'translate' Aufruf ist äquivalent und kostenlos), erhalten eine 20x Einsparungen bei der Laufzeit. Wenn Ihre Eingaben klein sind, spielt natürlich die Ersparnis keine Rolle, aber bei großen Daten kann die Eingabe von Parsing sehr teuer sein. – ShadowRanger

Antwort

1

das Problem ist nicht die yield per se, ist das, was ergibt wird, wobei der Fehler von for termid, _ in bow diese Zeile gesagt, dass Sie erwarten, dass bow eine Liste von Tupeln oder anderen Gegenständen enthalten mit genau 2 Elemente wie (1,2),[1,2],"12",... aber wie es Ende zu geben das Ergebnis MyCorpus, die offensichtlich eine Zeichenfolge mit mehr als 2 Elemente ist, damit die Fehler zu beheben, dies entweder for termid in bow oder in MyCorpus tun yield reBuildDoc("{} {} {}".format(title, summary, content)), None so ergeben Sie ein Tupel von 2 Objekt

diese Überprüfung zu veranschaulichen Dieses Beispiel

>>> def fun(obj): 
     for _ in range(2): 
      yield obj 


>>> for a,b in fun("xyz"): 
     print(a,b) 


Traceback (most recent call last): 
    File "<pyshell#11>", line 1, in <module> 
    for a,b in fun("xyz"): 
ValueError: too many values to unpack (expected 2) 
>>> for a,b in fun("xy"): 
     print(a,b) 


x y 
x y 
>>> for a,b in fun(("xy",None)): 
     print(a,b) 


xy None 
xy None 
>>> 
+0

Sie haben Recht, das Problem war mit der anderen Funktion. Ich musste Dictionary.doc2bow auf dem Dokument tun, bevor es nachgab. Und wie du es geschrieben hast, akzeptiert es Tupel. Vielen Dank! –

+1

Diese Antwort beschreibt die unmittelbare Ursache des letztendlichen Symptoms, aber die wirkliche Lösung besteht nicht einfach darin, "ein falsches zweites Element hinzuzufügen", und die wahre Ursache ist eine viel höhere Ebene; 'TfidfModel' benötigt eine Eingabe in einer bestimmten Form, und das Hinzufügen gefälschter zweiter Elemente würde den Code ebenfalls nicht funktionieren lassen. – ShadowRanger

1

Es sieht aus wie Ihr Problem ist, dass TfidfModel ein corpus erwartet, dass ein list von doc2bow Ausgänge (selbst list s von zwei tuple s) sind. Ihr ursprünglicher funktionierender Code doc2bow korrekt von Ihren einfachen Zeichenfolgen in das Korpusformat konvertieren, Ihr neuer Code wird in rohen Strings übergeben, nicht die "Vektoren" TfidfModel erwartet.

Gehen Sie zurück zu verwenden doc2bow und read the tutorial on converting string to vectors, die es klar macht, dass rohe Zeichenfolgen unsinnig als Eingabe sind.

Verwandte Themen