Posts tagged ‘Python’

Auto-Documenting Python Code

A while ago I thought about auto-documenting Python code, here’s what resulted from those experiments. (It’s far away from production quality, so use at your own risk)#

Let’s assume we have a Python file without docstrings:

class HalloWelt:
 
	def Hallo(welt):
		return welt
 
 
print(HalloWelt.Hallo("Welt"))

My experimental Python code:

import re
 
class Dokumenter:
	"""
	Fügt einer bestehenden Python-Datei Docstrings hinzu, falls keine vorhanden sind.
	"""
 
	def dokumentme(filename):
		print(">> Prüfe",filename,"auf Docstrings\n")
 
		with open(filename+"_bak", 'w') as outfile:
			with open(filename, 'r') as infile:
				rowIter= iter(infile)
				for row in rowIter:
					# schreibe die Zeile auf jeden Fall in die Zieldatei
					outfile.write(row)
					# Ist in der Zeile ein 'def ' vorhanden?
					if "def " in row:
						# suche erstes Zeichen, das kein Docstring ist
						index = re.search('\S', row).start()
						whitespace = row[:index]
						whitespaceLen = len(whitespace)
						if " " in whitespace:
							blanks = True
						else:
							blanks = False					
						print(whitespaceLen,blanks)	
 
 
						print(">> Funktionsdefinition gefunden")
						print(">> Schreibe Docstring")
						print(">> Whitespaces",index)
						outfile.write('"""\nHallo Welt\n"""\n')
					print(row)
 
Dokumenter.dokumentme("dokme.py")
# Tests, Datei mit und ohne Dokstring,unterschiedliche Einrückungstiefe
# extrahiere die Parameter

Output:

class HalloWelt:
 
	def Hallo(welt):
"""
Hallo Welt
"""
		return welt
 
 
print(HalloWelt.Hallo("Welt"))

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Sichere (deutsche) Passwörter mit Python und xkcdpass

Menschen sind schlecht darin, sich gute lange Passwörter zu merken. Computer sind dafür prädestiniert, schlechte (weil kurze) Passwörter zu knacken. Randall Munroe hat das in einem recht bekannten XKCD Comic dargestellt.

Mit xkcdpass (Link) gibt es ein Python-Modul zur Erstellung von Passwörtern nach dem „xkcd-Schema“, das sich mittels pip install xkcdpass einfach installieren lässt.

Startet man xkcdpass ohne weitere Parameter, so spuckt es einfach eine Reihe englischer Wörter aus:

C:\Users\Uwe>xkcdpass
tackle tonsorial satisfied gecko fission decor

Um xkcdpass die Ausgabe deutscher Passwörter beizubringen, geht man wie folgt vor.

1) Mittels --help Parameter kommt man an die Liste aller Optionen, hier ist besonders -w für die Angabe der Wortdatei spannend.

C:\Users\Uwe>xkcdpass --help
usage: xkcdpass [-h] [-w WORDFILE] [--min MIN_LENGTH] [--max MAX_LENGTH]
                [-n NUM_WORDS] [-i] [-v VALID_CHARS] [-V] [-a ACROSTIC]
                [-c COUNT] [-d DELIM] [--allow-weak-rng]

optional arguments:
  -h, --help            show this help message and exit
  -w WORDFILE, --wordfile WORDFILE
                        Specify that the file WORDFILE contains the list of
                        valid words from which to generate passphrases.
  --min MIN_LENGTH      Generate passphrases containing at least MIN_LENGTH
                        words.
  --max MAX_LENGTH      Generate passphrases containing at most MAX_LENGTH
                        words.
  -n NUM_WORDS, --numwords NUM_WORDS
                        Generate passphrases containing exactly NUM_WORDS
                        words.
  -i, --interactive     Generate and output a passphrase, query the user to
                        accept it, and loop until one is accepted.
  -v VALID_CHARS, --valid-chars VALID_CHARS
                        Limit passphrases to only include words matching the
                        regex pattern VALID_CHARS (e.g. '[a-z]').
  -V, --verbose         Report various metrics for given options.
  -a ACROSTIC, --acrostic ACROSTIC
                        Generate passphrases with an acrostic matching
                        ACROSTIC.
  -c COUNT, --count COUNT
                        Generate COUNT passphrases.
  -d DELIM, --delimiter DELIM
                        Separate words within a passphrase with DELIM.
  --allow-weak-rng      Allow fallback to weak RNG if the system does not
                        support cryptographically secure RNG. Only use this if
                        you know what you are doing.

2) Man besorgt sich eine Datei mit dem deutschen Wortschatz, per Google bin ich auf die folgende Seite der Uni Leipzig gestoßen: http://wortschatz.uni-leipzig.de/html/wliste.html. Hier gibt es Listen für die häufigsten 100, 1000 und 10’000 Wörter der deutschen Sprache (sowie auch für französisch, englisch und niederländisch). Die Liste mit den 10’000 häufigsten Wörtern speichern wir ab.

Ein Hinweis noch zur sprachlichen Einschätzung der 10000 Wörter: Der Duden geht davon aus, dass ein durchschnittlicher Deutsch-Sprecher zwischen 12’000 und 16’000 Wörter spricht, aber rund 50’000 deutsche Worte versteht.

3) Mit der Wortschatzdatei auf der Festplatte können wir jetzt einfach wieder xkcdpass aufrufen und nutzen gleich noch -c 10 für die Erzeugung von 10 Zeilen mit jeweils sechs Passwörtern:

C:\Users\Uwe>xkcdpass -c 10 -w e:\top10000de.txt
Munition Alexander Bernhard Wissen Erstmals geraumer
wissen genießt wenigen Verlierer wunderbar Teufel
Wiesen Hartmut länger römische älter Stimme
irgendwie Monitor operative läuft Vertrieb Optionen
Stahl Brust Polizei Hoffnung Verlauf runden
Ärzte Mafia Dieter Pakistan Systems Areal
trieb hinweg Kanzlers unterlag zweimal zuviel
vollzogen Sparen zwölf verlieren Dutzende Länge
Infotyp beliebten gewisser Malerei gefunden Blätter
Peking äußerten fährt Einblick Interesse schwach

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Time in Python

A short summary on Python’s timestamps:

import datetime
 
now = datetime.datetime.now()
 
print(now.strftime('%Y-%m-%d %H:%M'))
print(now.isoformat())

From the module’s documentation:

Directive Meaning
%a Locale’s abbreviated weekday name.
%A Locale’s full weekday name.
%b Locale’s abbreviated month name.
%B Locale’s full month name.
%c Locale’s appropriate date and time
representation.
%d Day of the month as a decimal number [01,31].
%H Hour (24-hour clock) as a decimal number
[00,23].
%I Hour (12-hour clock) as a decimal number
[01,12].
%j Day of the year as a decimal number [001,366].
%m Month as a decimal number [01,12].
%M Minute as a decimal number [00,59].
%p Locale’s equivalent of either AM or PM.
%S Second as a decimal number [00,61].
%U Week number of the year (Sunday as the first
day of the week) as a decimal number [00,53].
All days in a new year preceding the first
Sunday are considered to be in week 0.
%w Weekday as a decimal number [0(Sunday),6].
%W Week number of the year (Monday as the first
day of the week) as a decimal number [00,53].
All days in a new year preceding the first
Monday are considered to be in week 0.
%x Locale’s appropriate date representation.
%X Locale’s appropriate time representation.
%y Year without century as a decimal number
[00,99].
%Y Year with century as a decimal number.
%z Time zone offset indicating a positive or
negative time difference from UTC/GMT of the
form +HHMM or -HHMM, where H represents decimal
hour digits and M represents decimal minute
digits [-23:59, +23:59].
%Z Time zone name (no characters if no time zone
exists).
%% A literal '%' character.

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Slides from my 2016 Froscon Presentation „Using Python for Scientific Research“

Here are my slides from the Froscon 2016 presentation „Using Python for Scientific Research“.

Slides: Froscon_Slides_2016

Video: Video Recording (The screen was flickering most of the time, pretty annoying and distracting)

I will continously update and expand this presentation during the next months, if you want to receive updates follow the GitHub repository: https://github.com/UweZiegenhagen/2016-Python-Data-Analysis-Slides/

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Parsing Emacs Orgmode files with Python

Here’s some experimental (alpha) code to parse Emacs Orgmode files. It’s far from complete, I only aim at parsing basic TODO strings with level (**), status (TODO, DONE), priority (#A, #B, #C), task and tags.

2016-09-03: It takes my actual orgmode file, so it’s working fine.

2016-09-04: I created a github repo, code updates will be added there, only: https://github.com/UweZiegenhagen/python-orgmode-parser

# -*- coding: utf-8 -*-
import re
 
def parseEmaceOrgmode(s):
    r = '^([\*]+)?\s?(TODO|PROGRESSING|FEEDBACK|VERIFY|POSTPONED|DELEGATED|CANCELLED|DONE)?\s?(\[#[A|B|C]\])?\s?(.*?)\s*(:(.*):)?$'    
    m = re.search(r,s)
    level = m.group(1)
    if (level is not None):
        level = len(level)
    prio = m.group(3)
    if (prio is not None):
        prio = prio[2:3]
    tags = []
    a = m.group(5)
    if a != None:
        b = len(a)-1
        a= a[1:b]
        a = a.split(':')
    tags.append(a)
    return(level, m.group(2), prio, m.group(4), tags)
 
with open("../orgmode.org", "r") as ins:
    for line in ins:
        level, status, priority, task, tags = parseEmaceOrgmode(line)
        if level is not None:        
            print('Level:', level)
            print('Status:', status)
            print('Priority:', priority)
            print('Task:', task)
            print('Tags:',tags,'\n\n')

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Spalte aus Text-Datei extrahieren mit Python

Hier ein Quick & Dirty Code, um eine Spalte aus einer Text-Datei zu extrahieren. Geht auch mit AWK, aber wenn man nur Python hat…

def splitFileOneColumn(inputFile,outputFile,columnSeparator,column):
    with open(inputFile, 'r') as infile:
        with open(outputFile, 'w') as outfile:
            for line in infile:
                s = line.split(columnSeparator)
                outfile.write(s[column]+os.linesep) # '\r\n' on Windows, '\n' on Unix/Linux/Mac
            outfile.close()
    infile.close()

Bei Gelegenheit muss ich das mal um die Möglichkeit erweitern, n Spalten zu extrahieren.

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Daten aggregieren mit pandas

I recently came across a „challenge“ where I needed to combine various rows. Each row was identified by Key1 and Key2 and had two interesting columns, Foo and Bar. For each Key1 there may be a few Key2, for each Key2 n Foo/Bar entries. While all Foos are distinct per Key1 and Key2 the Bar column may appear j times.

The goal was to get a list of unique Bar items for each Key1/Key2 combination.

Key1 Key2 Foo Bar
0 C1 T1 a1 rc-1
1 C1 T1 a2 rc-1
2 C1 T1 a3 rc-1
3 C1 T1 a4 rc-1
4 C2 T2 b1 rc-1
5 C2 T2 b2 rc-2
6 C3 T3 c1 rc-3
7 C4 T4 d1 rc-4
8 C4 T4 d2 rc-5
9 C4 T4 d3 rc-4

The following Python code nicely did the job, thanks to http://stackoverflow.com/questions/17841149/pandas-groupby-how-to-get-a-union-of-strings

# -*- coding: utf-8 -*-
import pandas as pd
 
def unique(liste):
    """ takes a list of elements, separated by comma and returns sorted string of unique items separated by comma """
    a = liste.split(',')
    b = sorted(set(a))
    return ','.join(b)
 
df = pd.read_excel('groupb_Beispiel.xlsx')
print(df)
 
grouped = df.groupby(['Key1','Key2'],as_index=False)['Bar'].agg(lambda col: ','.join(col))
grouped = pd.DataFrame(grouped)
 
grouped['Unique'] = grouped['Bar'].apply(unique)
 
print(grouped)
 
grouped.to_excel('result.xlsx')
Key1 Key2 Bar Unique
0 C1 T1 rc-1,rc-1,rc-1,rc-1 rc-1
1 C2 T2 rc-1,rc-2 rc-1,rc-2
2 C3 T3 rc-3 rc-3
3 C4 T4 rc-4,rc-5,rc-4 rc-4,rc-5

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Zeilen kombinieren mit pandas

Vor einiger Zeit hatte ich eine Excel-Datei zu bearbeiten, in der in einer Spalte die Spaltennamen, in einer anderen die korrespondieren Werte standen. Immer drei Zeilen bildeten den eigentlichen Datensatz. Mit wenigen Zeilen Pandas und cleverer Adressierung der Ergebnis-Zelle.

Spaltenname Wert
ColA Andi
ColB Berni
ColC Cesar
ColA Dorian
ColB Ernest
ColC Frank

 

import pandas as pd
 
# Lade die Daten
daten = pd.read_excel('combine.xlsx')
# Erstelle leeren Dataframe mit den Spaltennamen aus den Excelzeilen 
verarbeitet = pd.DataFrame(columns=['ColA','ColB','ColC'])
 
# Iteriere über die Daten
for i, row in daten.iterrows():
    # ganzzahliges Teilen, um die Zeile zu bestimmen
    # in die die Zelle gehört, Spalte ergibt sich aus dem Wert in 'Spalte'
    verarbeitet.loc[i // 3,row['Spalte']] = row['Wert']
 
print(verarbeitet)
ColA ColB ColC
0 Andi Berni Cesar
1 Dorian Ernest Frank

Nachtrag: Stephan vom Kölner Data Science Meetup hat mir noch einen alternativen Weg gezeigt:

import pandas as pd
 
data = {'A': ["cola", "colb", "colc", "cola", "colb", "colc"], "B": [1, 2, 3, 4, 5, 6]}
data = pd.DataFrame(data)
gb = data.groupby('A')
res = pd.DataFrame()
for key in gb.groups:
    res[key] = gb.get_group(key)['B'].values.flatten()
 
print(res)

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Spendenbescheinigungen erstellen mit LaTeX und pandas

Nach Buchungen auswerten mit Python Pandas hier der zweite Artikel zum Thema pandas.

Ausgangspunkt bilden die Formulare der Finanzdirektion, die ich in LaTeX umgesetzt habe; ihr findet sie unter https://github.com/UweZiegenhagen/spendenquittungen-mit-latex.

Wenn man dieses Formular mit entsprechenden Jinja2 Variablen anreichert (dazu später mehr), erhält man Sammelbestaetigung_Geldzuwendung (PDF) (TeX Code)

Die Aufgabe, die sich jetzt für das automatische Befüllen mittels pandas stellt, ist die folgende:

  1. Lies die Stammdaten ein und bereite sie auf
  2. Lies die Buchungen (aus Quicken 2015) ein und bereite sie auf
  3. Befüll das entsprechende LaTeX-Template
  4. Erzeuge die fertige PDF Datei

Hier der Quellcode nebst Erläuterung:

  • jinja2 nutzen wir als Template-Engine, os für die pdflatex-Aufrufe, codecs für ein wenig UTF8-Gewusel
  • cleanPLZ und prepareAddress sind einfache Hilfsfunktionen. Die erste reinigt die PLZ (pandas nimmt an, das hier Floats drinstehen), die zweite erzeugt den Adressstring für die Ausgabe
  • kardinal erzeugt das Zahlwort, siehe dazu den verlinkten Beitrag vom Finanzamt.
  • Richtig los geht es ab „##################### Prepare Stammdaten #####################“:
    • Ich lade die Stammdaten in einen Dataframe und ersetzte fehlende Werte durch “
    • Die PLZ wird bereinigt (geht sicher auch einfacher), ehemalige Mitglieder (mit Status ‚E‘) werden entfernt
    • Die Buchungen werden geladen, fehlende Werte durch 0 bzw. “ ersetzt.
    • Relevant sind nur Buchungstypen wie Mitgliedsbeitrag und Aufnahmegebühr, alles andere wird entfernt.
    • Die Jinja2 Komponente wird konfiguriert
    • Für jeden Stammdatensatz werden die Stammdaten aufbereitet und die Buchungen eingesammelt.
    • Ein wenig Python/pandas Magie bereitet die daten auf und schreibt alles in das Template
    • Dieses Template wird dann in eine TeX Datei geschrieben und nach PDF übersetzt.

Hier der Link zur Zip-Datei mit allen benötigten Dateien: AlleDateien

Update vom 18.01.2021: Der Python-Code in der Datei hat nicht mehr funktioniert. In der folgenden Datei daher das Code-Update.

prepareSpenden-04

import pandas as pd # pandas selbst
 
import jinja2
import os
import codecs
 
def cleanPLZ(stringToClean):
    return stringToClean.replace('.0','')
 
# Eine Funktion, die die Adresse vorbereitet
# keine überflüssigen Leerzeichen, wenn Feld nicht gefüllt ist
def prepareAddress(id, vorname, name, strasse, plz, ort):
    address = '' # + str(id) + ': '
    if len(vorname)==0:
        address = address + name
    else:
        address = address + vorname + ' ' + name
    if len(strasse)>0:
        address = address + ", " + strasse    
    if len(plz)>0:
        address = address + ", " + plz + ' ' + ort
    return address
 
# Zerlege die Gesamtsumme in einzelne Bestandteile, um Zahlwort auszugeben
# Siehe http://www.steuer-schutzbrief.de/fileadmin/downloads/BMF-Schreiben/BMF-Schreiben-Zuwendungsbestaetigung-2012-08-30.pdf
def kardinal(summenstring,separator,indicator):
	zahlen = {"1" : "Eins", "2":"Zwei", "3":"Drei", "4":"Vier","5":"Fünf","6":"Sechs","7":"Sieben","8":"Acht","9":"Neun","0":"Null"}
	zahlwort = ''
	zahl = summenstring.split(',')[0]
	for i in zahl:
		zahlwort = zahlwort + zahlen[i]+ separator
	return indicator + separator + zahlwort + indicator
 
# Diverse Konfigurationsvariablen
# http://chrisalbon.com/python/pandas_list_unique_values_in_column.html
# Set ipython's max row display
pd.set_option('display.max_row', 10000)
# Set iPython's max column width to 50
pd.set_option('display.max_columns', 50)
# A set number format to 2 digits
pd.set_option('display.float_format', lambda x: '%.2f' % x)
# http://stackoverflow.com/questions/20625582/how-to-deal-with-this-pandas-warning
# komische Fehlermeldung beim Drop von Spalten loswerden
pd.options.mode.chained_assignment = None  # default='warn'
 
##################### Prepare Stammdaten ##################### 
# lade Daten
stammdaten = pd.read_excel('Stammdaten.xlsx', 'Tabelle1')
 
# Remove NaN values by " for strings
stammdaten['Vorname'].fillna(value='',inplace=True)
stammdaten['Name'].fillna(value='',inplace=True)
stammdaten['Adresszusatz'].fillna(value='',inplace=True)
stammdaten['Strasse'].fillna(value='',inplace=True)
stammdaten['PLZ'].fillna(value='',inplace=True)
stammdaten['Ort'].fillna(value='',inplace=True)
stammdaten['eMail'].fillna(value='',inplace=True)
stammdaten['Mitgliedsart'].fillna(value='',inplace=True)
 
# convert PLZ to string
# apply str function first, then run cleanPLZ on the string
stammdaten['PLZ']= stammdaten.PLZ.apply(str)
stammdaten['PLZ']= stammdaten.PLZ.apply(cleanPLZ)
 
# entferne ehemalige Mitglieder
stammdaten  = stammdaten[stammdaten.Mitgliedsart != 'E']
 
################################ Prepare Buchungen
# lade die Buchungen
buchungen = pd.read_excel('Buchungen.xlsx', 'Tabelle1')
buchungen[['Klasse']] = buchungen[['Klasse']].astype(str)
buchungen[['Betrag']] = buchungen[['Betrag']].astype(float)
 
# Change format of 'Buchungstag' to datetime
buchungen['Buchungstag'] = pd.to_datetime(buchungen['Buchungstag'],dayfirst=True)
 
# Remove NaN values by " for strings or 0 for numbers 
buchungen['Vorgang'].fillna(value=0,inplace=True)
buchungen['Empfänger'].fillna(value='',inplace=True)
buchungen['Verwendungszweck'].fillna(value='',inplace=True)
buchungen['Kategorie'].fillna(value='',inplace=True)
buchungen['Klasse'].fillna(value='',inplace=True)
buchungen['Relevant']=True
buchungen.Kategorie.str.match('^Aufnahmegebühr|Zweckspende|Mitgliedsbeitrag|Spende$')
 
# entferne irrelevante Buchungen
buchungen = buchungen[buchungen.Relevant != False]
 
# http://stackoverflow.com/questions/20937538/how-to-display-pandas-dataframe-using-a-format-string-for-columns
#pd.options.display.float_format = '{:,.2f} EUR'.format
 
class CommaFloatFormatter:
    def __mod__(self, x):
        return str(x).replace('.',',')
 
latex_jinja_env = jinja2.Environment(
    block_start_string = '\BLOCK{',
    block_end_string = '}',
    variable_start_string = '\VAR{',
    variable_end_string = '}',
    comment_start_string = '\#{',
    comment_end_string = '}',
    line_statement_prefix = '%-',
    line_comment_prefix = '%#',
    trim_blocks = True,
    autoescape = False,
    loader = jinja2.FileSystemLoader(os.path.abspath('.'))
)
 
# Laden des Templates aus einer Datei
template = latex_jinja_env.get_template('Sammelbestaetigung_Geldzuwendung.tex')
 
for index, row in stammdaten.iterrows():
    print("ID:",row["ID"])
    address = prepareAddress(row["ID"],row['Vorname'],row['Name'],row['Strasse'],row['PLZ'],row['Ort'])
    print(address)
    beitraege = buchungen[buchungen.Klasse.str.contains('^' +  str(row["ID"]) + '$')]
    beitraege.drop('Klasse',axis=1,inplace=True)
    beitraege.drop('Verwendungszweck',axis=1,inplace=True)
    beitraege.drop('Relevant',axis=1,inplace=True)
    beitraege.drop('Empfänger',axis=1,inplace=True)
    beitraege.drop('Konto',axis=1,inplace=True)
    beitraege.drop('Vorgang',axis=1,inplace=True)
    gesamtsumme = beitraege.sum()[0]
 
    beitraege['Buchungstag'] = beitraege['Buchungstag'].apply(lambda x: x.strftime('%d-%m-%Y'))
    texbuchungen = beitraege.applymap(lambda x: str(x).replace('.',',0')).to_latex(index=False)    
    texbuchungen = beitraege.to_latex(index=False)    
    summe = str(gesamtsumme).replace('.',',0') + ' EUR'
    # kardinal(summe,'-','xxx')
    dokument = template.render(Spender=address, ID=row['ID'],Summe=summe,kardinal=kardinal(summe,'-','xxx'),Buchungen=texbuchungen)
    with codecs.open(''+str(row['ID']) + ".tex", "w","utf-8") as letter:
        letter.write(dokument);
        letter.close();
        os.system("pdflatex -interaction=batchmode " + str(row['ID']) + ".tex")
 
os.system("del *.log")
os.system("del *.aux")

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website

Buchungen auswerten mit Python Pandas

Ich bin ehrenamtlich Schatzmeister der Dingfabrik Köln e.V., als solcher muss ich auch schauen, welches Mitglied denn die Beiträge bezahlt hat. Mit Hilfe von Pandas lassen sich die Buchungsdaten sehr elegant aufbereiten.

Ausgangspunkt sind die folgenden Excel-Dateien

Mitglieder.xlsx

Enthält die Mitgliedsnummer, den Namen sowie die Mitgliedsart (F für Fördermitglied, O für Ordentlich, E für Ehemalig, etc.)

Klasse Name Mitgliedsart
1 Max Mustermann O

Buchungen.xlsx

Die Buchungen wurden aus Quicken 2015 nach Excel exportiert, die Datei muss leider noch manuell bearbeitet werden, da der Excel-Export von Quicken nicht sonderlich schön ist. Die relevante Kategorie ist in diesem Beispiel „Mitgliedsbeitrag“, Klasse enthält die zugewiesene Mitgliedsnummer.

Buchungstag Konto Vorgang Empfänger Verwendungszweck Kategorie Klasse Betrag
04.01.2016 Firmengirokonto Köln 3763   Buchungstext bla, bla Mitgliedsbeitrag 1 23,00

Diese beiden Dateien können wir jetzt mit Pandas verarbeiten.

import pandas as pd
import numpy as np
import time as t
 
# Anpassungen an der Pandas-Ausgabe
pd.set_option('display.float_format', lambda x: '%.2f' % x)
# http://stackoverflow.com/questions/11707586/python-pandas-widen-output-display
pd.set_option('display.height', 1000)
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)
 
# lies die Stammdaten aus Excel
mitglieder = pd.read_excel('Mitglieder.xlsx', 'Tabelle1')
# konvertier die Mitgliedsnummer in einen String
mitglieder['Klasse'] = mitglieder['Klasse'].apply(int).apply(str)
# Ehemalige Mitglieder interessieren nicht
mitglieder = mitglieder[mitglieder.Mitgliedsart.str.contains('A|F|O')]
 
# lies die Buchungen ein
buchungen = pd.read_excel('Buchungen.xlsx', 'Sheet')
# entferne alle Zeilen, die keinen 'Mitgliedbeitrag' enthalten
buchungen = buchungen[buchungen.Kategorie.str.contains('Mitgliedsbeitrag')]
 
# entferne Zeilen ohne Betrag oder Klasse
buchungen = buchungen[np.isfinite(buchungen['Betrag'])]
buchungen = buchungen[np.isfinite(buchungen['Klasse'])]
 
# Konvertiere den Buchungstag in ein Pandas-Datum
buchungen['Buchungstag'] = pd.to_datetime(buchungen['Buchungstag'],dayfirst=True,format='%d.%m.%Y') 
 
# Füge neue Spalten für Quartal und Monat hinzu
buchungen['Quartal'] = buchungen['Buchungstag'].dt.quarter
buchungen['Monat'] = buchungen['Buchungstag'].dt.month
 
# Wandle 'Klasse' von Float => Integer => String 
buchungen['Klasse'] = buchungen['Klasse'].apply(int).apply(str) 
 
# Führe einen right join durch, wir wollen auch die Datensätze von Mitgliedern haben, die noch aktiv sind, aber noch nichts bezahlt haben
buchungen = pd.merge(buchungen,mitglieder, how='right', on=['Klasse', 'Klasse'])
 
# Durch den right Join haben wir jetzt einige NaN (Not a Number) Zellen, die wir durch 0.0 ersetzen
# Dadurch erzeugen wir Dummy Datensätze, die aber nicht stören
buchungen['Betrag'].fillna(value=0,inplace=True)
buchungen['Buchungstag'].fillna(value=pd.Timestamp('20160101'),inplace=True)
# Einen Eintrag für die Monatsspalte nehmen wir auch vor, darüber wird pivotisiert
buchungen['Monat'] = buchungen['Buchungstag'].dt.month
 
# Erstellung der Pivot-Table
pivotTable = pd.pivot_table(buchungen,dropna=False,margins=True,index=['Name'],values=['Betrag'],columns=['Monat'],aggfunc=np.sum)
 
print(pivotTable)
 
# Optional: Rausschreiben der Tabelle nach Excel
#pivotTable.to_excel('pivotisiert.xlsx')

Das Ergebnis, hier anonymisiert und bei Betrag nur jeweils 1.0 eingetragen, sieht dann so aus:

pivot

Uwe

Uwe Ziegenhagen likes LaTeX and Python, sometimes even combined. Do you like my content and would like to thank me for it? Consider making a small donation to my local fablab, the Dingfabrik Köln. Details on how to donate can be found here Spenden für die Dingfabrik.

More Posts - Website