Missing Values in Pandas

Auch wenn sie unerwünscht sind: Missings gehören nunmal häufig zum Datenmanagementprozess. Dieser Beitrag widmet sich daher der Frage, wie in pandas mit Missing-Werten umgegangen wird.

Missing Value Implementierung in Python

Pandas verwendet für fehlende Werte die numpy-Implementierung NaN. NaN steht für Not a Number und kann frei übersetzt als Missing Value bezeichnet werden.

Durch die interne numpy-Referenz existieren einige Methoden mit gleichem Anwendungsszenario in numpy als auch in pandas. Grundsätzlich empfiehlt es sich, konsequent mit der pandas-Bibliothek zu arbeiten. Langfristig zahlt es sich aus, in dieser einen Bibliothek „zu Hause“ zu sein und nicht immer wieder vor der Frage zu stehen: Woher stammt nochmal die Methode – numpy oder pandas?

nan ist NaN ist NAN

Auf den ersten Blick mag das verwundern, aber in numpy können verschiedene Schreibweisen für fehlende Werte verwendet werden. In ihrer Struktur, Bedeutung und ihrer Speicherreferenz sind die Objekte jedoch identisch. Welche Schreibweise bevorzugt wird, hängt ganz und gar vom Programmierer ab. (Erfahren Sie hier mehr über den is-Operator)

In [1]:
import numpy as np
np.nan is np.NaN is np.NAN
Out[1]:
True

np.nan ist nicht np.nan

Und auch dieser Umstand mag auf den ersten Blick verwundern: np.NaN entspricht nicht np.NaN.

In [2]:
np.NaN == np.NaN
Out[2]:
False

Lassen Sie uns versuchen, dieses Verhalten zu erklären und schauen wir uns dazu den Datentyp None an.
None ist das „objektivierte“ Nichts in Python und es existiert genau einmal. (Rein philosophisch betrachtet kann das Nichts natürlich nicht gleichzeitig auch vergegenständlicht sein, aber in der Welt der Programmiersprachen, müssen wir solche logischen Kompromisse eingehen.)

Beide Fragen:

None is None
None == None

returnieren den Wert True. Das heißt: Der Wert None hat genau eine Speicherreferenz und er entspricht gewissermaßen sich selbst.

Im Falle von np.NaN hingegen, teilen sich alle Objekte mit diesem Wert zwar eine Referenz, der Wert hingegen wird jedoch nicht als gleich angenommen. Erklären wir diesen Umstand an unserem Beispieldatensatz: Für 2 Personen fehlt die Angabe zum Gehalt. Verdienen diese Personen das gleiche? Vielleicht ja vielleicht nein. Wir wissen es nicht und daher ist der Rückgabewert des Vergleichs von np.NaN == np.NaN False. Man könnte sagen: Angesichts der unvollständigen Information und im Zweifel entscheidet sich numpy/pandas für die konservative Antwort.

Erstellen eines DataFrames mit Missing-Values

Soll ein DataFrame mit fehlenden Werte erstellt werden, kann dies auf 2 Wegen geschehen:

  1. Sowohl pandas als auch numpy werden importiert. Die fehlenden Werte werden mit np.NaN initialisiert.
  2. Für fehlende Werte wird der Wert None eingesetzt. None wird in pandas-Methoden wie np.NaN interpretiert.

Die beiden Varianten im unten aufgeführten Codeblock führen daher aus praktischer Sicht zum gleich Resultat (auch wenn sie streng genommen nicht das gleiche sind – vergleichen Sie den Output!):

In [3]:
import pandas as pd
import numpy as np
df = pd.DataFrame({'Name': ['Anton', np.NaN, 'Moni', 'Renate'],
                   'Alter': [32,22,62,np.NaN],
                   'Gehalt': [np.NaN, np.NaN,4500,2500]})
df
Out[3]:
Alter Gehalt Name
0 32.0 NaN Anton
1 22.0 NaN NaN
2 62.0 4500.0 Moni
3 NaN 2500.0 Renate
In [4]:
df = pd.DataFrame({'Name': ['Anton', None, 'Moni','Renate'],
                   'Alter': [32,22,62,None],
                   'Gehalt': [None, None,4500,2500]})
df
Out[4]:
Alter Gehalt Name
0 32.0 NaN Anton
1 22.0 NaN None
2 62.0 4500.0 Moni
3 NaN 2500.0 Renate

Test auf fehlende Werte

Sowohl für den Datentyp DataFrame als auch für den Datentyp Series existiert in pandas die Methode isnull(). Diese prüft, ob der Wert np.NaN entspricht und returniert einen entsprechenden booleschen Wert. Zugegeben, die Bezeichnung der Methode ist vielleicht nicht die glücklichste – schließlich ist in vielen Programmiersprachen NULL ein Schlüsselwort, welches eine andere Bedeutung einnimmt als ein fehlender Wert. Intuitiver ist daher auch die Methode isnan() aus der numpy-Bibliothek. Mit isnan prüfen wir, ob ein Wert nan ist – klingt logisch und nachvollziehbar! Dennoch bleibe ich bei der Empfehlung die pandas-Bibliothek zu konsequent zu nutzen.

Die folgenden Codeblöcke zeigen den Output der Methode isnull() von DataFrame und Series:

In [5]:
df.isnull()
Out[5]:
Alter Gehalt Name
0 False True False
1 False True True
2 False False False
3 True False False
In [6]:
df['Gehalt'].isnull()
Out[6]:
0     True
1     True
2    False
3    False
Name: Gehalt, dtype: bool

Hier die entsprechende Methode aus der numpy-Bibliothek:

In [7]:
np.isnan(df['Gehalt'])
Out[7]:
0     True
1     True
2    False
3    False
Name: Gehalt, dtype: bool

Fehlende Werte auszählen

Um die Anzahl fehlender Werte auszuzählen, kann auf die Ausgabe von isnull() die Methode sum() angewendet werden. Da boolesche Werte in der Form

False = 0
True = 1

interpretiert werden, entspricht die Summe der positiven Testfälle aus isnull() der Anzahl fehender Werte. Die Auszählung kann sowohl auf einer Series durchgeführt werden, als auch auf dem gesamten DataFrame. Für den gesamten DataFrame entspricht das Vorgehen von sum() dem Split-Apply-Combine Ansatz. (Hier erfahren Sie mehr über Split-Apply-Combine.)

In [8]:
df['Gehalt'].isnull().sum()
Out[8]:
2
In [9]:
df.isnull().sum()
Out[9]:
Alter     1
Gehalt    2
Name      1
dtype: int64

Arithmetische Operationen

Früher oder später ist wohl jeder Analyst mit dieser Frage konfrontiert: Wie gehen arithmetische Funktionen mit fehlenden Werten um?

Für pandas lässt sich diese Frage wie folgt beantworten: Wenn nicht explizit in den Parametern der Funktionen eingestellt, werden fehlende Werte in den Berechnungen standardmäßig ignoriert. Im folgenden Codeblock wird exemplarisch die Methode mean verwendet, um das Verhalten von Funktionen der deskriptiven Statistik aufzuzeigen. In gleicher Weise werden u.a. die Methoden std, var, median, min, max angewendet.

# Die default-Variante ignoriert fehlende Werte bei der Berechnung.
df['Alter'].mean()

# Wird das Argument skipna auf False gesetzt, führt der Versuch das Arithmetische Mittel unter Verwendung von NaN´s zu berechnen 
# ebenfalls zu NaN.
df['Alter'].mean(skipna=False)

# Wird skipna auf True gesetzt, entspricht das Verhalten der default-Variante
df['Alter'].mean(skipna=True)

Missings durch Werte ersetzen

Fehlende Werte können durch unterschiedliche Techniken ersetzt werden. Diese 2 Vorgehen sind für mich jedoch die elegantesten:

  1. Anwenden der Methode fillna()Die Methode existiert sowohl für die Klasse DataFrame als auch für die Klasse Series.
    df.fillna(0, inplace=True)
    df['Name'].fillna(0, inplace=True)
    
  2. Indizieren der Missings und Wertzuweisung
    df.loc[df['Gehalt'].isnull(),'Gehalt'] = "Unbekannter Wert"
    

Werte durch Missings ersetzen

Mit der Methode replace können Werte innerhalb einer Series oder eines DataFrames durch np.NaN ersetzt werden. Ebenso kann das Problem durch Indizierung und Wertzuweisung gelöst werden.

  1. Anwenden der Methode replace()
    df.replace(0, np.nan)
    df['Alter'].replace(0 ,np.NaN)
    
  2. Indizieren des Werts und Wertzuweisung
    df.loc[df['Alter'] == 0,:'Alter'] = np.NaN