Guten Abend, ich bin noch recht drisch mit Python.
Ich wollte mir ein Script schreiben mit dem ich meine Finanzen regelmäßig in eine Excel Tabelle eintrage.
Im Moment bin ich erst am Header. Hier würde ich gerne eine "def" schreiben mit der ich in die vorhandene Tabelle eine weitere Spalte oder Zelle am ende der letzten Zelle einfügen kann.
Ich hab schon sehr viel versucht aber es klappt nicht, Das einzige was ich geschafft habe ist, das die Zeile neu erstellt wird. Dann ist aber die Formatierung aus "def __init__" weg.
Hat jemand eine Idee und eventuell einen Tipp was ich besser machen könnte?
Ich finde für die wenigen Funktionen habe ich leider bisher viel Code erzeugt, das geht sicher viel einfacher oder?
Danke
"""
Overview about my Bank account's
"""
__author__ =""
__version__ ="0.0.1"
__copyright__ ="Copyright 10/2023
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import xlsxwriter
class make_header():
# Header erstellen
def __init__(self, filename: str, mysheet_name: str):
self.filename = filename
self.mysheet_name = mysheet_name
self.columns=["Date", "Total amount", "Bank1", "Bank2", "Bank3", "Bank4", "Bank5", "Bank6", "Bank7", "Bank8", "Bank9"]
df = pd.DataFrame(columns=self.columns)
writer = pd.ExcelWriter(filename+".xlsx", engine='xlsxwriter')
df.to_excel(writer, sheet_name= mysheet_name, header=False, index=False)
worksheet = writer.sheets[mysheet_name]
workbook = writer.book
header_format = workbook.add_format(
{
"bold":True,
"text_wrap":True,
"valign": "top",
"fg_color": "#D7E4BC",
"border": 1,
}
)
# Write the column headers with the defined format.
for col_num, value in enumerate(df.columns.values):
worksheet.set_column('B:L', 10)
worksheet.write(0, col_num + 1, value, header_format)
writer.close()
def __str__(self):
return "Values"+ self.filename + self.mysheet_name
def add_header(self, new_header:str):
#self.columns.append(new_header)
new_df = pd.read_excel(self.filename+".xlsx",sheet_name = self.mysheet_name, header=0)
new_df.head()
new_df.insert(loc=len(self.columns)+1, column=new_header, value=0)
print(new_df)
writer= pd.ExcelWriter(self.filename+".xlsx", engine='xlsxwriter')
new_df.to_excel(writer, sheet_name= self.mysheet_name)
writer.close()
test=make_header("myBanking", "Total amount")
test.add_header(new_header="Test")
Excel File mit Pandas lesen und beschreiben
- noisefloor
- User
- Beiträge: 4194
- Registriert: Mittwoch 17. Oktober 2007, 21:40
- Wohnort: WW
- Kontaktdaten:
Hallo,
bitte den Code in einem Codeblock posten, damit der besser lesbar ist und vor allen Dingen die Einrückungen sichtbar sind. Einen Codeblock bekommst du über einen Klick auf die Schaltfläche </> im vollständigen Editor hier im Forum.
pandas ist hier noch überflüssig, weil du nichts nutzt, wofür Panda gemacht ist. Wenn du nur openpyxl nutzt wird der Code einfacher.
Ich würde auch erstmal die Klasse weg lassen, weil die hier auch nicht wirklich nötig ist. Wenn der Code läuft, das mit den Excel-Dokumenten klappt und man dann die Funktionalität erweitern will kann man das immer noch in eine Klasse packen.
Gruß, noisefloor
bitte den Code in einem Codeblock posten, damit der besser lesbar ist und vor allen Dingen die Einrückungen sichtbar sind. Einen Codeblock bekommst du über einen Klick auf die Schaltfläche </> im vollständigen Editor hier im Forum.
pandas ist hier noch überflüssig, weil du nichts nutzt, wofür Panda gemacht ist. Wenn du nur openpyxl nutzt wird der Code einfacher.
Ich würde auch erstmal die Klasse weg lassen, weil die hier auch nicht wirklich nötig ist. Wenn der Code läuft, das mit den Excel-Dokumenten klappt und man dann die Funktionalität erweitern will kann man das immer noch in eine Klasse packen.
Gruß, noisefloor
- __blackjack__
- User
- Beiträge: 14052
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Matt87: Noch ein paar Anmerkungen zum Quelltext: Das heisst „bank accounts“ ohne Apostroph. Und auch eher „of“ oder „over“ als „about“ IMHO.
Wenn die Meta-Variablen am Anfang keine sinnvollen Werte enthalten, sollten sie da auch nicht stehen.
Von den Importen bleibt eigentlich nur Pandas übrig. Alle anderen Importe werden nirgends verwendet.
Klassen repräsentieren Dinge im weitesten Sinne. `make_header` ist kein ”Ding”, das beschreibt eine Tätigkeit. Zudem entspricht die Schreibweise nicht den Konventionen: alles in klein_mit_unterstrichen, ausser Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Wenn ich jetzt mal raten müsste was diese Klasse vielleicht repräsentieren soll, dann wäre das wohl ein Arbeitsblatt in einer Exceldatei, also `Sheet`.
`my` ist eine unsinnige Vorsilbe. Wenn es nicht auch ein `our` oder `their` gibt, wogegen sich das abgrenzen würde, ist da keinerlei Information für den Leser enthalten und das sollte einfach wegfallen.
Das der Dateiname keine Endung enthält und das die jedes mal wenn der Benutzt wird aufs neue angehängt wird, ist überraschend, umständlich, und fehleranfällig.
`ExcelWriter`-Objekte sind Kontextmanager, die sollte man mit ``with`` verwenden.
`self.columns` ist redundant weil diese Werte ja auch in der Datei stehen. Und der Code geht davon aus, dass in `self.columns` und in der Datei immer die gleichen Werte stehen. Das kann also divergieren. Es sollte nach Möglichkeit immer nur *eine* Quelle der Wahrheit geben.
Es macht nicht wirklich Sinn mit `write_excel()` ohne Kopfzeilen zu schreiben, also letztlich *gar nichts* zu schreiben um danach dann an Pandas vorbei die Kopfzeilenwerte in das Arbeitsblatt einzutragen.
`enumerate()` kann man einen Startwert mitgeben, dann muss man da später keine magische Konstante drauf addieren.
Es macht keinen Sinn für jeden Spaltentitel immer und immer wieder für Spalte B bis L die gleiche Breite zu setzen. Warum überhaupt B bis L? Sollte das nicht vielleicht besser von der tatsächlichen Anzahl der Spalten(titel) abhängen?
Die `__str__()`-Implementierung ist unsinnig und sollte weg.
Was soll das `new_` bei `new_df` in `add_header()`? Da ist nichts neu an dem DataFrame. Und einen anderen DataFrame gibt es da nicht, den man als ”alten” DataFrame ansehen könnte.
Zwischenstand (ungetestet):
Aber wie schon gesagt wurde ist Pandas hier eher im Weg als hilfreich.
Dass der Objektzustand aus dem Dateinamen und dem Arbeitsblattnamen besteht und das erstellen des Objekts immer eine neue Datei anlegt/alte Datei überschreibt, ist auch komisch. Eventuell käme man hier auch noch mit einfach nur zwei Funktionen aus.
Wenn die Meta-Variablen am Anfang keine sinnvollen Werte enthalten, sollten sie da auch nicht stehen.
Von den Importen bleibt eigentlich nur Pandas übrig. Alle anderen Importe werden nirgends verwendet.
Klassen repräsentieren Dinge im weitesten Sinne. `make_header` ist kein ”Ding”, das beschreibt eine Tätigkeit. Zudem entspricht die Schreibweise nicht den Konventionen: alles in klein_mit_unterstrichen, ausser Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). Wenn ich jetzt mal raten müsste was diese Klasse vielleicht repräsentieren soll, dann wäre das wohl ein Arbeitsblatt in einer Exceldatei, also `Sheet`.
`my` ist eine unsinnige Vorsilbe. Wenn es nicht auch ein `our` oder `their` gibt, wogegen sich das abgrenzen würde, ist da keinerlei Information für den Leser enthalten und das sollte einfach wegfallen.
Das der Dateiname keine Endung enthält und das die jedes mal wenn der Benutzt wird aufs neue angehängt wird, ist überraschend, umständlich, und fehleranfällig.
`ExcelWriter`-Objekte sind Kontextmanager, die sollte man mit ``with`` verwenden.
`self.columns` ist redundant weil diese Werte ja auch in der Datei stehen. Und der Code geht davon aus, dass in `self.columns` und in der Datei immer die gleichen Werte stehen. Das kann also divergieren. Es sollte nach Möglichkeit immer nur *eine* Quelle der Wahrheit geben.
Es macht nicht wirklich Sinn mit `write_excel()` ohne Kopfzeilen zu schreiben, also letztlich *gar nichts* zu schreiben um danach dann an Pandas vorbei die Kopfzeilenwerte in das Arbeitsblatt einzutragen.
`enumerate()` kann man einen Startwert mitgeben, dann muss man da später keine magische Konstante drauf addieren.
Es macht keinen Sinn für jeden Spaltentitel immer und immer wieder für Spalte B bis L die gleiche Breite zu setzen. Warum überhaupt B bis L? Sollte das nicht vielleicht besser von der tatsächlichen Anzahl der Spalten(titel) abhängen?
Die `__str__()`-Implementierung ist unsinnig und sollte weg.
Was soll das `new_` bei `new_df` in `add_header()`? Da ist nichts neu an dem DataFrame. Und einen anderen DataFrame gibt es da nicht, den man als ”alten” DataFrame ansehen könnte.
Zwischenstand (ungetestet):
Code: Alles auswählen
"""
Overview of my bank accounts.
"""
import pandas as pd
class Sheet:
def __init__(self, filename, sheet_name):
self.filename = filename
self.sheet_name = sheet_name
df = pd.DataFrame(
columns=[
"Date",
"Total amount",
"Bank1",
"Bank2",
"Bank3",
"Bank4",
"Bank5",
"Bank6",
"Bank7",
"Bank8",
"Bank9",
]
)
with pd.ExcelWriter(self.filename, engine="xlsxwriter") as writer:
df.to_excel(
writer, sheet_name=self.sheet_name, header=False, index=False
)
header_format = writer.book.add_format(
{
"bold": True,
"text_wrap": True,
"valign": "top",
"fg_color": "#D7E4BC",
"border": 1,
}
)
worksheet = writer.sheets[self.sheet_name]
worksheet.set_column("B:L", 10)
for column_number, value in enumerate(df.columns.values, 1):
worksheet.write(0, column_number, value, header_format)
def add_header(self, header):
df = pd.read_excel(
self.filename, sheet_name=self.sheet_name, header=True
)
df.insert(loc=len(df.columns) + 1, column=header, value=0)
with pd.ExcelWriter(self.filename, engine="xlsxwriter") as writer:
df.to_excel(writer, sheet_name=self.sheet_name)
def main():
sheet = Sheet("myBanking.xlsx", "Total amount")
sheet.add_header("Test")
if __name__ == "__main__":
main()
Dass der Objektzustand aus dem Dateinamen und dem Arbeitsblattnamen besteht und das erstellen des Objekts immer eine neue Datei anlegt/alte Datei überschreibt, ist auch komisch. Eventuell käme man hier auch noch mit einfach nur zwei Funktionen aus.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Hey, vielen dank fürs Formatieren des Codeblocks und die ganzen Inputs.__blackjack__ hat geschrieben: Donnerstag 26. Oktober 2023, 15:45 @Matt87: Noch ein paar Anmerkungen zum Quelltext: Das heisst „bank accounts“ ohne Apostroph. Und auch eher „of“ oder „over“ als „about“ IMHO.
Aber wie schon gesagt wurde ist Pandas hier eher im Weg als hilfreich.
Dass der Objektzustand aus dem Dateinamen und dem Arbeitsblattnamen besteht und das erstellen des Objekts immer eine neue Datei anlegt/alte Datei überschreibt, ist auch komisch. Eventuell käme man hier auch noch mit einfach nur zwei Funktionen aus.
In der tat muss ich mir noch genauer überlegen wie ich strukturiert an die Sache ran gehe.
Pandas verwende ich erst mal nicht mehr fürs Excel File sondern wie von @Sirius3 empfohlen openpyxl.
Danke
Guten Abend, ich habe verschiedenes mit openpyxl ausprobiert.
Mit dem Code unten kann ich den Header erzeugen.
Was mir nicht gefällt, es schein keine Funktion zu geben, das die Spaltenbreite an die Länge der Zeichen angepasst wird.
wrap_text=True, macht sowas ähnliches, allerdings mit Zeilenumbrüchen.
Muss man wirklich jedes Feld einzeln aufrufen und beschreiben was damit passieren soll?
Gib es keine Möglichkeit das für eine ganze Spalte auszuführen also etwas eleganter wie in meinem Code hier?
In meinem Code wird jede Zeile A1 bis K1 beschrieben.
Danke
Mit dem Code unten kann ich den Header erzeugen.
Was mir nicht gefällt, es schein keine Funktion zu geben, das die Spaltenbreite an die Länge der Zeichen angepasst wird.
wrap_text=True, macht sowas ähnliches, allerdings mit Zeilenumbrüchen.
Muss man wirklich jedes Feld einzeln aufrufen und beschreiben was damit passieren soll?
Gib es keine Möglichkeit das für eine ganze Spalte auszuführen also etwas eleganter wie in meinem Code hier?
In meinem Code wird jede Zeile A1 bis K1 beschrieben.
Danke
Code: Alles auswählen
from openpyxl import Workbook
from openpyxl.styles import Font, Border, Side, NamedStyle, Alignment
def print_header():
wb = Workbook()
ws = wb.active
black = "000000"
font = Font(bold=True,
size=18,)
thick = Side(style="thick",
color=black)
border = Border(left=thick,
right=thick,
top=thick,
bottom=thick)
named_style = NamedStyle(name="highlight",
font=font,
border=border)
alignment = Alignment(horizontal='center',
vertical='bottom',
text_rotation=0,
wrap_text=True,
shrink_to_fit=False,
indent=0)
#sheet.column_dimensions['B'].width = 25
ws["A1"].style = named_style
ws["A1"].alignment = alignment
ws.cell(row=1, column=1).value = "Date"
ws["B1"].style = named_style
ws["B1"].alignment = alignment
ws.cell(row=1, column=2).value = "Total amount"
ws["C1"].style = named_style
ws["C1"].alignment = alignment
ws.cell(row=1, column=3).value = "Bank1"
ws["D1"].style = named_style
ws["D1"].alignment = alignment
ws.cell(row=1, column=4).value = "Bank1"
ws["E1"].style = named_style
ws["E1"].alignment = alignment
ws.cell(row=1, column=5).value = "Bank1 Bank1"
ws["F1"].style = named_style
ws["F1"].alignment = alignment
ws.cell(row=1, column=6).value = "Bank1 Bank1"
ws["G1"].style = named_style
ws["G1"].alignment = alignment
ws.cell(row=1, column=7).value = "Bank1 Bank1"
ws["H1"].style = named_style
ws["H1"].alignment = alignment
ws.cell(row=1, column=8).value = "Bank1 Bank1"
ws["I1"].style = named_style
ws["I1"].alignment = alignment
ws.cell(row=1, column=9).value = "Bank1 Bank1"
ws["J1"].style = named_style
ws["J1"].alignment = alignment
ws.cell(row=1, column=10).value = "Bank1 Bank1"
ws["K1"].style = named_style
ws["K1"].alignment = alignment
ws.cell(row=1, column=11).value = "Cash"
ws.auto_filter.ref = "A1:K1" # Set a Filter function to the sheet
ws.print_title_rows = '1:1'
wb.save(filename="myfinance.xlsx")
"""
Function to print the rows of xlsx File
"""
def print_rows():
for row in ws.iter_rows(values_only=True):
print(row)
print_rows()
print_header()
Warum referenzierst Du einmal die Zellen über Eckige Klammern und einmal über .cell?
Das ganze würde man kürzer mit einer Schleife schreiben:
Das ganze würde man kürzer mit einer Schleife schreiben:
Code: Alles auswählen
values = [
"Date", "Total amount", "Bank1", "Bank1", "Bank1 Bank1",
"Bank1 Bank1", "Bank1 Bank1", "Bank1 Bank1", "Bank1 Bank1",
"Bank1 Bank1", "Cash"
]
for column, value in enumerate(values, 1):
cell = ws.cell(row=1, column=column)
cell.style = named_style
cell.alignment = alignment
cell.value = value
- __blackjack__
- User
- Beiträge: 14052
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Matt87: Für die Spaltenbreite aufgrund vom Inhalt müsste man die Schriftart kennen und wie die vom Zielsystem gerendert wird.
`print_header()` ist inhaltlich falsch, da werden nirgends Kopfzeilen ausgegeben/gedruckt.
Das "K1" bei den Autofiltern ist fehleranfällig. Da muss man dran denken wenn man die Kopfzeilen mal anpassen will/muss. An der Stelle sollte man sich besser auf die letzte erzeugte Zelle beziehen und von *der* die Koordinate abfragen.
Zeichenketten mitten im Code sind keine Kommentare. Kommentare werden mit dem ``#``-Zeichen eingeleitet. Und nur damit. Zeichenketten mitten Code können Docstrings sein, sofern sie an der richtigen Stelle stehen. Für Funktionen *nach* der ``def``-Zeile. Wobei auch für Docstrings wie für Kommentare gilt, dass die dem Leser einen Mehrwert liefern sollten. Also nicht einfach nur der Funktionsname als Satz formuliert.
Die `print_rows()`-”Funktion” ist aber auch komisch. Man definiert nicht innerhalb einer Funktion eine weitere Funktion die dann einfach nur direkt nach der Definition aufgerufen wird. Ausnahme sind manchmal rekursive Funktionen oder wenn man ein Closure als Rückgabewert haben will. Aber hier könnte der Code auch einfach direkt an der Aufrufstelle stehen.
Ungetestet:
`print_header()` ist inhaltlich falsch, da werden nirgends Kopfzeilen ausgegeben/gedruckt.
Das "K1" bei den Autofiltern ist fehleranfällig. Da muss man dran denken wenn man die Kopfzeilen mal anpassen will/muss. An der Stelle sollte man sich besser auf die letzte erzeugte Zelle beziehen und von *der* die Koordinate abfragen.
Zeichenketten mitten im Code sind keine Kommentare. Kommentare werden mit dem ``#``-Zeichen eingeleitet. Und nur damit. Zeichenketten mitten Code können Docstrings sein, sofern sie an der richtigen Stelle stehen. Für Funktionen *nach* der ``def``-Zeile. Wobei auch für Docstrings wie für Kommentare gilt, dass die dem Leser einen Mehrwert liefern sollten. Also nicht einfach nur der Funktionsname als Satz formuliert.
Die `print_rows()`-”Funktion” ist aber auch komisch. Man definiert nicht innerhalb einer Funktion eine weitere Funktion die dann einfach nur direkt nach der Definition aufgerufen wird. Ausnahme sind manchmal rekursive Funktionen oder wenn man ein Closure als Rückgabewert haben will. Aber hier könnte der Code auch einfach direkt an der Aufrufstelle stehen.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, NamedStyle, Side
BLACK = "000000"
def main():
workbook = Workbook()
sheet = workbook.active
thick = Side("thick", BLACK)
style = NamedStyle(
"highlight",
Font(bold=True, size=18),
border=Border(left=thick, right=thick, top=thick, bottom=thick),
)
alignment = Alignment(
horizontal="center",
vertical="bottom",
wrap_text=True,
shrink_to_fit=False,
)
for column_number, value in enumerate(
[
"Date",
"Total amount",
"Bank1",
"Bank1",
"Bank1 Bank1",
"Bank1 Bank1",
"Bank1 Bank1",
"Bank1 Bank1",
"Bank1 Bank1",
"Bank1 Bank1",
"Cash",
],
1,
):
cell = sheet.cell(1, column_number, value)
cell.style = style
cell.alignment = alignment
sheet.auto_filter.ref = f"A1:{cell.coordinate}"
sheet.print_title_rows = "1:1"
workbook.save("myfinance.xlsx")
for row in sheet.iter_rows(values_only=True):
print(row)
if __name__ == "__main__":
main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari