{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 3) Fehlende Werte, Werte außerhalb des Skalenbereichs, Zeilenduplikate\n", "\n", "Wir wissen aus dem vorangegangenen Kapitel bereits, wie man ein Dataframe in Python erstellen kann. Bevor wir im nächsten Kapitel erstmals echte Daten aus einem bereits existierendem Datenfile laden werden, befasst sich dieses Kapitel damit, wie wir Werte identifizieren, die unplausibel sind und/oder offensichtlich nicht zu unseren vorgegebenen Skalenbandbreiten passen. Außerdem wird der Umgang mit fehlenden Werten und Zeilenduplikaten besprochen." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.1) Fehlende Werte\n", "\n", "Fehlende Werte werden in Python typischerweise mit 'NaN' (not a number) bezeichnet und stellen bei manchen Analysen ev. ein Problem dar." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Zuerst ein neues Dataframe erstellen\n", "\n", "Erstellen wir zuerst mit bereits bekannter Methode ein neues Dataframe, in welches später fehlende Werte eingefügt werden sollen." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
0-0.231624-0.436485
1-0.5149170.916378
2-1.436841-0.786471
30.3717051.689001
4-0.323062-0.253596
\n", "
" ], "text/plain": [ " a b\n", "0 -0.231624 -0.436485\n", "1 -0.514917 0.916378\n", "2 -1.436841 -0.786471\n", "3 0.371705 1.689001\n", "4 -0.323062 -0.253596" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "daten = pd.DataFrame({'a' : np.random.randn(5), 'b' : np.random.randn(5)})\n", "\n", "daten" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Nun die fehlenden Werte einfügen\n", "\n", "Mit folgender Methode können beliebige einzelne Werte geändert werden.\n", "\n", "[pandas.DataFrame.at](https://pandas.pydata.org/pandas-docs/version/0.25/reference/api/pandas.DataFrame.at.html)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
0NaN-0.436485
1-0.514917NaN
2-1.436841-0.786471
30.3717051.689001
4NaN-0.253596
\n", "
" ], "text/plain": [ " a b\n", "0 NaN -0.436485\n", "1 -0.514917 NaN\n", "2 -1.436841 -0.786471\n", "3 0.371705 1.689001\n", "4 NaN -0.253596" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten.at[0, 'a'] = None # Zeile 0 in Spalte 'a' wird geändert zu None\n", "daten.at[1, 'b'] = 'NaN' # Zeile 1 in Spalte 'b' wird geändert zu 'NaN'\n", "daten.at[4, 'a'] = None\n", "# 'NaN' und None sind im Grunde nicht das Gleiche, für die Zwecke dieses Beispiels führen sie aber zum selben Ziel\n", "\n", "daten" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Auf fehlende Werte prüfen\n", "\n", "Wir haben 3 Werte unseres Datensatzes durch fehlende Werte ('NaN') ersetzt. Nun wollen wir prüfen, ob in unserem Datensatz fehlende Werte vorhanden sind (unser Beispiel ist mit 2 Spalten und 5 Zeilen leicht visuell überprüfbar, ein Datensatz mit 200 Spalten und 5000 Zeilen wäre das nicht mehr...). MIt **isnull()** bzw. **isna()** kann auf fehlende Werte geprüft werden. \n", "\n", "[DataFrame.isna](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.isna.html)\n", "\n", "[DataFrame.isnull](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.isnull.html)\n", "\n", "[How to check if any value is NaN in a Pandas DataFrame](https://stackoverflow.com/questions/29530232/how-to-check-if-any-value-is-nan-in-a-pandas-dataframe)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bei überschaubaren Datensätzen kann folgende Methode angewandt werden. Wahrscheinlich erkennt man fehlende Werte aber durch reguläre Anzeige des Dataframes (siehe oben) schneller ('NaN')." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Befinden sich fehlende Werte im Datensatz?\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
0TrueFalse
1FalseTrue
2FalseFalse
3FalseFalse
4TrueFalse
\n", "
" ], "text/plain": [ " a b\n", "0 True False\n", "1 False True\n", "2 False False\n", "3 False False\n", "4 True False" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print('\\nBefinden sich fehlende Werte im Datensatz?')\n", "\n", "daten.isnull() # alternativ: isna()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ob überhaupt fehlende Werte an irgendeiner Stelle im Datensatz vorhanden sind, kann mit **isnull.values.any()** geprüft werden." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten.isnull().values.any() # Im gesamten Dataframe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Eine Anzeige der Anzahl fehlender Werte pro Spalte (Variable) gelingt mit **isnull.sum()**." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "a 2\n", "b 1\n", "dtype: int64" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten.isnull().sum() # Pro Spalte (Variable)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Wieviele fehlende Werte pro Spalte bzw. pro Zeile?\n", "\n", "Man kann dies alles auch mit Hinweistexten ausbauen sowie getrennt nach Anzahl der fehlenden Werte für die Spalten und für die Zeilen suchen.\n", "\n", "[12 Useful Pandas Techniques in Python for Data Manipulation](https://www.analyticsvidhya.com/blog/2016/01/12-pandas-techniques-python-data-manipulation/)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fehlende Werte pro Spalte:\n", "a 2\n", "b 1\n", "dtype: int64\n", "\n", "Fehlende Werte pro Zeile:\n", "0 1\n", "1 1\n", "2 0\n", "3 0\n", "4 1\n", "dtype: int64\n" ] } ], "source": [ "def missing(x):\n", " return sum(x.isnull())\n", "\n", "# Pro Spalte (Variable):\n", "print(\"Fehlende Werte pro Spalte:\")\n", "print(daten.apply(missing, axis = 0)) # mit 'axis = 0' wird spaltenweise gesucht\n", "\n", "# Pro Zeile:\n", "print(\"\\nFehlende Werte pro Zeile:\")\n", "print(daten.apply(missing, axis = 1).head(5)) # mit 'axis = 1' wird zeilenweise gesucht" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Daten beschreiben, div. deskriptive Auswertungen\n", "\n", "Sehen wir uns nun die Auswirkungen von fehlenden Werten auf div. deskriptive Kennzahlen an. Zuerst mit den fehlenden Werten ('NaN'), dann ohne die fehlenden Werte.\n", "\n", "[DataFrame.describe](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.describe.html)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
0NaN-0.436485
1-0.514917NaN
2-1.436841-0.786471
30.3717051.689001
4NaN-0.253596
\n", "
" ], "text/plain": [ " a b\n", "0 NaN -0.436485\n", "1 -0.514917 NaN\n", "2 -1.436841 -0.786471\n", "3 0.371705 1.689001\n", "4 NaN -0.253596" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wir sehen, dass die Funktion **describe()** die fehlenden Werte nicht berücksichtigt (vgl. Zeile *count*)." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
count3.0000004.000000
mean-0.5266840.053112
std0.9043301.112776
min-1.436841-0.786471
25%-0.975879-0.523982
50%-0.514917-0.345040
75%-0.0716060.232054
max0.3717051.689001
\n", "
" ], "text/plain": [ " a b\n", "count 3.000000 4.000000\n", "mean -0.526684 0.053112\n", "std 0.904330 1.112776\n", "min -1.436841 -0.786471\n", "25% -0.975879 -0.523982\n", "50% -0.514917 -0.345040\n", "75% -0.071606 0.232054\n", "max 0.371705 1.689001" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Fehlende Werte (ganze jeweilige Zeile) aus Dataframe entfernen\n", "\n", "Wenn wir nur vollständige Datenzeilen möchten, können wir fehlende Werte zeilenweise entfernen (d.h. sobald in einer Zeile zumindest 1 fehlender Wert vorhanden ist, wird die ganze Zeile aus dem Dataframe entfernt). Die Funktion dafür ist **dropna()**. Sie kann auf das ganze Dataframe angewandt werden, oder mit *subset* auch nur auf einzelne Spalten (in denen dann nach fehlenden Werten gesucht wird - gelöscht wird aber natürlich die ganze Zeile, also auch die Werte in jenen Spalten, die nicht im *subset* waren!)\n", "\n", "[DataFrame.dropna](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html#pandas.DataFrame.dropna)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fehlende Werte werden aus Datensatz entfernt\n" ] } ], "source": [ "print('Fehlende Werte werden aus Datensatz entfernt')\n", "\n", "daten.dropna(subset=['a', 'b'], inplace = True) # nur in Spalten a und b wird nach fehlenden Werten gesucht\n", "\n", "# Soll in allen Spalten/Zeilen nach fehlenden Werten gesucht werden: daten.dopna()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Daten beschreiben, div. deskriptive Auswertungen\n", "\n", "Wie wir unten sehen, wurden jene Spalten, in denen sich fehlende Werte befanden, aus dem Dataframe entfernt. Es befinden sich statt 5 nun nur mehr 2 Zeilen im Datensatz." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
2-1.436841-0.786471
30.3717051.689001
\n", "
" ], "text/plain": [ " a b\n", "2 -1.436841 -0.786471\n", "3 0.371705 1.689001" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ab
count2.0000002.000000
mean-0.5325680.451265
std1.2788341.750423
min-1.436841-0.786471
25%-0.984704-0.167603
50%-0.5325680.451265
75%-0.0804321.070133
max0.3717051.689001
\n", "
" ], "text/plain": [ " a b\n", "count 2.000000 2.000000\n", "mean -0.532568 0.451265\n", "std 1.278834 1.750423\n", "min -1.436841 -0.786471\n", "25% -0.984704 -0.167603\n", "50% -0.532568 0.451265\n", "75% -0.080432 1.070133\n", "max 0.371705 1.689001" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "daten.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.2) Werte ausserhalb des Skalenbereichs\n", "\n", "Oftmals sind in Datensätzen Werte zu entdecken, die nicht recht in Bild passen. Z.B. unplausible Werte (bspw. weist eine Person beim Alter 155 Jahre auf) oder Werte die über den vorgesehenen Skalenbereich hinausgehen (z.B. finden sich bei einer Variable, die Antworten auf einer Ratingskala von 1 bis 5 enthält, auch die Werte 6, 22 und 99). Solche Werte können auf unterschiedliche Weise zustande kommen. Bspw. kann es sich um Tippfehler bei der Dateneingabe handeln, es kann sich um 'scherzhafte' (unseriöse) Antworten von Probanden handeln oder es kann sich um echte Werte handeln, die für bestimmte Antwortkategorien stehen (so kann bei einer Ratingskala von 1 bis 5 der Wert 6 für 'weiß nicht', der Wert 7 für 'keine Angabe' und der Wert 99 bspw. für 'fehlend' stehen)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Zuerst Dataframe aus Zufallszahlen erstellen\n", "\n", "Erstellen wir zuerst ein kurzes Dataframe, in welchem wir später div. 'unbrauchbare' Werte einfügen. Nehmen wir an, es handelt sich dabei um 2 Variablen, die jeweils Antworten auf einer 5-stufige Ratingskala (von 1 bis 5) enthalten." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "data = pd.DataFrame({'Item A' : np.random.randint(1, 6, 5), 'Item B' : np.random.randint(1, 6, 5)})\n", "# damit werden in 2 Variablen jeweils 5 Werte von 1-5(!) erzeugt (6 ist nicht mehr im Bereich)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
025
142
244
332
431
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2 5\n", "1 4 2\n", "2 4 4\n", "3 3 2\n", "4 3 1" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Nun Werte ausserhalb des Skalenbereichs (1-5) hinzufügen\n", "\n", "Nun fügen wir mit der bereits von weiter oben bekannten Methode einige Werte hinzu, die eindeutig ausserhalb des Bereichs von 1 bis 5 liegen." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
025
162
244
337
4111
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2 5\n", "1 6 2\n", "2 4 4\n", "3 3 7\n", "4 11 1" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.at[1, 'Item A'] = 6\n", "data.at[3, 'Item B'] = 7\n", "data.at[4, 'Item A'] = 11\n", "\n", "data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mit **value_counts()** (vgl. Kapitel 5 zur deskriptiven Analyse) kann die Häufigkeit der einzelnen Ausprägungen ermittelt werden. **sort_index()** sortiert die Ausgabe dabei nach den Merkmalsausprägungen und nicht nach den Häufigkeiten." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2 1\n", "3 1\n", "4 1\n", "6 1\n", "11 1\n", "Name: Item A, dtype: int64" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data['Item A'].value_counts().sort_index() # in der Ausgabe stehen links die Wertelabels, rechts die Häufigkeiten\n", "# mit 'sort_index()' wird nach den Wertelabels und nicht nach den Häufigkeiten sortiert" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1 1\n", "2 1\n", "4 1\n", "5 1\n", "7 1\n", "Name: Item B, dtype: int64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data['Item B'].value_counts().sort_index()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Somit erkennen wir sofort, dass in Spalte *Item A* der Wert 6 und der Wert 11 je einmal vorkommen und in Spalte *Item B* der Wert 7 einmal vorkommt. Untenstehendes Streudiagramm ermöglicht das Erkennen dieser Werte auf grafischem Weg." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "ax = data.plot.scatter(x = 'Item A', y = 'Item B', c = 'blue', s = 10) # 's' = Größe (size) der Punkte" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Umgang mit diesen Werten ausserhalb des Skalenbereichs\n", "\n", "Diese verzerren natürlich jegliche statistische Analyse. Zunächst ist der Grund für diese Werte in Erfahrung zu bringen:\n", "\n", "Wenn es sich um Fehleingaben handelt müssen diese korrigiert werden.\n", "\n", "Der Rest (nach Korrektur) soll im Rahmen der deskriptiven Analyse natürlich erfasst und dokumentiert werden,\n", "z.B. weiß nicht, keine Angabe, div. Gründe der Antwortverweigerung, usw.\n", "\n", "Für die statistischen Analysen schließlich müssen diese Werte aber ausgeschlossen werden, man könnte sie bspw. löschen.\n", "Dabei ist es aber sinnvoll, dies nicht im Originaldatenfile zu machen, sondern dafür ein neues Datenfile anzulegen!\n", "\n", "Mit der Funktion **replace()** können Werte durch beliebige andere Werte ersetzt werden. Im folgenden Beispiel werden die Werte 6, 7 und 11 durch fehlende Werte ersetzt.\n", "\n", "[DataFrame.replace](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.replace.html)\n", "\n", "[numpy.nan](https://numpy.org/devdocs/reference/constants.html#numpy.nan)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "neu1 = data.replace([6, 7, 11], np.nan)\n", "# Werte werden im ganzen Dataframe auf einmal ersetzt und Dataframe wird unter neuem Namen gespeichert" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
02.05.0
1NaN2.0
24.04.0
33.0NaN
4NaN1.0
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2.0 5.0\n", "1 NaN 2.0\n", "2 4.0 4.0\n", "3 3.0 NaN\n", "4 NaN 1.0" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Im nächsten Beispiel werden nur die Werte 6 und 7 als fehlende Werte definiert. Der Wert 11 hat sich als Tippfehler bei der Eingabe herausgestellt und wird durch den korrekt Wert 1 ersetzt." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "neu2 = data.replace({6: np.nan, 7: np.nan, 11: 1})\n", "# Jeder Wert wird im ganzen Dataframe individuell durch einen anderen Wert ersetzt" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
02.05.0
1NaN2.0
24.04.0
33.0NaN
41.01.0
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2.0 5.0\n", "1 NaN 2.0\n", "2 4.0 4.0\n", "3 3.0 NaN\n", "4 1.0 1.0" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Man das Ersetzen von Werten auch für jede Spalte getrennt definieren (anstatt wie in den beiden vorangegangenen Beispielen für das gesamte Dataframe auf einmal). In nachfolgendem Beispiel werden bei *Item A* und *Item B* einige Werte durch beliebige unplausible Werte bzw. 'NaN' ersetzt." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "neu3 = data.replace({'Item A' : {4: 666, 6: 667}, 'Item B' : {5: 99, 1: 999, 7: np.nan}})\n", "# Die Werte werden pro Spalte durch einen anderen Wert ersetzt" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
0299.0
16672.0
26664.0
33NaN
411999.0
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2 99.0\n", "1 667 2.0\n", "2 666 4.0\n", "3 3 NaN\n", "4 11 999.0" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Zeilen mit bestimmten Werten ausfindig machen\n", "\n", "Wie bereits an früherer Stelle erwähnt, können kurze überschaubare Dataframes - wie sie in diesen Beispielen dargestellt sind - leicht visuell überprüft werden. Bei großen Datensätzen ist dies keine geeignete Strategie. Mit **loc** können spezifische Werte gesucht und angezeigt werden. Sucht man, wie in folgendem Beispiel, in Spalte *Item A* nach dem Wert 11, so zeigt das Ergebnis, dass sich dieser in Zeile 4 (eigentlich Zeile 5, da Pandas die Zeilennummerierung bei 0 beginnt) befindet.\n", "\n", "[Select rows from a DataFrame based on values in a column in pandas](https://stackoverflow.com/questions/17071871/select-rows-from-a-dataframe-based-on-values-in-a-column-in-pandas)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
411999.0
\n", "
" ], "text/plain": [ " Item A Item B\n", "4 11 999.0" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu3.loc[neu3['Item A'] == 11]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Man kann auch in beiden (oder mehreren, falls vorhanden) Spalten nach Werten suchen, wie folgendes Beispiel zeigt. Hier wird nach Zeilen gesucht, die in beiden Spalten Werte aufweisen die >= 5 sind." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
411999.0
\n", "
" ], "text/plain": [ " Item A Item B\n", "4 11 999.0" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu3.loc[(neu3['Item A'] >= 5) & (neu3['Item B'] >= 5)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Werte ausserhalb des Skalenbereichs ersetzen bzw. löschen\n", "\n", "Alle Werte, die außerhalb des vorgesehenen Skalenbereichs liegen, können mit einem Befehl ersetzt werden. Im Folgenden werden mit der Funktion **where()** in beiden Spalten sämtliche Werte, die nicht <=5 sind, durch 'NaN' ersetzt." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "neu4 = neu3.where(neu3[['Item A', 'Item B']] <=5, np.nan)\n", "# Werte, bei denen die angeführte Bedingung (<=5) nicht zutrifft, werden durch NaN (oder beliebigen anderen Wert) ersetzt!" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item AItem B
02.0NaN
1NaN2.0
2NaN4.0
33.0NaN
4NaNNaN
\n", "
" ], "text/plain": [ " Item A Item B\n", "0 2.0 NaN\n", "1 NaN 2.0\n", "2 NaN 4.0\n", "3 3.0 NaN\n", "4 NaN NaN" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "neu4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.3) Doppelte Einträge, z.B. die gleiche Zeile mehrmals im Datensatz vorhanden\n", "\n", "In großen Datensätzen macht es durchaus Sinn, nach Zeilenduplikaten Ausschau zu halten. Zeilenduplikate sind jene, die bei allen Variablen (Spalten) identische Werte aufweisen. Selbstverständlich kann das in der Realität vorkommen, manchmal ist dies jedoch auch ein Hinweis auf Fehleingaben oder gar Fälschungen von Daten. Mit der Funktion **duplicated()** wird für jede Zeile angezeigt, ob sie mehrfach vorhanden ist (True) oder nicht (False).\n", "\n", "[DataFrame.duplicated](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.duplicated.html)\n", "\n", "[Pandas : Find duplicate rows in a Dataframe based on all or selected columns using DataFrame.duplicated() in Python](https://thispointer.com/pandas-find-duplicate-rows-in-a-dataframe-based-on-all-or-selected-columns-using-dataframe-duplicated-in-python/)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 False\n", "1 False\n", "2 False\n", "3 False\n", "4 False\n", "dtype: bool" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.duplicated(subset = ['Item A', 'Item B'], keep = False) # Mit 'subset' wird nur in den beiden angegebenen Spalten gesucht" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternativ kann man dies folgendermaßen schreiben:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gleiche Einträge finden sich in folgenden Zeilen:\n", "\n", "Empty DataFrame\n", "Columns: [Item A, Item B]\n", "Index: []\n" ] } ], "source": [ "doppelt = data[data.duplicated(['Item A', 'Item B'], keep = False)]\n", " \n", "print(\"Gleiche Einträge finden sich in folgenden Zeilen:\\n\", doppelt, sep = '\\n')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Glück gehabt, es wurden keine Zeilenduplikate gefunden!\n", "\n", "Man könnte auch nur in einer Spalte suchen. Das macht bei großen Datensätzen für die meisten Variablen keinen Sinn, da meist sehr viele idente Nennungen für jede Merkmalsausprägung vorliegen werden. Dennoch gibt es dafür Anwendungsfälle, z.B. ein Datensatz mit Sozialversicherungsnummmern oder Matrikelnummern oder Autokennzeichen - diese sollten alle nur jeweils einmal vorkommen." ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Idente Einträge finden sich in folgenden Zeilen:\n", "\n", "Empty DataFrame\n", "Columns: [Item A, Item B]\n", "Index: []\n" ] } ], "source": [ "ident = data[data.duplicated(['Item A'], keep = False)]\n", " \n", "print(\"Idente Einträge finden sich in folgenden Zeilen:\\n\", ident, sep = '\\n')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Auch innerhalb von Spalte *Item A* wurden keine doppelten Einträge gefunden." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.9" } }, "nbformat": 4, "nbformat_minor": 2 }