HeatMap Probleme sobald NaN durch 0 ersetzt wird!

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
pi-suer
User
Beiträge: 1
Registriert: Montag 10. Juli 2023, 14:37

Hallo zusammen,

ich habe folgendes Problem. Ich bin derzeit dabei einen bestimmten Datensatz zu analysieren und ihn bereinigen. Es geht um folgender Datensatz: https://www.kaggle.com/datasets/rajkuma ... e=download

Ich rufe HeatMap vorher auf, also bevor ich die NaN Werte durch einen geeigneten Wert ersetzt habe und einmal danach, damit ich den Unterschied sehe. Es gibt eine Spalte, die Probleme macht bzw. meine HeatMap durcheinander bringt. Nur diese Spalte macht Probleme. Wenn ich die NaN Werte andere Spalten durch einen geeigneten Wert ersetze, dann wird alles wunderbar grafisch dargestellt. Wenn ich nun die NaN Werte der Spalte
worldPercentage durch 0 ersetze, dann zeigt meine HeatMap die 0 Werte als undefinierte Daten!?! Die anderen Spalten haben auch 0 Werte und hat meine HeatMap auch keine Probleme gemacht. Sobald ich

Code: Alles auswählen

self.df['worldPercentage'].fillna(0, inplace=True)
aufrufe, dann zeigt meine HeatMap auf einmal viele rot markierte Daten, die nicht definiert sind. Hier ist der Code:

Code: Alles auswählen

#..............................................................................      

    def detect_outliers(self):
        
        '''
            Methode: detect_outliers
            Diese Methode identifiziert numerische Ausreißer in jeder Spalte. 
            Die Ausreißer sind Werte, die 1,5 Interquartilsabstände unter dem 
            ersten Quartil (Q1) oder über dem dritten Quartil (Q3) liegen.
        '''
        
        numeric_columns = self.df.select_dtypes(include=['float64', 'int64']).columns
        outliers = pd.DataFrame(index=self.df.index, columns=self.df.columns)
        
        for column in numeric_columns:
            data = self.df[column]
            q1 = data.quantile(0.25)
            q3 = data.quantile(0.75)
            iqr = q3 - q1
            lower_bound = q1 - 1.5 * iqr
            upper_bound = q3 + 1.5 * iqr
            column_outliers = (data < lower_bound) | (data > upper_bound)
            outliers.loc[column_outliers, column] = True
        
        return outliers
    
#..............................................................................
    
    def detect_inconsistent_data(self):
        
        '''
            Methode: detect_inconsistent_data
            Diese Methode überprüft und bereinigt inkonsistente Daten in einem DataFrame. 
            Zuerst identifiziert sie doppelte Zeilen. Dann erfasst sie alle Spalten, die Text enthalten. 
            Aus dieser Liste von Textspalten werden die Spalten 'country', 'cca2' und 'cca3' ausgenommen, 
            da ihre Werte in Großbuchstaben beibehalten werden sollen. Alle anderen Textspalten werden in 
            Kleinbuchstaben umgewandelt, um die Konsistenz zu gewährleisten. Schließlich erstellt sie ein 
            neues DataFrame, das die Positionen von inkonsistenten Daten markiert (in diesem Fall doppelte Zeilen).
        '''
        
        # Duplizierte Zeilen
        duplicated_rows = self.df[self.df.duplicated(keep=False)]
        
        # Inkonsistente Groß-/Kleinschreibung in Zeichenspalten
        string_columns = self.df.select_dtypes(include=['object']).columns
        
        # Liste der Spalten, die in Kleinbuchstaben umgewandelt werden sollen
        lowercase_columns = [col for col in string_columns if col not in ['country', 'cca2', 'cca3']]
        
        # Nur die Spalten in 'lowercase_columns' in Kleinbuchstaben ändern
        for column in lowercase_columns:
            self.df[column] = self.df[column].str.lower()
        
        # Inkonsistente Zeilen markieren
        inconsistent_data = pd.DataFrame(index=self.df.index, columns=self.df.columns)
        inconsistent_data.loc[duplicated_rows.index, :] = True
        
        return inconsistent_data
     
#..............................................................................

    def detect_data_issues_heatmap(self, pFile):
        
        '''
        Methode: detect_data_issues_heatmap
        Parameter: pFile -> Der Parameter "pFile" gibt den Dateinamen an.
        
        Diese Methode detect_data_issues_heatmap erstellt eine Heatmap zur Identifizierung von Datenproblemen. 
        Sie analysiert die Eingabedaten und kennzeichnet Normaldaten, Ausreißer, inkonsistente Daten, fehlende Daten 
        und undefinierte Daten (NaN-Werte).
        
        Hier sind die Steps:
        
        1. Zunächst identifiziert sie fehlende Daten (also Datenpunkte, die im Dataframe als `NaN` dargestellt werden) 
        und erstellt eine Maske dieser Daten, die 'True' für fehlende Daten und 'False' für andere Werte darstellt. 
        Diese Maske wird dann in numerische Form konvertiert (1 für fehlende Daten, 0 für andere) und fehlende Daten werden 
        als '4' gekennzeichnet.
        
        2. Als nächstes wird die `detect_outliers`-Methode aufgerufen, um Ausreißer in den Daten zu identifizieren. 
        Ausreißer sind Datenpunkte, die signifikant von anderen Datenpunkten abweichen und könnten auf Datenqualitätsprobleme hinweisen. 
        Wir nehmen jedoch nur die Ausreißer, die keine fehlenden Daten (keine NaN-Werte) sind. 
        Eine Maske wird erstellt und numerisch konvertiert, wobei Ausreißer als '1' gekennzeichnet werden.
        
        3. Ähnlich wird die `detect_inconsistent_data`-Methode aufgerufen, um inkonsistente Daten zu identifizieren. 
        Inkonsistenz kann in vielen Formen auftreten, z.B. wenn ein Datum in der falschen Reihenfolge ist, 
        oder wenn eine Kategorie, die nur bestimmte Werte annehmen sollte, einen unbekannten Wert hat. 
        Auch hier nehmen wir nur die inkonsistenten Daten, die keine fehlenden Daten (keine NaN-Werte) sind.
        Eine Maske wird erstellt und numerisch konvertiert, wobei inkonsistente Daten als '2' gekennzeichnet werden.
        
        4. Schließlich werden alle Masken zu einer einzigen Maske addiert. 
        Dies gibt uns eine einzige Matrix, in der jeder Datenpunkt einen von vier Werten haben kann: 
        0 für Normaldaten, 1 für Ausreißer, 2 für inkonsistente Daten, 3 für fehlende Daten und 4 für undefinierte Daten (NaN-Werte).
        
        5. Diese Matrix wird dann als Heatmap geplottet, wobei verschiedene Farben verwendet werden, um die verschiedenen Arten von 
        Datenproblemen darzustellen.
        
        Zusammengefasst ist diese Methode ein nützliches Werkzeug zur Identifizierung und Visualisierung von Datenproblemen, 
        und kann helfen, Probleme in den Daten frühzeitig zu erkennen und anzugehen.

        '''
        
        # Erkennung von Datenproblemen
        missing_data = self.df.isnull()
        outlier_data = self.detect_outliers()
        inconsistent_data = self.detect_inconsistent_data()
        
        # Umgang mit NaN-Werten: als fehlende Daten markieren
        missing_mask = missing_data.astype(float).fillna(0)
        
        # Ein eindeutiger Bezeichner für fehlende oder NaN-Daten zuweisen
        missing_or_nan = missing_mask.replace({1: 4})  
        
        # Erkennung von Ausreißern unter Ausschluss von NaN-Werten
        outlier_mask = outlier_data.where(~missing_data).astype(float).fillna(0)
        
        # Erkennung von inkonsistenten Daten unter Ausschluss von NaN-Werten
        inconsistent_mask = inconsistent_data.where(~missing_data).astype(float).fillna(0)
        
        # Alle Masken zusammenzählen
        data_issues_mask = outlier_mask + 2 * inconsistent_mask + 3 * missing_or_nan
    
        # Eine Heatmap erstellen
        plt.figure(figsize=(10, 8))
        cmap = sns.color_palette(['#000099', '#40E0D0', '#FFFF00', '#FFA500', '#FF0000'])  # blue, turquoise, yellow, orange, red
        sns.heatmap(data_issues_mask, cmap=cmap, cbar=False)
        
        # Eine Legende erstellen
        legend_labels = ['Normal data', 'Ausreißer', 'Inkonsistente Daten', 'Fehlende Daten', 'Undefinierte Daten']
        colors = ['#000099', '#40E0D0', '#FFFF00', '#FFA500', '#FF0000']
        patches = [plt.Rectangle((0,0),1,1, fc=color) for color in colors]
        plt.legend(patches, legend_labels, loc='upper right')
        
        plt.title('Heatmap der Datenprobleme')
        plt.xlabel('Spalten')
        plt.ylabel('Zeilen')
        
        if pFile.endswith('.jpg'):
            # Speichern der Heatmap als Bilddatei
            output_file = self.prepare_file_path(pFile)
            plt.savefig(output_file, format='jpg', dpi=300)
        else:
            raise Exception("Die Dateiendung ist nicht '.jpg'. Bitte geben Sie einen Dateinamen mit der Endung '.jpg' an.")

        plt.show()


#..............................................................................  


    def correct_nan_values_worldPercentage_col(self):
        
        """
            Methode: correct_nan_values_worldPercentage_col
            Diese Methode ersetzt fehlende Werte in der Spalte 'worldPercentage' durch 0.
            
            Ich habe mich für diesen Ansatz entschieden, da die Länder, die NaN-Werte in der 
            'worldPercentage'-Spalte haben, eine sehr kleine Bevölkerung haben und somit einen 
            sehr kleinen Anteil an der Weltbevölkerung. Obwohl diese Annahme eine Vereinfachung ist,
            sollte sie für viele Anwendungen ausreichend genau sein.           
        """
        # Ersetze den NaN Wert in "worldPercentage" mit 0
        self.df['worldPercentage'].fillna(0, inplace=True)
        
 
#..............................................................................  

    def correct_data(self, new_file=None):
        # Aufruf der Korrekturmethode
        #self.correct_nan_values_cca2_col()
        
               
        # Neu
        self.correct_nan_values_cca2_cca3_col("countries_codes_and_coordinates.csv")
        self.correct_nan_values_netChange_col()
        self.correct_nan_values_worldPercentage_col()
        
        
        # Speichern des korrigierten DataFrames in einer neuen CSV-Datei, falls ein Dateiname gegeben ist
        if new_file is not None:
            self.df.to_csv(new_file, index=False)
            
            
  helper = DM_Helper("countries-table.csv")
 helper.detect_data_issues_heatmap("step_1_heatmap.jpg")
 # in der Methode correct_data wird self.df['worldPercentage'].fillna(0, inplace=True) 
 helper.correct_data("clean_countries-table.csv") 

Warum funktioniert meine HeatMap nicht, wenn ich die NaN Werte in der Spalte worldPercentage durch 0 ersetze?

Vielen Dank im Voraus für die Antworten.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pi-suer: Die Klasse sieht komisch aus. Die hat einen eigenartigen Namen, und scheint ausser dem `df`-Attribut keinen Zustand zu haben. Also sieht das einfach nur nach einem Haufen *Funktionen* aus, die in eine Klasse gestopft wurden. Warum?

So komische Trennlinienkommentare macht man nicht. Man sieht ganz gut an der Einrückung wo eine neue Funktion anfängt.

Jede Dokumentation einer Methode (gilt auch für Funktionen) noch mal mit „Methode: methoden_name“ anzufangen ist Unsinn. Der Methodenname steht doch direkt darüber schon beim ``def`` und die Werkzeuge die Docstrings in externe Dokumentation umwandeln, holen auch genau daher den Namen. Ebenso macht es auch nicht viel Sinn jede Methodenbeschreibung mit „Diese Methode…“ anzufangen. Das sieht man ja auch and der ``def``-Zeile, dass/ob es eine Methode oder eine Funktion ist.

`detect_outliers()` ist komisch, dass das eine Maske mit Ausreissern erzeugt und dann diese Maske für die Zuweisung von `True` verwendet. Da kann man doch einfach die Maske selbst bereits als Spaltenwerte verwenden‽

`detect_inconsistent_data()` macht mehr als der Name suggereriert. Zudem sollte man vielleicht die duplizierten Zeilen erst detektieren *nachdem* man die Gross-/Kleinschreibung von Spalten angepasst hat, denn dadurch könnten doch weitere Doubletten entstehen‽ Auch ist das wieder komisch gelöst warum nicht direkt mit der Maske die `duplicated()` liefert, gearbeitet wird.

`pFile`? Was soll das `p` bedeuten? Und das ist keine Datei, sondern ein Datei*name*. Wenn etwas Datei/`file` heisst, dann erwartet der Leser ein Datei-Objekt mit Methoden wie `read()` oder `write()` und `close()`.

Die Ausnahme würde man eher am Anfang der Funktion auslösen, bevor man die ganze Arbeit macht die am Ende umsonst ist, falls der Dateiname nicht den Vorgaben entspricht. Zudem sollte man nicht `Exception` auslösen, sondern etwas spezifischeres. Mindestens `ValueError`.

Code: Alles auswählen

import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt


def detect_outliers(df):
    """
    Diese Methode identifiziert numerische Ausreißer in jeder Spalte.

    Die Ausreißer sind Werte, die 1,5 Interquartilsabstände unter dem ersten
    Quartil (Q1) oder über dem dritten Quartil (Q3) liegen.
    """
    outliers = pd.DataFrame(index=df.index, columns=df.columns, dtype=bool)

    for column in df.select_dtypes(include=["float64", "int64"]).columns:
        data = df[column]
        q1 = data.quantile(0.25)
        q3 = data.quantile(0.75)
        iqr = q3 - q1
        lower_bound = q1 - 1.5 * iqr
        upper_bound = q3 + 1.5 * iqr
        outliers[column] = (data < lower_bound) | (data > upper_bound)

    return outliers


def detect_inconsistent_data(df):
    """
    Diese Methode überprüft und bereinigt inkonsistente Daten in einem
    DataFrame.

    Zuerst identifiziert sie doppelte Zeilen. Dann erfasst sie alle Spalten, die
    Text enthalten.

    Aus dieser Liste von Textspalten werden die Spalten "country", "cca2"
    und "cca3" ausgenommen, da ihre Werte in Großbuchstaben beibehalten werden
    sollen. Alle anderen Textspalten werden in Kleinbuchstaben umgewandelt, um
    die Konsistenz zu gewährleisten. Schließlich erstellt sie ein neues
    DataFrame, das die Positionen von inkonsistenten Daten markiert (in diesem
    Fall doppelte Zeilen).
    """
    #
    # XXX Sollte das nicht besser nach der Anpassung der Schreibweisen
    # passieren‽
    #
    duplicated_mask = df.duplicated(keep=False)

    columns_not_to_convert = {"country", "cca2", "cca3"}
    for column in (
        column
        for column in df.select_dtypes(include=["object"]).columns
        if column not in columns_not_to_convert
    ):
        df[column] = df[column].str.lower()

    return pd.DataFrame({column: duplicated_mask for column in df.columns})


def detect_data_issues_heatmap(df, image_filename):
    """
    Diese Methode erstellt eine Heatmap zur Identifizierung von Datenproblemen.

    Parameters
    ----------
    image_filename
        Gibt den Dateinamen an, unter dem die Heatmap als Bild gespeichert
        wird.

    Sie analysiert die Eingabedaten und kennzeichnet Normaldaten, Ausreißer,
    inkonsistente Daten, fehlende Daten und undefinierte Daten (NaN-Werte).

    Hier sind die Steps:

    1. Zunächst identifiziert sie fehlende Daten (also Datenpunkte, die im
       Dataframe als `NaN` dargestellt werden) und erstellt eine Maske dieser
       Daten, die 'True' für fehlende Daten und 'False' für andere Werte
       darstellt.

       Diese Maske wird dann in numerische Form konvertiert (1 für fehlende
       Daten, 0 für andere) und fehlende Daten werden als '4' gekennzeichnet.

    2. Als nächstes wird die `detect_outliers`-Methode aufgerufen, um Ausreißer
       in den Daten zu identifizieren.

       Ausreißer sind Datenpunkte, die signifikant von anderen Datenpunkten
       abweichen und könnten auf Datenqualitätsprobleme hinweisen.

       Wir nehmen jedoch nur die Ausreißer, die keine fehlenden Daten (keine
       NaN-Werte) sind.

       Eine Maske wird erstellt und numerisch konvertiert, wobei Ausreißer als
       '1' gekennzeichnet werden.

    3. Ähnlich wird die `detect_inconsistent_data`-Methode aufgerufen, um
       inkonsistente Daten zu identifizieren.

       Inkonsistenz kann in vielen Formen auftreten, z.B. wenn ein Datum in der
       falschen Reihenfolge ist, oder wenn eine Kategorie, die nur bestimmte
       Werte annehmen sollte, einen unbekannten Wert hat.

       Auch hier nehmen wir nur die inkonsistenten Daten, die keine fehlenden
       Daten (keine NaN-Werte) sind.

       Eine Maske wird erstellt und numerisch konvertiert, wobei inkonsistente
       Daten als '2' gekennzeichnet werden.

    4. Schließlich werden alle Masken zu einer einzigen Maske addiert. Dies gibt
       uns eine einzige Matrix, in der jeder Datenpunkt einen von vier Werten
       haben kann:

       0 für Normaldaten, 1 für Ausreißer, 2 für inkonsistente Daten, 3 für
       fehlende Daten und 4 für undefinierte Daten (NaN-Werte).

    5. Diese Matrix wird dann als Heatmap geplottet, wobei verschiedene Farben
       verwendet werden, um die verschiedenen Arten von Datenproblemen
       darzustellen.

    Zusammengefasst ist diese Methode ein nützliches Werkzeug zur
    Identifizierung und Visualisierung von Datenproblemen, und kann helfen,
    Probleme in den Daten frühzeitig zu erkennen und anzugehen.
    """
    filename_extension = ".jpg"
    if not image_filename.endswith(filename_extension):
        raise ValueError(
            f"Die Dateiendung ist nicht {filename_extension!r}."
            f" Bitte geben Sie einen Dateinamen mit der"
            f" Endung {filename_extension!r} an."
        )

    missing_data = df.isnull()
    outlier_data = detect_outliers(df)
    inconsistent_data = detect_inconsistent_data(df)

    # Umgang mit NaN-Werten: als fehlende Daten markieren
    missing_mask = missing_data.astype(float).fillna(0)

    # Ein eindeutiger Bezeichner für fehlende oder NaN-Daten zuweisen
    missing_or_nan = missing_mask.replace({1: 4})

    # Erkennung von Ausreißern unter Ausschluss von NaN-Werten
    outlier_mask = outlier_data.where(~missing_data).astype(float).fillna(0)

    # Erkennung von inkonsistenten Daten unter Ausschluss von NaN-Werten
    inconsistent_mask = (
        inconsistent_data.where(~missing_data).astype(float).fillna(0)
    )

    # Alle Masken zusammenzählen
    data_issues_mask = (
        outlier_mask + 2 * inconsistent_mask + 3 * missing_or_nan
    )
    # Eine Heatmap erstellen
    plt.figure(figsize=(10, 8))
    #
    # blue, turquoise, yellow, orange, red
    #
    cmap = sns.color_palette(
        ["#000099", "#40E0D0", "#FFFF00", "#FFA500", "#FF0000"]
    )
    sns.heatmap(data_issues_mask, cmap=cmap, cbar=False)

    plt.legend(
        [
            plt.Rectangle((0, 0), 1, 1, fc=color)
            for color in [
                "#000099",
                "#40E0D0",
                "#FFFF00",
                "#FFA500",
                "#FF0000",
            ]
        ],
        [
            "Normal data",
            "Ausreißer",
            "Inkonsistente Daten",
            "Fehlende Daten",
            "Undefinierte Daten",
        ],
        loc="upper right",
    )

    plt.title("Heatmap der Datenprobleme")
    plt.xlabel("Spalten")
    plt.ylabel("Zeilen")

    plt.savefig(
        prepare_file_path(image_filename),
        format=filename_extension[1:],
        dpi=300,
    )
    plt.show()


def correct_nan_values_worldPercentage_col(df):
    """
    Diese Methode ersetzt fehlende Werte in der Spalte 'worldPercentage'
    durch 0.

    Ich habe mich für diesen Ansatz entschieden, da die Länder, die NaN-Werte in
    der 'worldPercentage'-Spalte haben, eine sehr kleine Bevölkerung haben und
    somit einen sehr kleinen Anteil an der Weltbevölkerung. Obwohl diese Annahme
    eine Vereinfachung ist, sollte sie für viele Anwendungen ausreichend genau
    sein.
    """
    df["worldPercentage"].fillna(0, inplace=True)


def correct_data(df, filename=None):
    correct_nan_values_cca2_cca3_col(df, "countries_codes_and_coordinates.csv")
    correct_nan_values_netChange_col(df)
    correct_nan_values_worldPercentage_col(df)
    if filename:
        df.to_csv(filename, index=False)


def main():
    df = some_function("countries-table.csv")
    detect_data_issues_heatmap(df, "step_1_heatmap.jpg")
    correct_data(df, "clean_countries-table.csv")


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten