Seite 1 von 1

Perioden als Kopfzeile

Verfasst: Dienstag 7. Dezember 2010, 19:56
von hobitt
Hallo,

ich bin neu hier und auch neu in Python.
Ich bin auch kein Programmierer , sondern benötige ab und zu ein Skript
zur Datenanalyse im Rechnungswesen.

Für ein Projekt habe ich gerade ein Skript fertigstellt, das auch wie erwartet funktioniert.
Als Buchhalter habe ich aber die Excel-Sicht auf Daten und kann den Schalter beim Coden nur schwer umlegen.

Deshalb wollte ich noch eine Meinung einholen.
Die Aufgabe war. die Datenfelder:

"Service-Auftrag, Auftragssumme, Startdatum, Enddatum"

in einem Report darzustellen, so dass die Monate/Perioden die Kopfzeile bilden
und unter der jeweiligen Periode die anteilige Auftragssumme ausgewiesen wird.
Bsp.
01.2009 02.2009 03.2009
120,- 100,- 120,-
Die Aufträge haben natürlich unterschiedliche Laufzeiten, Start- und Enddaten.
Es muß also die erste mögliche und die letzte mögliche Periode ermittelt werden.
Dann Ausgabe in eine CSV-Datei, die in Excel importiert wird.

Das Problem war die Kopfzeile. Deshalb auch meine Frage:
Ich habe die Daten mit "readline" eingelesen, da ich nicht weiss, wie groß so eine Datei werden kann.
Gibt es einen einfacheren Weg, als die Datei _zweimal_ einzulesen, um die Kopzeile zu ermitteln?

Danke!

Re: Perioden als Kopfzeile

Verfasst: Dienstag 7. Dezember 2010, 20:09
von Darii
hobitt hat geschrieben:Gibt es einen einfacheren Weg, als die Datei _zweimal_ einzulesen, um die Kopzeile zu ermitteln
Was spricht denn dagegen, erst die erste Zeile per readline() einzulesen und dann die Datei normal weiterzuverarbeiten?

Code: Alles auswählen

from __future__ import with_statement
with open("file") as f:
    header = f.readline()
    for line in f: # tue irgendwas

Re: Perioden als Kopfzeile

Verfasst: Dienstag 7. Dezember 2010, 20:18
von hobitt
Die erste Zeile existiert ja noch nicht.
Die Kopfzeile soll sich aus den Perioden zusammensetzen,
die sich aus den Start- und Enddaten aller Auftrage ergeben.

In der Spalte, z.B. "06.2009" sollen also die jeweilige anteiligen Auftragssummen
aller zu diesem Zeitpunkt noch laufenden Aufträge stehen.
Pro Auftrag und pro Periode ein Betrag, untereinander unter der entsprechenden Periode.

Re: Perioden als Kopfzeile

Verfasst: Dienstag 7. Dezember 2010, 21:04
von Darii
Hmm...verstehe. Sicher, dass die Daten so groß sind, dass du sie nicht einfach komplett im Speicher halten kannst?

Re: Perioden als Kopfzeile

Verfasst: Mittwoch 8. Dezember 2010, 00:41
von BlackJack
@hobitt: Man kann die Datensätze doch einfach *einmal* Zeilenweise einlesen und dabei die Daten zusammen fassen und ganz am Ende dann aus den zusammengefassten Daten diese beiden Zeilen erzeugen. Man könnte zum Beispiel ein Dictionary erstellen, das Daten/Perioden auf die Summe abbildet und dann dort jeden Eingangsdatensatz entsprechend auf Einträge in dem Dictionary aufaddieren. Ganz am Ende sortiert man die Daten noch nach Datum und gibt die Schlüssel in der ersten und die dazugehörigen Teilsummen in der zweiten Zeile aus. `collections.defaultdict` bietet sich hier als Container an.

Wenn man sonst nur Tabellen gewohnt ist, übersieht man vielleicht schnell, dass man keine direkte Umsetzung einer Eingangstabelle auf eine Ausgangstabelle machen muss, sondern dass man die Daten dazwischen in eine beliebige, nicht auf eine Tabelle beschränkte Struktur überführen kann, die für die Verarbeitung am besten passt.

Re: Perioden als Kopfzeile

Verfasst: Mittwoch 8. Dezember 2010, 17:29
von hobitt
@Darii: Das Tool wird bei einer internationalen Gesellschaft
während einer Prüfung eingesetzt. Wieviele Daten bei jeder einzelnen Company anfallen,
kann ich nicht einschätzen. Und auch nicht, über welche Hardwareausstattung sie verfügen.

@BlackJack:
Ersten, ich müßte auch dabei die Daten komplett im Speicher halten.
Zweitens denke ich aber schon, daß Du grundsätzlich recht hast und ich nicht genügen Ahnung oder Vorstellungsvermögen von Dictionaries.

So ungefähr sieht das Ergebnis aus:

Document Number;SoP;Salesprice;Startdate;Enddate;Days_Total;Sales_Price_per_Day;01.2007; ...; 05.2009;06.2009;07.2009;08.2009;09.2009;10.2009;11;2009; ...
'Auftrag 1'; '8';3.924,00; '2009-07-01'; '2012-06-30';1096;3,58; 0,00;...; 0,00;0,00;110,99;110,99;107,41;110,99;107,41; ...
'Auftrag 2';'12';5.250,00; '2009-01-01'; '2012-06-30';1277;4,11; etc.
...(noch ein paar tausen Zeilen)

Ich ermittle beim ersten Einlesen des Files die niedrigste und die höchste Periode mittels eine Dictionaries, sowie alle Peroden dazwischen und erstelle damit schon mal die Kopfzeile.
Dann lese ich das File nochmal Zeile für Zeile ein und benute den Dictionary mit den Perioden um die Periodenwerte den einzelne Spalten zuzuordnen.
Selbst wenn ich alle Daten im Speicher halten würde, bräuchte ich einen zweidimensionalen Dictionary:

Headline; Perioden ...
Auftrag1; Daten;
Auftrag 2;Daten;
...
Da habe ich, ehrlich gesagt, keinen Plan.

Ich kann meinen aktuelle Code gere posten, wenn es jemand interessiert ( Und mir jemand sagt, wie man Code postet)

Re: Perioden als Kopfzeile

Verfasst: Mittwoch 8. Dezember 2010, 18:24
von BlackJack
@hobitt: Bei meinem Vorschlag müsstest Du nicht alle Daten im Speicher behalten sondern nur die, die dann am Ende auch ausgegeben werden. Da sehe ich aber sowieso keinen Weg dran vorbei.

Du musst im ersten Durchgang doch keine komplette Kopfzeile aufbauen bevor Du mit dem eintragen/aufaddieren der Daten anfangen kannst. Es muss während der Verarbeitung ja keine "Tabelle" bestehen die von Anfang an Spalten für jede Periode hat die innerhalb der Daten "berührt" wird. Diese Informationen kann man doch auch während der Verarbeitung erstellen. Wenn Du einen Datensatz einliest und der einen Zeitraum umfasst, für den es noch keine, oder zumindest nicht alle Perioden in der Datenstruktur gibt, dann erstellst Du sie eben bei Bedarf.

Edit: Wobei ich in Deinem Beitrag vor diesem hier gerade sehe, dass das Ergebnis nicht aus zwei Zeilen besteht, sondern auch für jeden Auftrag eine Zeile geschrieben wird!?

Re: Perioden als Kopfzeile

Verfasst: Mittwoch 8. Dezember 2010, 19:42
von hobitt
@Blackjack:
Edit: Wobei ich in Deinem Beitrag vor diesem hier gerade sehe, dass das Ergebnis nicht aus zwei Zeilen besteht, sondern auch für jeden Auftrag eine Zeile geschrieben wird!?
Ja, das ist richtig. Eine Kopfzeile mit allen möglichen Perioden und dann viele tausend Zeilen mit Auftragsnummern.
@hobitt: Bei meinem Vorschlag müsstest Du nicht alle Daten im Speicher behalten sondern nur die, die dann am Ende auch ausgegeben werden. Da sehe ich aber sowieso keinen Weg dran vorbei.
Das ist richtig. Ich brauche eigentlich nur die benötigten Felder aus meinem File PLUS die Berechnungen per Periode.
Und die müsste ich dann für alle Aufträge im Speicher halten. Da kommt schon was zusammen.

Das sind die Felder, die ich auslese:
Document Number;SOP Transaction Type;Document Date;Posting Date;Debtor ID;Debtor Name;SOP Order Number;Original Type;Sales Person;Sales Territory;SOP Line Number;Item Number;Item Description;Item Type;Item Class;Flash Category;Category;Def.;List Price;SOP Quantity;Sales Price;PO Number;Unit Cost;Cost Value;Currency ID;Originating Unit Cost;Originating Cost Value;Margin;Credit Note Reference;Original Invoice Number;Start Date;End Date

Ich hänge den Code mal an (nicht professionell, aber zumindest funktioniert er):

Code: Alles auswählen

#!/usr/bin/env python

from calendar import monthrange
from datetime import date
from dateutil.relativedelta import *
import locale
locale.setlocale(locale.LC_ALL, '')


#### Modules

# convert Dates in datetime.date
def ConvDat(d):
    sdat = d.split(".")
    day = int(sdat[0])
    month = int(sdat[1])
    year = int(sdat[2])
    if year < 1000: year += 2000
    conv_date =date(year, month, day)
    return conv_date

# calculate days in a month
def Daysmonth(date):
    d = (monthrange(date.year, date.month))[1]
    return d

# convert datetime.date as string
def Pdate(date):
    p = (str(date.year)	+ "."
        + str(date.month).zfill(2))
    return p

#### Main ####


# open files
try:
    input =  open('Smartlist.csv', 'r')
    output = open('Deferrals.csv', 'w')

except IOError:
    print 'file not found'


# Create headline
startdate_low = {}
enddate_high = {}
for line in input:
    row = line.rstrip('\n')                                         # read CSV-file
    if "Document Number" in row: continue                           # skip if Headline
    field =  row.split(";")                                         # split in fields
    if field[17].lower() != "y": continue                           # skip if not Deferrals
    if field[20].startswith("-"): continue 	                    # skip if alesprice empty
    salesprice = locale.atof(field[20])                             # convert salesprice in  local format
    if salesprice <= 0: continue                                    # skip if salesprice <0
    startdate_low[ConvDat(field[30])] = ''                          # add all existing startdates to a list
    enddate_high[ConvDat(field[31])] = ''                           # add all existing enddates to a list
    


ls = startdate_low.keys()                                           # calculate lowest startdate and
ls.sort()                                                           # highest enddate
le = enddate_high.keys()
le.sort()
start = ls[0]
end = le[-1]
head = []
while start <= end:                                                 # calculate each period between
    head.append(str(start.year) + '.' \
    + str(start.month).zfill(2)	+ ';')                              # the lowest startdate and
    start += relativedelta(months=+1)                               # the highest enddate

head.sort()
headline = (['Document Number;',
             'SoP;',
             'Salesprice;',
             'Startdate;', 
             'Enddate;'
             'Days_Total;'
             'Sales_Price_per_Day;']
              +	head)                                               # Additional fields for headline

headline += '\n'                                                    # linebreak for headline
for xa in headline:	                                            # print header for csv-file
    output.write(xa)                                                # one column for each period

period = head[:]                                                    # copy and save headline

# Create data for each column

input.seek(0)
for line in input:
    row = line.rstrip('\n')	                                    # read csv-file again
    if "Document Number" in row: continue                           # skip if Headline
    field =  row.split(";")                                         # split in fields
    if field[17].lower() != "y": continue                           # skip if not Deferrals
    if field[20].startswith("-"): continue                          # skip if salesprice empty
    salesprice = locale.atof(field[20])                             # convert in  local format
    if salesprice <= 0:	continue                                    # skip if salesprice <= 0

    document_n  = field[0]                                          # read Document Number
    startdate   = ConvDat(field[30])                                # read and convert Startdate
    enddate     = ConvDat(field[31])                                # read and convert Enddate

    defer_time = enddate - startdate                                # calculate contract days
    defer_time_p1 = int(defer_time.days) + 1                        # add one for the first day
    salesprice_day = (salesprice / defer_time_p1)                   # calculate contract amount per day

# clean
    defer_period = {}							
    deferrals = []                                                  
    for xb in period:                                               
        xc = xb[:-1]                                                
        defer_period[xc] = 0
    

# calculate Deferrals for each period
    
    first_period = startdate
    last_period  = enddate
    
    while first_period <= last_period:
        days_in_month = Daysmonth(first_period)                     # Days in a Period
        def_month = salesprice_day * days_in_month                  # Deferrals in Period
        act_per = (Pdate(first_period))                             # actual period	

    	if act_per in defer_period:	                            # enter Deferrals/Month in
            defer_period[act_per] = def_month                       # the right period

    	first_period += relativedelta(months=+1)                    # go to following month
    
# startperiod is probably not a full period	

    days_in_month = Daysmonth(startdate) - startdate.day  
    days_in_month_p1 = int(days_in_month) + 1                       # add one day for the first day
    def_month = salesprice_day * days_in_month_p1
    act_per = (str(startdate.year) + "."
              + str(startdate.month).zfill(2))
    if act_per in defer_period:	
        defer_period[act_per] = def_month

# for endperiod	is probably not a full period

    days_in_month = enddate.day
    def_month = salesprice_day * days_in_month
    act_per = (str(enddate.year) + "."
              + str(enddate.month).zfill(2))
    if act_per in defer_period:
	defer_period[act_per] = def_month
    
# if period is less than one month
    
    p1 = (enddate - startdate).days
    p2 = monthrange (startdate.year, startdate.month)[1]
    if p1 < p2 and startdate.month == enddate.month:
	days_in_month = enddate.day - startdate.day  
    	days_in_month_p1 = int(days_in_month) + 1                       # add one day for the first day
    	def_month = salesprice_day * days_in_month_p1
    	act_per = (str(startdate.year) + "."
              + str(startdate.month).zfill(2))
    	if act_per in defer_period:	
        	defer_period[act_per] = def_month

# sort Deferrals to Month 

    line = defer_period.keys()
    line.sort()
    for xe in line:
        deferrals.append(defer_period[xe])

# convert string for Excel-Import
    string = ([document_n,
                field[10],
                salesprice,
                str(startdate),
                str(enddate),
                defer_time_p1,
                salesprice_day]
                + deferrals)
    string = (str(string).replace(',',';')
                .replace('.',',')
                .rstrip(']')
                .lstrip('['))

# write string in txt-file  

    output.write(string + '\n')
    print document_n


print "ready"
input.close()
output.close()
raw_input()