Du betrachtest gerade 13) Datenvisualisierung

13) Datenvisualisierung

  • Beitrags-Autor:Peter Kühn
  • Beitrags-Kategorie:chess_analyser
  • Beitrags-Kommentare:0 Kommentare

Die Datenbasis steht nun, so dass wir in diesem Kapitel die Visualisierung der Kennzahlen gemäß unserem Fahrplan in Angriff nehmen können.

Wir nutzen dazu wieder Jupyter-Notbooks. Allerdings benötigen wir jetzt statt der Programm-Bibliothek chess Pakete aus dem Statistik- und Grafik-Bereich wie pandas und mathplotlib. Außerdem verwenden wir wieder unsere mariadb-Bibliothek für den Zugriff auf unsere Datenbank mit den Kennzahlen.

Zur Auflockerung starten wir mit einer Auswertung zur Anzahl der insgesamt gespielten Partien aller bisherigen Schachweltmeister, indem wir folgenden Code in unser Jupyter-Notebook eingeben.

import sys
import getpass
import mariadb
import pandas as pd
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings('ignore')
    
def connect(p_password):
    global conn
    global cursor
    
    # Database connection details
    db_config = {
        "user": "chess_user",
        "password": p_password,
        "host": "localhost",
        "database": "chess",
        "port": 3306,  # Standard port for MariaDB
    }
    # Establishing the connection
    conn = mariadb.connect(**db_config)
    # Disable autocommit
    conn.autocommit = False
    # Create a cursor to execute queries
    cursor = conn.cursor()

def disconnect():
    cursor.close()
    conn.close()

def main():
    passwd = None
    # If password not provided, prompt securely
    if not passwd:
        try:
            passwd = getpass.getpass(prompt="Enter database password: ")
        except (KeyboardInterrupt, EOFError):
            print("\nPassword input cancelled.")
            sys.exit(1)

    # Validate password input
    if not passwd.strip():
        print("Error: Database password cannot be empty.")
        sys.exit(1)

    # connect to database
    try:
        connect(passwd)

        df = pd.read_sql_query("""select
	max(p.name) name,
	count(g.id) anzahl_spiele
from
	player p
join game g on
	(p.id = g.black_player_id
		or p.id = g.white_player_id )
where
	p.name in ('Staunton, Howard', 'Morphy, Paul', 'Steinitz, Wilhelm', 'Lasker, Emanuel', 'Capablanca, Jose', 'Alekhine, Alexander', 'Euwe, Max',
'Botvinnik, Mikhail URS', 'Smyslov, Vasily', 'Smyslov, V.', 'Tal, Mikhail', 'Petrosian, Tigran V', 'Spassky, Boris V.', 'Fischer, Robert J', 'Karpov, Anatoly',
'Kasparov, Garry', 'Kasparov, G.', 'Kramnik, Vladimir', 'Anand, Viswanathan', 'Carlsen, Magnus', 'Ding, Liren', 'Gukesh D')
group by
	case when p.name in ('Kasparov, Garry', 'Kasparov, G.') then 'Kasparov, Garry'
		 when p.name in ('Smyslov, Vasily', 'Smyslov, V.') then 'Smyslov, Vasily'
		 else p.name
	end
order by
	p.name""", conn)
        print(df)

        disconnect()
    except Exception as e:
        print(e)
        try:
            disconnect()
        except:
            pass
        sys.exit(1)


if __name__ == "__main__":
    main()
    

Das geht auch noch etwas hübscher 😉

Ein Bild sagt mehr…

Wir benötigen dazu lediglich ein paar zusätzliche Codezeilen.

import sys
import getpass
import mariadb
import pandas as pd
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings('ignore')
    
def connect(p_password):
    global conn
    global cursor
    
    # Database connection details
    db_config = {
        "user": "chess_user",
        "password": p_password,
        "host": "localhost",
        "database": "chess",
        "port": 3306,  # Standard port for MariaDB
    }
    # Establishing the connection
    conn = mariadb.connect(**db_config)
    # Disable autocommit
    conn.autocommit = False
    # Create a cursor to execute queries
    cursor = conn.cursor()

def disconnect():
    cursor.close()
    conn.close()

def get_data():
        df = pd.read_sql_query("""select
        	max(p.name) 'Name',
        	count(g.id) 'Anzahl Spiele'
        from
        	player p
        join game g on
        	(p.id = g.black_player_id
        		or p.id = g.white_player_id )
        where
        	p.name in ('Staunton, Howard', 'Morphy, Paul', 'Steinitz, Wilhelm', 'Lasker, Emanuel', 'Capablanca, Jose', 'Alekhine, Alexander', 'Euwe, Max',
        'Botvinnik, Mikhail URS', 'Smyslov, Vasily', 'Smyslov, V.', 'Tal, Mikhail', 'Petrosian, Tigran V', 'Spassky, Boris V.', 'Fischer, Robert J', 'Karpov, Anatoly',
        'Kasparov, Garry', 'Kasparov, G.', 'Kramnik, Vladimir', 'Anand, Viswanathan', 'Carlsen, Magnus', 'Ding, Liren', 'Gukesh D')
        group by
        	case when p.name in ('Kasparov, Garry', 'Kasparov, G.') then 'Kasparov, Garry'
        		 when p.name in ('Smyslov, Vasily', 'Smyslov, V.') then 'Smyslov, Vasily'
        		 else p.name
        	end
        order by
        	p.name""", conn)
        return(df)
def visualize_data(p_df):   
    barplot = plt.bar(p_df['Name'], p_df['Anzahl Spiele'], color='skyblue')
    plt.xlabel("Name")
    plt.ylabel("Anzahl Spiele")
    plt.ylim([0,6000])
    plt.title("Anzahl Partien der Schachweltmeister")
    plt.bar_label(container=barplot, rotation = 90, label_type = "edge", padding=5)
    plt.xticks(rotation=90)
    plt.show()
    
def main():
    passwd = None
    # If password not provided, prompt securely
    if not passwd:
        try:
            passwd = getpass.getpass(prompt="Enter database password: ")
        except (KeyboardInterrupt, EOFError):
            print("\nPassword input cancelled.")
            sys.exit(1)

    # Validate password input
    if not passwd.strip():
        print("Error: Database password cannot be empty.")
        sys.exit(1)

    # connect to database
    try:
        connect(passwd)
        df = get_data()
        visualize_data(df)
        disconnect()
    except Exception as e:
        print(e)
        try:
            disconnect()
        except:
            pass
        sys.exit(1)


if __name__ == "__main__":
    main()

Die Grafik kann sich schon sehen lassen, doch wir wollen mehr wissen. Vor allem sind wir an Ergebnissen interessiert und daher wollen wir auch die Anteile von Gewinn-, Verlust- und Remispartien sehen – hold my beer!

Das Schöne an unserer Konfiguration mit einer separaten Datenbank ist, dass wir uns die benötigten Daten immer leicht mit SQL-Abfragen beschaffen können, ohne eine Zeile Python-Code programmieren zu müssen🤗

So auch hier – wir müssen im Vergleich zur vorigen Auswertung lediglich noch die entsprechend aufgeschlüsselten Summen dazu ermitteln.

select
	p.name Name,
	count(p.name) "Anzahl Spiele",
	sum(case when (p.id = g.white_player_id and g.result = 1) or (p.id = g.black_player_id and g.result = 2) then 1 else 0 end) "Anzahl Gewinn",
	sum(case when g.result = 3 then 1 else 0 end) "Anzahl Remis",
	sum(case when (p.id = g.white_player_id and g.result = 2) or (p.id = g.black_player_id and g.result = 1) then 1 else 0 end) "Anzahl Verlust",
	sum(case when g.result = 4 then 1 else 0 end) "Anzahl mit unbekanntem Ergebnis"
from
	player p
join game g on
	(p.id = g.black_player_id
		or p.id = g.white_player_id )
where
	p.name in ('Staunton, Howard', 'Morphy, Paul', 'Steinitz, Wilhelm', 'Lasker, Emanuel', 'Capablanca, Jose', 'Alekhine, Alexander', 'Euwe, Max',
'Botvinnik, Mikhail URS', 'Smyslov, Vasily', 'Smyslov, V.', 'Tal, Mikhail', 'Petrosian, Tigran V', 'Spassky, Boris V.', 'Fischer, Robert J', 'Karpov, Anatoly',
'Kasparov, Garry', 'Kasparov, G.', 'Kramnik, Vladimir', 'Anand, Viswanathan', 'Carlsen, Magnus', 'Ding, Liren', 'Gukesh D')
group by
	case when p.name in ('Kasparov, Garry', 'Kasparov, G.') then 'Kasparov, Garry'
		 when p.name in ('Smyslov, Vasily', 'Smyslov, V.') then 'Smyslov, Vasily'
		 else p.name
	end
order by
	p.name;
die Auswertung nach Ergebnissen

Wir sehen, dass alle Partien ein reguläres Ende genommen haben, so dass wir für unsere grafische Präsentation die letzte Spalte mit lauter Nullen bei der Verwendung in unserem Jupyter Notebook wieder entfernen können.

Wir ersetzen daher das SQL in der Funktion get_data() unseres Notebooks und speichern dieses unter einem neuen Namen. Außerdem legen wir noch eine Konfigurationsdatei an, um dort unser Datenbank-Passwort zu speichern. Damit sparen wir uns die Tipparbeit bei jedem Programmstart und lesen es stattdessen unter Benutzung der load_dotenv-Bibliothek aus der Datei .env ein.

Etwas mehr Aufwand bereitet lediglich die Anpassung der Funktion visualize_data(), da wir die relativ simple Balkengrafik oben nun durch eine gestalpelte Balkengrafik ersetzen werden, die nicht nur mit der Anzahl Spiele, sondern auch mit den Werten für die Ergebnis-Anteile für jeden gestapelten Balken beschriftet sein soll. Außerdem wollen wir sinnvolle Farben für diese Anteile nutzen sowie eine Legende zur Übersicht.

import sys
import os
import getpass
import mariadb
import pandas as pd
import matplotlib.pyplot as plt
import warnings
from dotenv import load_dotenv

warnings.filterwarnings('ignore')
    
def connect(p_password):
    global conn
    global cursor
    
    # Database connection details
    db_config = {
        "user": "chess_user",
        "password": p_password,
        "host": "localhost",
        "database": "chess",
        "port": 3306,  # Standard port for MariaDB
    }
    # Establishing the connection
    conn = mariadb.connect(**db_config)
    # Disable autocommit
    conn.autocommit = False
    # Create a cursor to execute queries
    cursor = conn.cursor()

def disconnect():
    cursor.close()
    conn.close()

def get_data():
        df = pd.read_sql_query("""select
        	p.name Spieler,
        	cast(sum(case when (p.id = g.white_player_id and g.result = 1) or (p.id = g.black_player_id and g.result = 2) then 1 else 0 end) as unsigned) "Anzahl Gewinn",
        	cast(sum(case when g.result = 3 then 1 else 0 end) as unsigned) "Anzahl Remis",
        	cast(sum(case when (p.id = g.white_player_id and g.result = 2) or (p.id = g.black_player_id and g.result = 1) then 1 else 0 end) as unsigned) "Anzahl Verlust"
        from
        	player p
        join game g on
        	(p.id = g.black_player_id
        		or p.id = g.white_player_id )
        where
        	p.name in ('Staunton, Howard', 'Morphy, Paul', 'Steinitz, Wilhelm', 'Lasker, Emanuel', 'Capablanca, Jose', 'Alekhine, Alexander', 'Euwe, Max',
        'Botvinnik, Mikhail URS', 'Smyslov, Vasily', 'Smyslov, V.', 'Tal, Mikhail', 'Petrosian, Tigran V', 'Spassky, Boris V.', 'Fischer, Robert J', 'Karpov, Anatoly',
        'Kasparov, Garry', 'Kasparov, G.', 'Kramnik, Vladimir', 'Anand, Viswanathan', 'Carlsen, Magnus', 'Ding, Liren', 'Gukesh D')
        group by
        	case when p.name in ('Kasparov, Garry', 'Kasparov, G.') then 'Kasparov, Garry'
        		 when p.name in ('Smyslov, Vasily', 'Smyslov, V.') then 'Smyslov, Vasily'
        		 else p.name
        	end
        order by
        	p.name,
        	g.result""", conn)
        return(df)

def visualize_data(p_df): 
    color=['green', 'orange', 'red']
    ax=p_df.plot(x='Spieler', kind='bar', width=.8, stacked=True, figsize=(20,18), color=color, ylabel="Anzahl Partien", title="Anzahl Partien der Schachweltmeister nach Ergebnis")
    plt.bar_label(ax.containers[-1], rotation = 0, label_type = "edge", padding=6)
    plt.bar_label(ax.containers[0], labels=p_df['Anzahl Gewinn'], rotation = 0, label_type = "center")
    plt.bar_label(ax.containers[1], labels=p_df['Anzahl Remis'], rotation = 0, label_type = "center")
    plt.bar_label(ax.containers[2], labels=p_df['Anzahl Verlust'], rotation = 0, label_type = "center")
    plt.ylim([0,4300])
    plt.show()

    
def main():
    # try to get passwd from .env file
    load_dotenv()
    passwd = os.getenv("passwd")
    
    # If password not provided, prompt securely
    if not passwd:
        try:
            passwd = getpass.getpass(prompt="Enter database password: ")
        except (KeyboardInterrupt, EOFError):
            print("\nPassword input cancelled.")
            sys.exit(1)

    # Validate password input
    if not passwd.strip():
        print("Error: Database password cannot be empty.")
        sys.exit(1)

    # connect to database
    try:
        connect(passwd)
        df = get_data()
        visualize_data(df)
        disconnect()
    except Exception as e:
        print(e)
        try:
            disconnect()
        except:
            pass
        sys.exit(1)


if __name__ == "__main__":
    main()
Anzahl Partien nach ihren Ergebnissen

Schreibe einen Kommentar