In cmd Konsole die Zellen eines DF farbig gestalten?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
August1328
User
Beiträge: 65
Registriert: Samstag 27. Februar 2021, 12:18

Hallo zusammen,

über die Schnittstelle eines Brokers rufe ich live Daten einer Aktie ab, die in einem Dataframe gespeichert werden, neue Daten werden dem Datensatz hinzugefügt, ein paar Berechnungen gemacht und das Ganze wird in der command Konsole unter Windows ausgegeben.

Nun dachte ich mir, es wäre ja schick, wenn eine Zelle farbig wäre, z.B. wenn der Schlusskurs größer ist als der gleitende Durchschnitt, dann ist der Hintergrund grün, wenn er kleiner ist, ist er rot.

Habe gegoogelt, wie man das machen könnte und df.style gefunden, aber in der Konsole funktioniert das nicht.

Dann habe ich das mit colorama probiert, was ich ab und zu nutze. Also schnell ne Abfrage eingebaut, die über alle Zellen iteriert (Spalte 4 ist der Schlusskurs und in Spalte 10 ist der gleitende Durchschnitt, in den geschweiften Klammern ist die Farb-Vorgabe):

Code: Alles auswählen

if df_pro_min.iat[i , 4] >= df_pro_min.iat[i , 10]:
    df_pro_min.iat[i, 10] = f"{GREEN}{BLACK}{df_pro_min.iat[i , 10]}{ENDE}"
else:
    df_pro_min.iat[i, 10] = f"{RED}{BLACK}{df_pro_min.iat[i , 10]}{ENDE}"
Und siehe da, die Zellen werden gemäß der Bedingungen unterschiedlich farbig.

Nun das große aber... Irgendwie zerlegt diese Lösung die Darstellung der Tabelle. Ich habe das erst für eine Spalte probiert und mich gewundert, daß die Tabelle in der Konsole etwas komisch bzw. versetzt aussieht, als ich das für 2 weitere Spalten so gemacht habe, wurde die Tabelle unleserlich, die farbigen Spalten sind total versetzt zu den Überschriften. Wenn ich die Farb-Vorgaben in den geschweiften Klammern rausnehme, ist die Tabelle wieder ganz normal.

Kann da jemand weiter helfen? Was mache ich falsch bzw. warum mag pandas diese Ansi-Codes nicht? Oder kennt jemand ein anderes Modul für Farben in der Konsole, welches mit pandas harmoniert?

Grüße
Andy
Sirius3
User
Beiträge: 17793
Registriert: Sonntag 21. Oktober 2012, 17:20

Über ein Index iteriert, macht man etwas falsch. Die Farben kann man mit Pandas ganz einfach paar bedingten Mapping auf die ganze Spalte übertragen.
Zum Problem: woher soll Pandas wissen dass die komischen Zeichen in geschweiften Klammern Farbangaben für Colorama sind? Du kannst die Tabelle ja auch einfach selbst paar Schleife ausgeben.
August1328
User
Beiträge: 65
Registriert: Samstag 27. Februar 2021, 12:18

Hallo Sirius,

danke für Deine Antwort, ich habe vorher schon ein paar Sachen probiert, hätte ich hinzufügen sollen.

Ich habe mich zuerst an dieser Frage bei Stackoverflow orientiert https://stackoverflow.com/questions/687 ... -the-table. Der user wollte das gleiche wie ich (er hat auch das Problem das es mit den Farbangaben den Kopf der Tabelle zerschiesst, siehe das 1. Bild bzw "messes up the values", hab mir erst dabei nichts gedacht).

Ich hab diesen Code versucht auf meinen Fall umzuschreiben, also df.apply() benutzt. Das hat auf Anhieb wegen einem TypeError nicht geklappt.
Dann habe ich erst mal geschaut, ob es da überhaupt geht, also nur auf die 1 Spalte bezogen:

Code: Alles auswählen

df_pro_min['8EMA'] = df_pro_min['8EMA'].map(lambda x: GREEN + str(x) + ENDE)
Das funktioniert nur begrenzt, d.h. ab dem ersten farbigen Zeichen ist der Rest der Tabelle grün, d.h. das mapping auf 1 Spalte "akzeptiert" den Farb-Reset nicht. Und der Tabellen Kopf ist wieder verschoben.

Die Farbenangaben werden vorher so definiert:

Code: Alles auswählen

from colorama import Back, Style, Fore, just_fix_windows_console
MAGENTA, RED, BRIGHT, ENDE, YELLOW, BLACK, GREEN, WHITE, CYAN = Back.MAGENTA, Back.RED, Style.BRIGHT, Style.RESET_ALL, Back.YELLOW, Fore.BLACK, Back.GREEN, Back.WHITE, Back.CYAN
just_fix_windows_console()
Somit habe ich das per iterieren probiert. Die Farben pro Zelle passen, nur der Tabellen Kopf ist wieder so verschoben, daß die Tabelle schwer lesbar wird.

Ich nehme an, daß die ANSI-Codes die Ursache sind. Hat jemand ne Idee wie man das Verschieben des Headers vermeiden kann?

Das Ausgeben der Tabelle per Schleife teste ich heute abend mal.

Gruß
Andy
Benutzeravatar
__blackjack__
User
Beiträge: 13199
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@August1328: Ich würde das da nicht irgendwie reinbasteln, sondern eine Bibliothek verwenden die dafür gedacht ist. Zum Beispiel `rich`. Da gibt es speziell was für Tabellen.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
August1328
User
Beiträge: 65
Registriert: Samstag 27. Februar 2021, 12:18

Guten Abend,

ich habe nochmal ein wenig recherchiert, weil mich das gewurmt hat, daß das nicht geht und dabei bin auf eine dementsprechende ältere Diskussion auf der github Seite für pandas gestossen. Nicht uninteressant, deshalb hier der Link: https://github.com/pandas-dev/pandas/issues/18066

Ich hab dann noch eine Bastellösung probiert, d.h. die Kopfzeile mit der Hand zu schreiben, den df als string auszugeben (obwohl ich gelesen habe, daß print() eigentlich nichts anderes macht) und dabei die Kopfzeile, die es verschiebt, zu unterdrücken, also so:

Code: Alles auswählen

string_ausgabe = df_pro_min.iloc[-10:].to_string(header=False, index=True)
print(string_ausgabe)
Das funktioniert auch, bis es neue empfangene Daten in den bestehenden DF einfügt, dann zerlegt es den alten Teil. Zusätzlich ist plötzlich die update() Funktion blockiert, d.h. es lädt nur 1x Daten nach, dann nicht mehr - und damit werde ich diesen "Weg" nicht weiter gehen, es macht keinen Sinn Fehlersuche für ne arge Bastellösung zu betreiben, wie ihr ja auch angemerkt habt.

@__blackjack__: Danke für den Hinweis, das Modul rich kenne und nutze ich an anderer Stelle. In eine einfache Tabelle, die man neu aufbaut, Farbe reinzubringen, das habe ich schon gemacht. Aber in einer Tabelle aus einem dataframe einzelne Zellen farbig zu machen, dazu habe ich nichts gefunden, deshalb habe ich das noch nicht probiert. Hast Du evtl. nen Link oder kurzes Code Beispiel?

@Sirius: Was meinst Du genau mit die Tabelle per Schleife auszugeben? Nachdem der Weg mit colorama im dataframe nicht funktioniert und aufgrund Deines Hinweises, wollte ich mal probieren die letzten Zeilen des dataframes z.B. in ein Dictionary zu speichern, das dann schrittweise auszugeben und dabei z.B. die Werte der Schlüssel 'close' und 'EMA8' zu vergleichen und entsprechend farbig zu machen, mit rich oder colorama. Oder denke ich zu kompliziert?

Grüße
Andy
Benutzeravatar
snafu
User
Beiträge: 6748
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Du willst also eine farbige Ausgabe haben, aber ansonsten die Form der Darstellung deines DataFrames weiter so belassen? Da würde ich trotzdem die Umwandlung des Frames in eine ``rich``-Tabelle gehen. Da kann man ja viel konfigurieren und somit die "störenden" Linien weglassen. Falls dein farbiges ``df`` weiterhin bearbeitet werden soll, wäre das Überschreiben der ``__str__``-Methode innerhalb einer abgeleiteten ``DataFrame``-Klasse eine Option. Diese Methode würde dann ein angepasstes ``Table``-Objekt mit den Daten des Frames ausgeben. Somit führt ``print(df)`` zu einer farbigen Ausgabe, aber dein DataFrame an sich bleibt davon unberührt.

Und einzelne Zellen kann man in ``rich`` wohl über die ``Styled``-Klasse anpassen, wenn ich das richtig verstanden habe (ich hab's noch nie benutzt). Da sollte doch theoretisch etwas mit ``apply()`` auf dem DataFrame möglich sein. Also in einem kopierten ``df`` die Styles für die betreffenden Zellen setzen und dann alles als ``rich``-Tabelle ausgeben lassen. Irgendwie sowas.
August1328
User
Beiträge: 65
Registriert: Samstag 27. Februar 2021, 12:18

Ich habe das ein paar Tage liegen lassen und inzwischen eine recht simple Lösung per rich table gefunden.

Erst hatte ich snafu´s Antwort gedanklich durchgespielt, das übersteigt aber meine Fähigkeiten und dann stand ich vor dem Problem, daß die Daten in der rich Tabelle nur als string vorliegen, man somit keine Zahlenwerte vergleichen kann.

Also, was anderes überlegt... wenn die letzten Zeilen des dataframe z.B. als Json vorliegen, dann müsste man die einzelnen Zahlenwerte doch vergleichen können und anpassen können, so wie das manche, wie in den beiden Links oben und ich auch, direkt im dataframe probiert haben.

Mit folgenden Zeilen bekommt man, nach der Definition der rich Tabelle und den Spalten, ganz einfach farbige Zellen, abhängig von den Werten.

Die letzten Zeilen aus dem df ins Json Format ziehen

Code: Alles auswählen

json_data = loads(df.tail(10).to_json(orient='index'))
und dann einfach in die for Schleife vor der Defintion der Zeile ne kurze if / else Abfrage rein.

Ein Beispiel

Code: Alles auswählen

for index in json_data:
    if json_data[index]['close'] >= json_data[index]['20EMA']:
        json_data[index]['20EMA'] = f"[black on green]{json_data[index]['20EMA']}[/]"
    else:
        json_data[index]['20EMA'] = f"[black on red]{json_data[index]['20EMA']}[/]"

    table.add_row(str(f"{json_data[index]['close']:.2f}"), str(json_data[index]['20EMA'])
Habe mich gefragt, ob das "eleganter" geht, aber es erreicht was es soll, es funktioniert auch für mehrere unterschiedliche farbige Spalten und damit bin ich erst mal zufrieden.

Grüße,
Andy
Sirius3
User
Beiträge: 17793
Registriert: Sonntag 21. Oktober 2012, 17:20

Über einen Index iteriert man nicht, und Du hast bereits Strings, die `str`-Aufrufe sind also unnötig.

Code: Alles auswählen

for row in json_data.itertuples():
    color = "black on green" if row['close'] >= row['20EMA'] else "black on red"
    table.add_row(f"{row['close']:.2f}", f"[{color}]{row['20EMA']:.2f}[/]")
Wenn man aber schon Pandas hat, dann benutzt man `map` für die Farbe:

Code: Alles auswählen

json_data['color'] = (row['close'] >= row['20EMA']).map({True: "black on green", False: "black on red"})
for row in json_data.itertuples():
    table.add_row(f"{row['close']:.2f}", f"[{row.color}]{row['20EMA']:.2f}[/]")
Antworten