Python und pandas: Excel Tabelle zusammenfassen

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Glyxbringer
User
Beiträge: 4
Registriert: Samstag 7. Oktober 2023, 11:33

Hallo liebe Community,

ich stehe vor einer Herausforderung und benötige eure Expertise. Ich arbeite mit einer Excel-Tabelle, die verschiedene Spalten enthält.
Ich möchte gleichartige Zeilen (die alle Transaktionen enthalten) dieser Tabelle basierend auf mehreren Kriterien zusammenfassen, um weniger Datensätze zu haben.
Die originale Excel-Datei hat mittlerweile mehr als 300.000 Einträge ist ca. 40MB groß und dementsprechend sperrig zu handhaben.

ChatGPT hat mich auf Python und pandas gebracht und nach mehreren Stunden bin ich nun an dem Punkt angekommen an dem ich nicht weiterkomme, weil ChatGPT immer wieder Sachen vergisst oder auslässt.

Ich kenne mich selbst weder mit Python, noch mit anderen Programmiersprachen aus und ich verstehe leider auch nur bruchstückhaft, was der von ChatGPT entwickelte Code genau macht.

Ich weiß, dass das eine ziemliche Zumutung für jeden Programmierer ist, weiß mir aber nicht anders zu helfen.

Hier ein Link zu einer Beispieltabelle bei docs.google
https://docs.google.com/spreadsheets/d/ ... sp=sharing


Ich habe mal versucht zusammenzustellen, was passieren soll, wobei mein Aufbau vielleicht unnötig kompliziert ist.
Ich hoffe es ist klar, was ich theoretisch erreichen möchte.

Zusammenfassungskriterien:

1. Wenn der Wert in der Spalte "isIgnored" "WAHR" lautet -> Zeile ignorieren.
2. Wenn der Wert in der Spalte "isIgnored" "Falsch" lautet -> Zeile verarbeiten.
3. Nur Zeilen mit identischem Eintrag bei "area" können zusammengefasst werden.
4. Nur Zeilen mit identischem Eintrag bei "location" können zusammengefasst werden.
5. Nur Zeilen mit identischem oder leerem Eintrag bei "type" können zusammengefasst werden.

6. An dieser Stelle ist nun eine Prüfung erforderlich, die unterschiedliches weiteres vorgehen erforderlich macht, abhängig davon, ob der Eintrag in der Spalte „type“ die Werte „deposit“, „withdraw“ oder „order“ enthält. Nur gleichartige Einträge können eventuell zusammengefasst werden.

Fangen wir mal mit deposit an. Ich versuche es dann gern für die anderen beiden Typen in Eigenregie hinzubekommen. Withdraw wird nicht so schwer werden (hoffe ich) und für "Order" werde ich dann wohl noch mal fragen müssen.

Die Prüfung geht bei „deposit“ so von statten:
6 a) Bei „deposit“ können nur Spalten zusammengefasst werden in denen der Eintrag in „boughtCurrencyId“ identisch ist.
6 b) Wenn die Einträge identisch sind, muss geprüft werden, ob die Einträge in „feeCurrencyId“ identisch sind.
6 c) Wenn sie identisch sind, dann können sie zusammengefasst werden.
6 d) Wenn sie unterschiedlich sind, so können sie nur zusammengefasst werden, wenn die Einträge der anderen Zeilen bei "feeQuantity" leer sind sind.
6 e) Die Felder „soldQuantity“ „soldCurrency” “soldCurrencyId” müssen leer sein, sonst können die Einträge nicht zusammengefasst werden.

Zeilen sollen bei „deposit“ folgendermaßen zusammengefasst:
Als Uhrzeit in der Spalte „TimeExecuted“ soll die späteste der zusammengefassten Transaktionen übernommen werden.
Hier gibt es die Besonderheit, dass bei Transaktionen am letzten Tag eines Monats die Transaktionen nur bis 23:59:59 zusammengefasst werden sollen.
In der Spalte „type“ bleibt es (logischerweise) bei „deposit“
Die Werte aller zusammengefassten Zeilen aus der Spalte „boughtQuantity“ werden addiert.

Falls es Einträge bei den Fees gibt, so sind die Werte aus „feeQuantity“ zu addieren.
Die Werte aus „feeCurrency“ sind ebenfalls zu übernehmen. Falls eine Transaktion mit einem leeren Wert bei „feeCurrency“ und „feeCurrencyId“ ebenfalls zusammengefasst wird, so verbleibt es bei dem nicht leeren Eintrag.

Die Werte aus allen zusammengefassten Zeilen der Spalte „txId“ sollen in die zusammengefasste Transaktion übernommen werden.
Dazu sollen die Einträge beginnend von der ersten bis zur letzten durch ein „+“ getrennt in die neue und zusammengefasste Transaktion übernommen werden.
Wenn die „txId“ von 3 zusammengefassten Transaktionen also 1, 2 und 3 lautet, so soll in der zussamengefassten Transaktion der Eintrag im Feld „txId“ „1 + 2 + 3“ lauten.
Als Wert für das Feld „Id“ soll der Eintrag der letzten zusammengefassten Transaktion übernommen werden.
Das Feld „comments“ der zusammengefassten Tranaktion soll so befüllt werden:
<Anzahl der zusammengefassten Transaktionen> „Transaktionen“ von <Zeitstempel der frühsten in dieser Zusammenfassung enthaltenen Transaktion einsetzen> bis < Zeitstempel der frühsten in dieser Zusammenfassung enthaltenen Transaktion einsetzen> zusammengefasst. ID: <Einträge der Felder aus der Spalte „Id“ für alle zusammengefassten Transaktionen, getrennt durch ein „+“ einfügen>
Danach sollen eingefügt werden </ ursprüngliche Kommentare> und dahinter alle in den Feldern „comments“ vorhandenen Einträge der zusammengefassten Transaktionen übernommen werden.

Ein vollständiger Eintrag im Feld „comments“ könnte so aussehen:
4 Transaktionen von 01.01.2020 00:09:12 bis 01.01.2020 07:06:14 zusammengefasst. ID: 1 + 2 + 3 + 4 / ursprüngliche Kommentare: Hallo + das + ist + ein + Test

Das beendet die Vorgehensweise für den Typ „deposit“

Mit ChatGPTs Hilfe habe ich folgenden Code für den Bereich "Order" versucht hinzubekommen.

Code: Alles auswählen

import pandas as pd

# Daten einlesen
df = pd.read_excel('testdatei.xlsx', engine='openpyxl')

# Schritt 1: Zeilen mit "Wahr" in "isIgnored" entfernen (Hier soll aber nicht entfernt sondern nur ignoriert werden!)
df = df[df['isIgnored'] != "Wahr"]

# Funktion zum Zusammenfassen von Gruppen von Transaktionen
def summarize_group(group):
    if len(group) == 1:
        return group

    first, last = group.iloc[0], group.iloc[-1].copy()

    # Anpassung der relevanten Spalten
    last['comments'] = f"Transaktionen von {first['timeExecuted']} bis {last['timeExecuted']} (insgesamt {len(group)} zusammengefasst)"
    last['comments'] += '+' + '+'.join(group['comments'].dropna())
    last['id'] = '+'.join(group['id'].astype(str))
    last['txId'] = '+'.join(group['txId'].dropna())

    return pd.DataFrame([last])

# Bestimme die Schlüssel für die Gruppierung
group_keys = df['type'].ne(df['type'].shift()).cumsum()
if 'order' in df['type'].values:
    group_keys += (df['area'] + df['boughtCurrency'] + df['soldCurrency']).ne((df['location'] + df['boughtCurrency'] + df['soldCurrency']).shift()).cumsum()

result = df.groupby(group_keys).apply(summarize_group).reset_index(drop=True)

# Speichern des bereinigten DataFrames
result.to_excel('bereinigte_testdatei.xlsx', index=False)
Ich freue mich über jeden Input und wünsche euch einen schönen Sonntag.
Benutzeravatar
grubenfox
User
Beiträge: 432
Registriert: Freitag 2. Dezember 2022, 15:49

Glyxbringer hat geschrieben: Sonntag 8. Oktober 2023, 17:36 Die originale Excel-Datei hat mittlerweile mehr als 300.000 Einträge ist ca. 40MB groß und dementsprechend sperrig zu handhaben.
"Excel ist keine Datenbank!" Sorry, konnte ich mir beim lesen nicht verkneifen... :wink: "Einträge" oder "Zeilen"? Falls Zeilen gemeint sind: :shock: Erstaunlich, soviel passen da mittlerweile rein?

Zu Pandas müssen die anderen was zu sagen, die kennen sich da aus. Aber ohne den Code um diese Zeit noch lesen geschweige denn verstehen zu wollen: ich vermute mal das wäre besser eine Aufgabe für eine Datenbank (z.B. 'sqlite' um mal ganz klein anzufangen)
Glyxbringer
User
Beiträge: 4
Registriert: Samstag 7. Oktober 2023, 11:33

Guten Morgen,

zunächst Danke, dass Du Dir um diese Uhrzeit noch die Zeit genommen hast, mir zu antworten.

Ich weiß, dass Excel keine Datenbank ist. Ich habe es auch schon mit SQLite mit DBBrowser, Access, Power BI Desktop und noch ein paar anderen Tools (an die ich mich so früh morgens nicht erinnere) versucht. Bei all diesen Versuchen hatte ich allerdings bei den Auswertungen (und meistens schon beim Import der Daten) erheblich mehr Probleme, als beim guten alten Excel. Da es hier um ein Hobby geht, bin ich zwar bereit mich ein paar Tage am Wochenende in Vollzeit mit dem Problem zu beschäftigen, kann mir aber zeitlich bedingt nicht vorstellen eine Programmiersprache dafür zu erlernen. Dafür bin ich auch leider zu unstrukturiert.
Ich habe gerade noch mal geschaut: Die Datei hat 94.516 Zeilen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Du willst keine Programmiersprache lernen?
Dann bist Du falsch hier. Excel bietet mit Pivot-Tabellen einige Möglichkeiten der Filterung und Gruppierung.
Glyxbringer
User
Beiträge: 4
Registriert: Samstag 7. Oktober 2023, 11:33

Ich kann mich nicht erinnern, dass ich geschrieben habe, dass ich keine Programmiersprache lernen will.
Ich habe leider nicht die Zeit eine Programmiersprache from scratch zu lernen. (Da wird mir - wie ich oben geschrieben habe - die Zeit an den Wochenenden nicht reichen) und dann kann ich wahrscheinlich auch eher die 94k Zeilen manuell zusammenfassen und bin schneller fertig.

Daher hatte ich gehofft hier ein paar Denkanstöße zu bekommen, die ich dann benutzen kann, um damit rumzubasteln.
Wenn ich dann hier falsch bin: Kein Thema, dann ziehe ich weiter und werde irgendwann schon auf ein paar nützliche Hinweise stoßen.
Das was ChatGPT da fabriziert hat, ist ja schon einigermaßen brauchbar.

Danke für die Idee mit Excel und den Pivot-Tabellen. Damit lässt sich mein Problem allerdings nicht lösen.
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Glyxbringer: Nach dem was Du schreibst hast Du da schon eine Menge Zeit für aufgewendet. Dann hast Du auch die Zeit eine Programmiersprache zu lernen. Ist eine Frage der Prioritäten die Du setzt.

Der Denkanstoss wenn Du etwas mit Werkzeug X lösen willst, ist zu lernen wie man mit Werkzeug X arbeitet. Bei einer Programmiersprache wäre das dann programmieren lernen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Glyxbringer
User
Beiträge: 4
Registriert: Samstag 7. Oktober 2023, 11:33

Ich bin dabei es zu lernen und versuche mein bestes...
Seit Tagen komme ich allerdings nicht mehr weiter.

Der bislang entwickelte Code sieht so aus:

Code: Alles auswählen

# Importieren der benötigten Bibliotheken
import pandas as pd
import sys

# Definition einer Funktion zur Anzeige eines Fortschrittsbalkens im Terminal
def print_progress_bar(iteration, total, bar_length=50):
    progress = iteration / total  # Berechnung des Fortschritts als Verhältnis
    arrow = '-' * int(round(progress * bar_length) - 1) + '>'  # Erstellung des Pfeils für den Balken
    spaces = ' ' * (bar_length - len(arrow))  # Erstellung der Leerzeichen für den Balken
    sys.stdout.write(f'\rFortschritt: [{arrow + spaces}] {progress * 100:.2f}%')  # Ausgabe des Fortschrittsbalkens
    sys.stdout.flush()  # Stellt sicher, dass der Fortschrittsbalken sofort angezeigt wird


# Einlesen der Daten aus einer Excel-Datei
df = pd.read_excel('testdatei.xlsx', engine='openpyxl')
print("Daten eingelesen.")
print(f"Die Excel-Tabelle hat {df.shape[0]} Zeilen.")
#print("Spalten im DataFrame:", df.columns)

initial_withdraw_count = (df['type'] == 'withdraw').sum()
print("Anzahl der 'withdraw'-Transaktionen vor der Verarbeitung:", initial_withdraw_count)
initial_order_count = (df['type'] == 'order').sum()
print("Anzahl der 'order'-Transaktionen vor der Verarbeitung:", initial_order_count)
initial_deposit_count = (df['type'] == 'deposit').sum()
print("Anzahl der 'deposit'-Transaktionen vor der Verarbeitung:", initial_deposit_count)

# Aufteilung des DataFrames in zwei Teile: ignorierte und zu verarbeitende Transaktionen
ignored_df = df[df['isIgnored'] == True].copy()  # Transaktionen, die ignoriert werden sollen
processing_df = df[df['isIgnored'] == False].copy()  # Transaktionen, die verarbeitet werden sollen
print(f"Anzahl der Zeilen im ignored DataFrame: {ignored_df.shape[0]}")
print(f"Anzahl der Zeilen im processing DataFrame: {processing_df.shape[0]}")

processing_withdraw_count = (processing_df['type'] == 'withdraw').sum()
ignored_withdraw_count = (ignored_df['type'] == 'withdraw').sum()
print("Anzahl der 'withdraw'-Transaktionen im processing DataFrame:", processing_withdraw_count)
print("Anzahl der 'withdraw'-Transaktionen im ignored DataFrame:", ignored_withdraw_count)
processing_order_count = (processing_df['type'] == 'order').sum()
ignored_order_count = (ignored_df['type'] == 'order').sum()
print("Anzahl der 'order'-Transaktionen im processing DataFrame:", processing_order_count)
print("Anzahl der 'order'-Transaktionen im ignored DataFrame:", ignored_order_count)
processing_deposit_count = (processing_df['type'] == 'deposit').sum()
ignored_deposit_count = (ignored_df['type'] == 'deposit').sum()
print("Anzahl der 'deposit'-Transaktionen im processing DataFrame:", processing_deposit_count)
print("Anzahl der 'deposit'-Transaktionen im ignored DataFrame:", ignored_deposit_count)


# Definition einer Funktion zur Zusammenfassung von Transaktionen in einer Gruppe
def summarize_group(group):
    if len(group) == 1:  # Wenn die Gruppe nur eine Transaktion enthält, wird sie unverändert zurückgegeben
        return group
    
    # Überprüfen, ob alle Transaktionen in der Gruppe den Typ "order" haben
    if all(group['type'] == 'order'):
        aggregated = group.iloc[-1].copy()  # Kopie der letzten Transaktion der Gruppe für die Zusammenfassung
        start_time = group.iloc[0]['timeExecuted'].strftime('%Y-%m-%d %H:%M:%S')  # Startzeit der Gruppe
        end_time = group.iloc[-1]['timeExecuted'].strftime('%Y-%m-%d %H:%M:%S')  # Endzeit der Gruppe
        
        # Hinzufügen eines Kommentars zur zusammengefassten Transaktion
        aggregated['comments'] = f"{len(group)} TX merged ({start_time}-{end_time})"
        aggregated['comments'] += f" [OrigIDs: {'+'.join(group['id'].astype(str))}]"
        
        # Zusammenfassung der Transaktions-IDs
        aggregated['txId'] = '+'.join(group['txId'].dropna().astype(str))
        
        # Berechnung der Summen für verschiedene Felder
        aggregated['fiatValueOverwrite'] = group['fiatValueOverwrite'].sum()
        aggregated['feeFiatValueOverwrite'] = group.groupby('feeCurrency')['feeFiatValueOverwrite'].sum().sum()
        aggregated['boughtQuantity'] = group['boughtQuantity'].astype(str).str.replace(',', '.').astype(float).sum()
        aggregated['soldQuantity'] = group['soldQuantity'].astype(str).str.replace(',', '.').astype(float).sum()
    
        return pd.DataFrame([aggregated])  # Rückgabe der zusammengefassten Transaktion als DataFrame
    else:
        return group  # Wenn nicht alle Transaktionen den Typ "order" haben, gib die Gruppe unverändert zurück


# Definition einer Funktion zur Konsolidierung von Transaktionsblöcken
def consolidate_blocks(df):
    result = pd.DataFrame()  # Initialisierung eines leeren DataFrames für das Ergebnis
    current_block = pd.DataFrame()  # Initialisierung eines leeren DataFrames für den aktuellen Block

    for index, row in df.iterrows():
        if row['type'] == 'order':
            current_block = pd.concat([current_block, row.to_frame().T])
        else:
            if not current_block.empty:
                summarized = summarize_group(current_block)
                result = pd.concat([result, summarized])
                current_block = pd.DataFrame()
            result = pd.concat([result, row.to_frame().T])

    if not current_block.empty:
        summarized = summarize_group(current_block)
        result = pd.concat([result, summarized])

    return result.reset_index(drop=True)



print("Verarbeite nach boughtCurrency und order Gruppen...")
result = consolidate_blocks(processing_df)  # Durchführung der Konsolidierung

final_withdraw_count = (result['type'] == 'withdraw').sum()
print("Anzahl der 'withdraw'-Transaktionen nach der Verarbeitung:", final_withdraw_count)
final_order_count = (result['type'] == 'order').sum()
print("Anzahl der 'order'-Transaktionen nach der Verarbeitung:", final_order_count)
final_deposit_count = (result['type'] == 'deposit').sum()
print("Anzahl der 'deposit'-Transaktionen nach der Verarbeitung:", final_deposit_count)


# Zusammenführen der ignorierten und verarbeiteten Transaktionen und Speichern in einer Excel-Datei
final_result = pd.concat([result, ignored_df])  # Zusammenführen der DataFrames
final_result.to_excel('bereinigte_testdatei_bought.xlsx', index=False)  # Speichern des Ergebnisses

print("\nVerarbeitung abgeschlossen.")
print(f"Die bereinigte Excel-Tabelle hat nun {final_result.shape[0]} Zeilen.")  # Änderung hier
print("Daten gespeichert.")
Allerdings bin ich irgendwo falsch abgebogen und wenn ich nun einen Transaktionsblock habe der vereinfacht so aussieht:

stone gegen metal
stone gegen metal
gold gegen silver
gold geben silver
gold gegen silver

dann fliegen die stone gegen metal tx aus dem dataframe raus und es fasst nur die 3x gold gegen silver zusammen...

Hat jemand einen Tipp?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du mit ChatGPT alleine nicht weiter kommst, such dir jemanden auf upwork oder moonlight etc. Das sind Stunden an Arbeit das umzusetzen, die garantiert auch diverse Runden an Feedback benötigen. Das umsonst zu machen tut keiner, außer es gibt da ein persönliches Interesse dran.
juwido
User
Beiträge: 20
Registriert: Donnerstag 15. Dezember 2022, 13:41

Also wo es nur um eine Excel-Tabelle geht:
Die "Datenbank-Funktionen" sind schon gut brauchbar. Ich denke das man mit etwas mehr "Handarbeit" am Ende schneller und überschaubarer mit "Bordmitteln" ans Ziel kommt, als dafür extra ein Programm zu entwickeln.
Nur, egal wie man vorgeht, Sicherungskopie vor jedem Schritt nicht vergessen!
Antworten