2017-08-30 3 views
5

ich einen Rechnungs Baum, die mit Einzügen/Räumen in der Quelle gespeichert sind:Wie Hierarchie analysieren basierend auf Einzüge mit Python

Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 

Es gibt eine feste Anzahl von Ebenen, so würde Ich mag die abzuflachen Hierarchie unter Verwendung von 3 Feldern (ist-Daten 6 Niveaus, beispielsweise vereinfacht):

L1  L2   L3 
Income 
Income Revenue 
Income Revenue  IAP 
Income Revenue  Ads 
Income Other-Income 
Expenses Developers In-house 
... etc 

ich kann dies tun, indem die Anzahl der Leerzeichen vor dem Kontonamen überprüft:

for rownum in range(6,ws.max_row+1): 
    accountName = str(ws.cell(row=rownum,column=1).value) 
    indent = len(accountName) - len(accountName.lstrip(' ')) 
    if indent == 0: 
     l1 = accountName 
     l2 = '' 
     l3 = '' 
    elif indent == 3: 
     l2 = accountName 
     l3 = '' 
    else: 
     l3 = accountName 

    w.writerow([l1,l2,l3]) 

Gibt es eine flexiblere Möglichkeit, dies zu erreichen, basierend auf dem Einrücken der aktuellen Zeile im Vergleich zur vorherigen Zeile, anstatt anzunehmen, dass es immer 3 Leerzeichen pro Ebene gibt? L1 wird immer keinen Einzug haben, und wir können darauf vertrauen, dass niedrigere Ebenen weiter eingerückt werden als ihre übergeordneten, aber vielleicht nicht immer 3 Leerzeichen pro Ebene.

Update, endete mit dieser als das Fleisch der Logik, da ich schließlich die Kontenliste mit dem Inhalt wollte, schien es am einfachsten, nur den Einzug zu verwenden, um zu entscheiden, ob die Liste zurücksetzen, anhängen oder Pop:

 if indent == 0: 
      accountList = [] 
      accountList.append((indent,accountName)) 
     elif indent > prev_indent: 
      accountList.append((indent,accountName)) 
     elif indent <= prev_indent: 
      max_indent = int(max(accountList,key=itemgetter(0))[0]) 
      while max_indent >= indent: 
       accountList.pop() 
       max_indent = int(max(accountList,key=itemgetter(0))[0]) 
      accountList.append((indent,accountName)) 

So ist die accountList in jeder Zeile der Ausgabe abgeschlossen.

Antwort

4

Sie können die Art und Weise nachahmen, wie Python den Einzug tatsächlich analysiert. Erstellen Sie zuerst einen Stapel mit den Einzugsebenen. An jeder Zeile:

  • Wenn die Vertiefung größer als die Spitze des Stapels ist, schieben und die Tiefenebene erhöhen.
  • Wenn es gleich ist, fahren Sie auf dem gleichen Niveau fort.
  • Wenn es niedriger ist, heben Sie den oberen Teil des Stapels auf, während er höher als der neue Einzug ist. Wenn Sie eine niedrigere Einrückungsstufe finden, bevor Sie genau das Gleiche gefunden haben, liegt ein Einrückungsfehler vor.
indentation = [] 
indentation.append(0) 
depth = 0 

f = open("test.txt", 'r') 

for line in f: 
    line = line[:-1] 

    content = line.strip() 
    indent = len(line) - len(content) 
    if indent > indentation[-1]: 
     depth += 1 
     indentation.append(indent) 

    elif indent < indentation[-1]: 
     while indent < indentation[-1]: 
      depth -= 1 
      indentation.pop() 

     if indent != indentation[-1]: 
      raise RuntimeError("Bad formatting") 

    print(f"{content} (depth: {depth})") 

Mit einer "test.txt" Datei, deren Inhalt, wie Sie zur Verfügung gestellt:

Income (depth: 0) 
Revenue (depth: 1) 
IAP (depth: 2) 
Ads (depth: 2) 
Other-Income (depth: 1) 
Expenses (depth: 0) 
Developers (depth: 1) 
In-house (depth: 2) 
Contractors (depth: 2) 
Advertising (depth: 1) 
Other Expense (depth: 1) 

Also, was können:

Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 

Hier ist der Ausgang machst du damit? Angenommen, Sie möchten verschachtelte Listen erstellen. Erstellen Sie zunächst einen Datenstapel.

  • Wenn Sie eine Einrückung finden, fügen Sie eine neue Liste am Ende des Datenstapels an.
  • Wenn Sie eine Independent-Datei gefunden haben, blenden Sie die oberste Liste ein und fügen Sie sie dem neuen Anfang hinzu.

Und unabhängig, für jede Zeile, fügen Sie den Inhalt an die Liste an der Spitze des Datenstacks.

Hier ist die entsprechende Implementierung:

for line in f: 
    line = line[:-1] 

    content = line.strip() 
    indent = len(line) - len(content) 
    if indent > indentation[-1]: 
     depth += 1 
     indentation.append(indent) 
     data.append([]) 

    elif indent < indentation[-1]: 
     while indent < indentation[-1]: 
      depth -= 1 
      indentation.pop() 
      top = data.pop() 
      data[-1].append(top) 

     if indent != indentation[-1]: 
      raise RuntimeError("Bad formatting") 

    data[-1].append(content) 

while len(data) > 1: 
    top = data.pop() 
    data[-1].append(top) 

Ihre verschachtelte Liste ist an der Spitze Ihres data Stack. Der Ausgang für die gleiche Datei ist:

['Income', 
    ['Revenue', 
     ['IAP', 
     'Ads' 
     ], 
    'Other-Income' 
    ], 
'Expenses', 
    ['Developers', 
     ['In-house', 
     'Contractors' 
     ], 
    'Advertising', 
    'Other Expense' 
    ] 
] 

Das ist ziemlich leicht zu manipulieren, wenn auch recht tief verschachtelt. Sie die Daten, die durch die Verkettung des Artikels zugreifen können Zugriffe:

>>> l = data[0] 
>>> l 
['Income', ['Revenue', ['IAP', 'Ads'], 'Other-Income'], 'Expenses', ['Developers', ['In-house', 'Contractors'], 'Advertising', 'Other Expense']] 
>>> l[1] 
['Revenue', ['IAP', 'Ads'], 'Other-Income'] 
>>> l[1][1] 
['IAP', 'Ads'] 
>>> l[1][1][0] 
'IAP' 
+0

Vielen Dank für diese, ich wollte schließlich die Hierarchie in jeder Zeile zusammen mit dem Inhalt der Zeile ausgeben können, so dass ich leicht modifiziert, aber das brachte mich in die richtige Richtung. –

2

Wenn die Vertiefung eine feste Menge von Räumen (3 Plätze hier) ist, können Sie die Berechnung des Einzugsebene vereinfachen.

Anmerkung: verwende ich eine StringIO eine Datei

import io 
import itertools 

content = u"""\ 
Income 
    Revenue 
     IAP 
     Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 
""" 

stack = [] 
for line in io.StringIO(content): 
    content = line.rstrip() # drop \n 
    row = content.split(" ") 
    stack[:] = stack[:len(row) - 1] + [row[-1]] 
    print("\t".join(stack)) 

Sie erhalten zu simulieren:

Income 
Income Revenue 
Income Revenue IAP 
Income Revenue Ads 
Income Other-Income 
Expenses 
Expenses Developers 
Expenses Developers In-house 
Expenses Developers Contractors 
Expenses Advertising 
Expenses Other Expenses 

EDIT: Einzug nicht fixiert

Wenn die Vertiefung nicht ist behoben (Sie haben nicht immer 3 Leerzeichen) wie im Beispiel unten:

content = u"""\ 
Income 
    Revenue 
    IAP 
    Ads 
    Other-Income 
Expenses 
    Developers 
     In-house 
     Contractors 
    Advertising 
    Other Expenses 
""" 

Sie müssen die Verschiebung bei jeder neuen Zeile schätzen:

stack = [] 
last_indent = u"" 
for line in io.StringIO(content): 
    indent = "".join(itertools.takewhile(lambda c: c == " ", line)) 
    shift = 0 if indent == last_indent else (-1 if len(indent) < len(last_indent) else 1) 
    index = len(stack) + shift 
    stack[:] = stack[:index - 1] + [line.strip()] 
    last_indent = indent 
    print("\t".join(stack)) 
Verwandte Themen