2017-04-24 3 views
16

Ich suche Ratschläge, wie man Elemente aus mehreren Textdateien abliest, um ein Wörterbuch zu erstellen.Python analysiert Text aus mehreren TXT-Dateien

Diese Textdatei: https://pastebin.com/Npcp3HCM

wurde manuell in diese transformierte Datenstruktur erforderlich: https://drive.google.com/file/d/0B2AJ7rliSQubV0J2Z0d0eXF3bW8/view

Es gibt Tausende solcher Textdateien und sie unterschiedliche Abschnittsüberschriften haben können wie in diesen Beispielen gezeigt:

  1. https://pastebin.com/wWSPGaLX
  2. https://pastebin.com/9Up4RWHu

begann ich durch das Lesen der Dateien

from glob import glob 

txtPth = '../tr-txt/*.txt' 
txtFiles = glob(txtPth) 

with open(txtFiles[0],'r') as tf: 
    allLines = [line.rstrip() for line in tf] 

sectionHeading = ['Corporate Participants', 
        'Conference Call Participiants', 
        'Presentation', 
        'Questions and Answers'] 

for lineNum, line in enumerate(allLines): 
    if line in sectionHeading: 
     print(lineNum,allLines[lineNum]) 

Meine Idee war, suchen die Zeilennummern in dem Abschnittsüberschriften existiert und versuchen zu extrahieren den Inhalt zwischen diesen Zeilennummern, dann Streifen aus Separatoren wie Striche aus . Das hat nicht funktioniert, und ich bin bei dem Versuch, ein Wörterbuch dieser Art zu erstellen, steckengeblieben, damit ich später verschiedene Verarbeitungsalgorithmen für natürliche Sprache auf Bruchstücken ausführen kann.

{file-name1:{ 
    {date-time:[string]}, 
    {corporate-name:[string]}, 
    {corporate-participants:[name1,name2,name3]}, 
    {call-participants:[name4,name5]}, 
    {section-headings:{ 
     {heading1:[ 
      {name1:[speechOrderNum, text-content]}, 
      {name2:[speechOrderNum, text-content]}, 
      {name3:[speechOrderNum, text-content]}], 
     {heading2:[ 
      {name1:[speechOrderNum, text-content]}, 
      {name2:[speechOrderNum, text-content]}, 
      {name3:[speechOrderNum, text-content]}, 
      {name2:[speechOrderNum, text-content]}, 
      {name1:[speechOrderNum, text-content]}, 
      {name4:[speechOrderNum, text-content]}], 
     {heading3:[text-content]}, 
     {heading4:[text-content]} 
     } 
    } 
} 

Die Herausforderung besteht darin, dass verschiedene Dateien unterschiedliche Überschriften und Überschriften haben können. Aber es wird immer einen Abschnitt namens "Präsentation" geben, der sehr wahrscheinlich "Frage und Antwort" enthält. Diese Abschnittsüberschriften sind immer durch eine Zeichenkette mit Gleichheitszeichen getrennt. Der Inhalt verschiedener Sprecher wird immer durch Bindestriche getrennt. Die "Sprachreihenfolge" für Q & Ein Abschnitt ist mit einer Zahl in eckigen Klammern gekennzeichnet. Die Teilnehmer sind immer am Anfang des Dokuments mit einem Sternchen vor ihrem Namen gekennzeichnet und ihre Kachel befindet sich immer in der nächsten Zeile.

Jeder Vorschlag zum Parsen der Textdateien ist willkommen. Die ideale Hilfe wäre, eine Anleitung zu geben, wie ein solches Wörterbuch (oder eine andere geeignete Datenstruktur) für jede Datei erstellt wird, die dann in eine Datenbank geschrieben werden kann.

Dank

--EDIT--

Eine der Dateien wie folgt aussieht: https://pastebin.com/MSvmHb2e

, in dem die „Frage & Antwort“ Abschnitt als „Präsentation“ falsch beschriftet ist und es gibt keine andere "Frage & Antwort" Abschnitt.

Und letzte Beispieltext: https://pastebin.com/jr9WfpV8

+3

Ich werde nicht vorschlagen, dass Sie in einem einzigen 'dict' Objekt alle Textdaten zu speichern, wie Sie erwähnt haben, dass Es könnte eine große Anzahl von zu analysierenden Textdateien geben. In der Laufzeit würde der Python-Prozess mehr Zeit benötigen, um das 'dict'-Objekt zu aktualisieren, wenn die Größe des' dict' -Objekts zunimmt, und könnte OutOfMemory erhalten einige sehr große Dateien zu verarbeiten, würde ich auf einige "DBMS" setzen, um diese Art von Daten zu speichern. – ZdaR

+0

@ZdaR danke für den Rat. Nachdem ich Ihren Kommentar gelesen hatte, entschied ich mich, mit der Datenbank zu arbeiten. Ich bin derzeit auf der Suche in sqlalchemy – samkhan13

+0

Mislabeling wird nicht so einfach zu lösen sein. Sie müssen einen Klassifikator mit ML-Techniken erstellen, der einen Abschnitt als "Präsentation" oder "Frage & Antwort" kategorisiert, da es keine garantierten Hinweise gibt (es wird keine Menge an Mustererkennung mit handgemachten Regeln erhalten) richtig 100%) im Text vorhanden. – entrophy

Antwort

8

Die Kommentare im Code sollte alles erklären. Lassen Sie mich wissen, wenn irgendetwas unter spezifiziert ist, und mehr Kommentare benötigt.

Kurz gesagt setze ich regex ein, um die '=' Trennlinien zu finden, um den gesamten Text in Unterabschnitte zu unterteilen. Behandle dann jeden Typ von Sektionen aus Gründen der Übersichtlichkeit (damit du sehen kannst, wie ich jeden Fall behandle).

Randnotiz: Ich verwende das Wort "Teilnehmer" und "Autor" synonym.

BEARBEITEN: Aktualisiert den Code zum Sortieren basierend auf dem '[x]' Muster gefunden direkt neben dem Teilnehmer/Autor in der Präsentation/QA Abschnitt. Änderte auch den hübschen Druckteil, da pprint OrderedDict nicht sehr gut verarbeitet.

Um alle zusätzlichen Leerzeichen einschließlich \n irgendwo in der Zeichenfolge abzustreifen, einfach str.strip(). Wenn Sie nur \n entfernen möchten, dann tun Sie einfach str.strip('\n').

Ich habe den Code geändert, um alle Leerzeichen in den Gesprächen zu entfernen.

import json 
import re 
from collections import OrderedDict 
from pprint import pprint 


# Subdivides a collection of lines based on the delimiting regular expression. 
# >>> example_string =' ============================= 
#      asdfasdfasdf 
#      sdfasdfdfsdfsdf 
#      ============================= 
#      asdfsdfasdfasd 
#      ============================= 
# >>> subdivide(example_string, "^=+") 
# >>> ['asdfasdfasdf\nsdfasdfdfsdfsdf\n', 'asdfsdfasdfasd\n'] 
def subdivide(lines, regex): 
    equ_pattern = re.compile(regex, re.MULTILINE) 
    sections = equ_pattern.split(lines) 
    sections = [section.strip('\n') for section in sections] 
    return sections 


# for processing sections with dashes in them, returns the heading of the section along with 
# a dictionary where each key is the subsection's header, and each value is the text in the subsection. 
def process_dashed_sections(section): 

    subsections = subdivide(section, "^-+") 
    heading = subsections[0] # header of the section. 
    d = {key: value for key, value in zip(subsections[1::2], subsections[2::2])} 
    index_pattern = re.compile("\[(.+)\]", re.MULTILINE) 

    # sort the dictionary by first capturing the pattern '[x]' and extracting 'x' number. 
    # Then this is passed as a compare function to 'sorted' to sort based on 'x'. 
    def cmp(d): 
     mat = index_pattern.findall(d[0]) 
     if mat: 
      print(mat[0]) 
      return int(mat[0]) 
     # There are issues when dealing with subsections containing '-'s but not containing '[x]' pattern. 
     # This is just to deal with that small issue. 
     else: 
      return 0 

    o_d = OrderedDict(sorted(d.items(), key=cmp)) 
    return heading, o_d 


# this is to rename the keys of 'd' dictionary to the proper names present in the attendees. 
# it searches for the best match for the key in the 'attendees' list, and replaces the corresponding key. 
# >>> d = {'mr. man ceo of company [1]' : ' This is talk a' , 
# ...  'ms. woman ceo of company [2]' : ' This is talk b'} 
# >>> l = ['mr. man', 'ms. woman'] 
# >>> new_d = assign_attendee(d, l) 
# new_d = {'mr. man': 'This is talk a', 'ms. woman': 'This is talk b'} 
def assign_attendee(d, attendees): 
    new_d = OrderedDict() 
    for key, value in d.items(): 
     a = [a for a in attendees if a in key] 
     if len(a) == 1: 
      # to strip out any additional whitespace anywhere in the text including '\n'. 
      new_d[a[0]] = value.strip() 
     elif len(a) == 0: 
      # to strip out any additional whitespace anywhere in the text including '\n'. 
      new_d[key] = value.strip() 
    return new_d 


if __name__ == '__main__': 
    with open('input.txt', 'r') as input: 
     lines = input.read() 

     # regex pattern for matching headers of each section 
     header_pattern = re.compile("^.*[^\n]", re.MULTILINE) 

     # regex pattern for matching the sections that contains 
     # the list of attendee's (those that start with asterisks) 
     ppl_pattern = re.compile("^(\s+\*)(.+)(\s.*)", re.MULTILINE) 

     # regex pattern for matching sections with subsections in them. 
     dash_pattern = re.compile("^-+", re.MULTILINE) 

     ppl_d = dict() 
     talks_d = dict() 

     # Step1. Divide the the entire document into sections using the '=' divider 
     sections = subdivide(lines, "^=+") 
     header = [] 
     print(sections) 
     # Step2. Handle each section like a switch case 
     for section in sections: 

      # Handle headers 
      if len(section.split('\n')) == 1: # likely to match only a header (assuming) 
       header = header_pattern.match(section).string 

      # Handle attendees/authors 
      elif ppl_pattern.match(section): 
       ppls = ppl_pattern.findall(section) 
       d = {key.strip(): value.strip() for (_, key, value) in ppls} 
       ppl_d.update(d) 

       # assuming that if the previous section was detected as a header, then this section will relate 
       # to that header 
       if header: 
        talks_d.update({header: ppl_d}) 

      # Handle subsections 
      elif dash_pattern.findall(section): 
       heading, d = process_dashed_sections(section) 

       talks_d.update({heading: d}) 

      # Else its just some random text. 
      else: 

       # assuming that if the previous section was detected as a header, then this section will relate 
       # to that header 
       if header: 
        talks_d.update({header: section}) 

     #pprint(talks_d) 
     # To assign the talks material to the appropriate attendee/author. Still works if no match found. 
     for key, value in talks_d.items(): 
      talks_d[key] = assign_attendee(value, ppl_d.keys()) 

     # ordered dict does not pretty print using 'pprint'. So a small hack to make use of json output to pretty print. 
     print(json.dumps(talks_d, indent=4)) 
+0

Ich kann diese Antwort akzeptieren, wenn Sie die Sprachreihenfolge zusammen mit der Rede in 'talks_d' einschließen können. Die Reihenfolge der Rede wird durch eckige Klammern angezeigt. Es wäre nützlich, wenn talk_d ein geordnetes Wörterbuch wäre. – samkhan13

+0

wie '\ n' aus dem Text in talks_d ausstreichen? – samkhan13

+0

aktualisierte die Antwort mit den angeforderten Änderungen. – entrophy

3

Könnten Sie bitte bestätigen, dass Sie nur die Abschnitte "Präsentation" und "Frage und Antwort" benötigen? Auch in Bezug auf die Ausgabe ist es in Ordnung, CSV-Format ähnlich dem, was Sie "manuell umgewandelt" haben.

Aktualisierte Lösung für jede von Ihnen bereitgestellte Beispieldatei.

Ausgabe ist von Zelle "D: H" gemäß "Parsed-Transkript" Datei freigegeben.

#state = ["other", "head", "present", "qa", "speaker", "data"] 
# codes : 0, 1, 2, 3, 4, 5 
def writecell(out, data): 
    out.write(data) 
    out.write(",") 

def readfile(fname, outname): 
    initstate = 0 
    f = open(fname, "r") 
    out = open(outname, "w") 
    head = "" 
    head_written = 0 
    quotes = 0 
    had_speaker = 0 
    for line in f: 
     line = line.strip() 
     if not line: continue 
     if initstate in [0,5] and not any([s for s in line if "=" != s]): 
      if initstate == 5: 
       out.write('"') 
       quotes = 0 
       out.write("\n") 
      initstate = 1 
     elif initstate in [0,5] and not any([s for s in line if "-" != s]): 
      if initstate == 5: 
       out.write('"') 
       quotes = 0 
       out.write("\n") 
       initstate = 4 
     elif initstate == 1 and line == "Presentation": 
      initstate = 2 
      head = "Presentation" 
      head_written = 0 
     elif initstate == 1 and line == "Questions and Answers": 
      initstate = 3 
      head = "Questions and Answers" 
      head_written = 0 
     elif initstate == 1 and not any([s for s in line if "=" != s]): 
      initstate = 0 
     elif initstate in [2, 3] and not any([s for s in line if ("=" != s and "-" != s)]): 
      initstate = 4 
     elif initstate == 4 and '[' in line and ']' in line: 
      comma = line.find(',') 
      speech_st = line.find('[') 
      speech_end = line.find(']') 
      if speech_st == -1: 
       initstate = 0 
       continue 
      if comma == -1: 
       firm = "" 
       speaker = line[:speech_st].strip() 
      else: 
       speaker = line[:comma].strip() 
       firm = line[comma+1:speech_st].strip() 
      head_written = 1 
      if head_written: 
       writecell(out, head) 
       head_written = 0 
      order = line[speech_st+1:speech_end] 
      writecell(out, speaker) 
      writecell(out, firm) 
      writecell(out, order) 
      had_speaker = 1 
     elif initstate == 4 and not any([s for s in line if ("=" != s and "-" != s)]): 
      if had_speaker: 
       initstate = 5 
       out.write('"') 
       quotes = 1 
      had_speaker = 0 
     elif initstate == 5: 
      line = line.replace('"', '""') 
      out.write(line) 
     elif initstate == 0: 
      continue 
     else: 
      continue 
    f.close() 
    if quotes: 
     out.write('"') 
    out.close() 

readfile("Sample1.txt", "out1.csv") 
readfile("Sample2.txt", "out2.csv") 
readfile("Sample3.txt", "out3.csv") 

Einzelheiten

in dieser Lösung ist eine Zustandsmaschine, die funktioniert wie folgt: 1. erkennt, ob Überschrift vorhanden ist, wenn ja, schreiben Sie es 2. Lautsprecher erkennt nach Überschrift geschrieben 3 schreibt Notizen für diesen Lautsprecher 4. schaltet zum nächsten Lautsprecher und so weiter ...

Sie können die csv-Dateien später wie gewünscht verarbeiten. Sie können die Daten auch in einem beliebigen Format auffüllen, nachdem die grundlegende Verarbeitung durchgeführt wurde.

Edit:

Bitte ersetzen Sie die Funktion „writecell“

def writecell(out, data): 
    data = data.replace('"', '""') 
    out.write('"') 
    out.write(data) 
    out.write('"') 
    out.write(",") 
+0

Ihr Ansatz ist meiner Anforderung am nächsten. Es behandelt alle bereitgestellten Beispieldateien genau. aber manchmal gibt es nach dem Firmennamen ein Koma, das die Ausgabestruktur durcheinanderbringt. Ich kann die Antwort akzeptieren, die am besten für die Beispiele im Abschnitt --EDIT-- der Frage gelöst wird. – samkhan13

+0

es ist in Ordnung, direkt in eine CSV-Datei oder ein Diktat oder eine Datenbank zu schreiben – samkhan13

+0

Hallo Ich habe meine Antwort basierend auf Ihrem Feedback aktualisiert. Danke für die Rückmeldung. – mangupt

Verwandte Themen