{ "cells": [ { "cell_type": "markdown", "id": "4231b346", "metadata": {}, "source": [ "# Arithmetik\n", "\n", "Eine wichtige Funktion von pandas ist das arithmetische Verhalten bei Objekten mit unterschiedlichen Indizes. Wenn beim Addieren von Objekten die Indexpaare nicht gleich sind, wird der entsprechende Index im Ergebnis die Vereinigung der Indexpaare sein. Für Benutzer mit Datenbankerfahrung ist dies vergleichbar mit einem automatischen [OUTER JOIN](https://de.wikipedia.org/wiki/Join_(SQL)#%C3%84u%C3%9Ferer_Verbund_(OUTER_JOIN)) auf den Indexbezeichnungen. Schauen wir uns ein Beispiel an:" ] }, { "cell_type": "code", "execution_count": 1, "id": "5b29913f", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "\n", "s1 = pd.Series(np.random.randn(5))\n", "s2 = pd.Series(np.random.randn(7))" ] }, { "cell_type": "markdown", "id": "35204d7d", "metadata": {}, "source": [ "Addiert man diese Werte, erhält man:" ] }, { "cell_type": "code", "execution_count": 2, "id": "9c5ca9ed", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 -3.583041\n", "1 1.307006\n", "2 1.075852\n", "3 1.041123\n", "4 2.397227\n", "5 NaN\n", "6 NaN\n", "dtype: float64" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1 + s2" ] }, { "cell_type": "markdown", "id": "658fc144", "metadata": {}, "source": [ "Der interne Datenabgleich führt zu fehlenden Werten an den Stellen der Labels, die sich nicht überschneiden. Fehlende Werte werden dann bei weiteren arithmetischen Berechnungen weitergegeben." ] }, { "cell_type": "markdown", "id": "9a8a4ca0", "metadata": {}, "source": [ "Bei DataFrames wird die Ausrichtung sowohl für die Zeilen als auch für die Spalten durchgeführt:" ] }, { "cell_type": "code", "execution_count": 3, "id": "02308a89", "metadata": {}, "outputs": [], "source": [ "df1 = pd.DataFrame(np.random.randn(5,3))\n", "df2 = pd.DataFrame(np.random.randn(7,2))" ] }, { "cell_type": "markdown", "id": "5b6810dd", "metadata": {}, "source": [ "Wenn die beiden DataFrames addiert werden, ergibt sich ein DataFrame, dessen Index und Spalten die Vereinigungen derjenigen in jedem der obigen DataFrames sind:" ] }, { "cell_type": "code", "execution_count": 4, "id": "dd2da918", "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", " \n", " \n", " \n", "
012
0-1.2495541.414471NaN
1-1.152496-0.526046NaN
2-0.563285-1.919667NaN
3-0.818103-1.213753NaN
4-1.2679420.887387NaN
5NaNNaNNaN
6NaNNaNNaN
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -1.249554 1.414471 NaN\n", "1 -1.152496 -0.526046 NaN\n", "2 -0.563285 -1.919667 NaN\n", "3 -0.818103 -1.213753 NaN\n", "4 -1.267942 0.887387 NaN\n", "5 NaN NaN NaN\n", "6 NaN NaN NaN" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1 + df2" ] }, { "cell_type": "markdown", "id": "635e754e", "metadata": {}, "source": [ "Da die Spalte 2 nicht in beiden DataFrame-Objekten vorkommen, erscheinen sie im Ergebnis als fehlend. Das Gleiche gilt für die Zeilen, deren Bezeichnungen nicht in beiden Objekten vorkommen." ] }, { "cell_type": "markdown", "id": "4a99f089", "metadata": {}, "source": [ "## Arithmetische Methoden mit Füllwerten\n", "\n", "Bei arithmetischen Operationen zwischen unterschiedlich indizierten Objekten kann es sinnvoll sein, einen speziellen Wert (z. B. `0`) zu verwenden, wenn eine Achsenbeschriftung in einem Objekt gefunden wird, im anderen aber nicht. Mit der `add`-Methode kann das Argument `fill_value` übergeben werden:" ] }, { "cell_type": "code", "execution_count": 5, "id": "b587fa86", "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", " \n", " \n", " \n", "
012
0-1.2495541.414471-0.926853
1-1.152496-0.526046-1.331257
2-0.563285-1.919667-2.501289
3-0.818103-1.2137530.577517
4-1.2679420.8873870.109231
50.2331210.397626NaN
6-0.853883-1.058706NaN
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -1.249554 1.414471 -0.926853\n", "1 -1.152496 -0.526046 -1.331257\n", "2 -0.563285 -1.919667 -2.501289\n", "3 -0.818103 -1.213753 0.577517\n", "4 -1.267942 0.887387 0.109231\n", "5 0.233121 0.397626 NaN\n", "6 -0.853883 -1.058706 NaN" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12 = df1.add(df2, fill_value=0)\n", "\n", "df12" ] }, { "cell_type": "markdown", "id": "3184db4a", "metadata": {}, "source": [ "Im folgenden Beispiel setzen wir die beiden verbleibenden NaN-Werte auf `0`:" ] }, { "cell_type": "code", "execution_count": 6, "id": "a91d7a81", "metadata": {}, "outputs": [], "source": [ "df12.iloc[[5,6], [2]] = 0" ] }, { "cell_type": "code", "execution_count": 7, "id": "0467d4bc", "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", " \n", " \n", " \n", "
012
0-1.2495541.414471-0.926853
1-1.152496-0.526046-1.331257
2-0.563285-1.919667-2.501289
3-0.818103-1.2137530.577517
4-1.2679420.8873870.109231
50.2331210.3976260.000000
6-0.853883-1.0587060.000000
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -1.249554 1.414471 -0.926853\n", "1 -1.152496 -0.526046 -1.331257\n", "2 -0.563285 -1.919667 -2.501289\n", "3 -0.818103 -1.213753 0.577517\n", "4 -1.267942 0.887387 0.109231\n", "5 0.233121 0.397626 0.000000\n", "6 -0.853883 -1.058706 0.000000" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12" ] }, { "cell_type": "markdown", "id": "75266ce2", "metadata": {}, "source": [ "## Arithmetische Methoden\n", "\n", "Methode | Beschreibung\n", ":------ | :-----------\n", "`add`, `radd` | Methoden für Addition (`+`)\n", "`sub`, `rsub` | Methoden für die Subtraktion (`-`)\n", "`div`, `rdiv` | Methoden für die Division (`/`)\n", "`floordiv`, `rfloordiv` | Methoden für die Abrundungsfunktion (engl.: floor divison) (`//`)\n", "`mul`, `rmul` | Methoden für die Multiplikation (`*`)\n", "`pow`, `rpow` | Methoden zur Potenzierung (`**`)\n", "\n", "`r` (engl.: _reverse_) kehrt die Methode um." ] }, { "cell_type": "markdown", "id": "72c03854", "metadata": {}, "source": [ "## Operationen zwischen DataFrame und Series\n", "\n", "Wie bei NumPy-Arrays verschiedener Dimensionen ist auch die Arithmetik zwischen DataFrame und Series definiert." ] }, { "cell_type": "code", "execution_count": 8, "id": "8d7b47b5", "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
01234
0-1.8839613.221520-1.603931NaNNaN
1-1.7869031.281004-2.008336NaNNaN
2-1.197692-0.112617-3.178368NaNNaN
3-1.4525090.593297-0.099561NaNNaN
4-1.9023482.694437-0.567848NaNNaN
5-0.4012862.204675-0.677079NaNNaN
6-1.4882900.748343-0.677079NaNNaN
\n", "
" ], "text/plain": [ " 0 1 2 3 4\n", "0 -1.883961 3.221520 -1.603931 NaN NaN\n", "1 -1.786903 1.281004 -2.008336 NaN NaN\n", "2 -1.197692 -0.112617 -3.178368 NaN NaN\n", "3 -1.452509 0.593297 -0.099561 NaN NaN\n", "4 -1.902348 2.694437 -0.567848 NaN NaN\n", "5 -0.401286 2.204675 -0.677079 NaN NaN\n", "6 -1.488290 0.748343 -0.677079 NaN NaN" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1 + df12" ] }, { "cell_type": "markdown", "id": "3048aa55", "metadata": {}, "source": [ "Wenn wir `s1` mit `df12` addieren, wird die Additon für jede Zeile einmal durchgeführt. Dies wird als _Broadcasting_ bezeichnet. Standardmäßig entspricht die Arithmetik zwischen DataFrame und Serie dem Index der Serie in den Spalten des DataFrame, wobei die Zeilen nach unten übertragen werden." ] }, { "cell_type": "markdown", "id": "3d69cc0e", "metadata": {}, "source": [ "Wenn ein Indexwert weder in den Spalten des DataFrame noch im Index der Serie gefunden wird, werden die Objekte neu indiziert, um die Vereinigung zu bilden:" ] }, { "cell_type": "markdown", "id": "c252358c", "metadata": {}, "source": [ "Wenn ihr stattdessen die Spalten übertragen und die Zeilen abgleichen wollt, müsst ihr eine der arithmetischen Methoden verwenden, z.B.:" ] }, { "cell_type": "code", "execution_count": 9, "id": "b5499888", "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", " \n", " \n", " \n", "
012
0-4.198189-1.534164-3.875487
1-1.652540-1.026089-1.831301
21.189646-0.166736-0.748358
30.104381-0.2912681.500002
4-0.0485692.1067601.328604
5-1.142244-0.977739-1.375365
6-0.464831-0.6696540.389053
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -4.198189 -1.534164 -3.875487\n", "1 -1.652540 -1.026089 -1.831301\n", "2 1.189646 -0.166736 -0.748358\n", "3 0.104381 -0.291268 1.500002\n", "4 -0.048569 2.106760 1.328604\n", "5 -1.142244 -0.977739 -1.375365\n", "6 -0.464831 -0.669654 0.389053" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12.add(s2, axis=\"index\")" ] }, { "cell_type": "markdown", "id": "f4dabeca", "metadata": {}, "source": [ "Die Achsennummer, die ihr übergebt, ist die Achse, auf die abgeglichen werden soll. In diesem Fall soll der Zeilenindex des DataFrame (`axis='index'` oder `axis=0`) abgeglichen und übertragen werden." ] }, { "cell_type": "markdown", "id": "f04a8ce0", "metadata": {}, "source": [ "## Funktionsanwendung und Mapping\n", "\n", "`numpy.ufunc` (elementweise Array-Methoden) funktionieren auch mit Pandas-Objekten:" ] }, { "cell_type": "code", "execution_count": 10, "id": "6703726d", "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", " \n", " \n", " \n", "
012
01.2495541.4144710.926853
11.1524960.5260461.331257
20.5632851.9196672.501289
30.8181031.2137530.577517
41.2679420.8873870.109231
50.2331210.3976260.000000
60.8538831.0587060.000000
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 1.249554 1.414471 0.926853\n", "1 1.152496 0.526046 1.331257\n", "2 0.563285 1.919667 2.501289\n", "3 0.818103 1.213753 0.577517\n", "4 1.267942 0.887387 0.109231\n", "5 0.233121 0.397626 0.000000\n", "6 0.853883 1.058706 0.000000" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.abs(df12)" ] }, { "cell_type": "markdown", "id": "e6cb5cbe", "metadata": {}, "source": [ "Eine weitere häufige Operation ist die Anwendung einer Funktion auf eindimensionale Arrays auf jede Spalte oder Zeile. Die [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)-Methode tut genau dies:" ] }, { "cell_type": "code", "execution_count": 11, "id": "fba73255", "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", " \n", " \n", " \n", "
012
0-1.2495541.414471-0.926853
1-1.152496-0.526046-1.331257
2-0.563285-1.919667-2.501289
3-0.818103-1.2137530.577517
4-1.2679420.8873870.109231
50.2331210.3976260.000000
6-0.853883-1.0587060.000000
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -1.249554 1.414471 -0.926853\n", "1 -1.152496 -0.526046 -1.331257\n", "2 -0.563285 -1.919667 -2.501289\n", "3 -0.818103 -1.213753 0.577517\n", "4 -1.267942 0.887387 0.109231\n", "5 0.233121 0.397626 0.000000\n", "6 -0.853883 -1.058706 0.000000" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12" ] }, { "cell_type": "code", "execution_count": 12, "id": "e7589502", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 1.501062\n", "1 3.334137\n", "2 3.078807\n", "dtype: float64" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = lambda x: x.max() - x.min()\n", "\n", "df12.apply(f)" ] }, { "cell_type": "markdown", "id": "5c730fc0", "metadata": {}, "source": [ "Hier wird die Funktion `f`, die die Differenz zwischen dem Maximum und dem Minimum einer Reihe berechnet, einmal für jede Spalte des Rahmens aufgerufen. Das Ergebnis ist eine Reihe mit den Spalten des Rahmens als Index." ] }, { "cell_type": "markdown", "id": "cd3d8cef", "metadata": {}, "source": [ "Wenn ihr `axis='columns'` an `apply` übergebt, wird die Funktion stattdessen einmal pro Zeile aufgerufen:" ] }, { "cell_type": "code", "execution_count": 13, "id": "8db58697", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 2.664025\n", "1 0.805211\n", "2 1.938004\n", "3 1.791270\n", "4 2.155329\n", "5 0.397626\n", "6 1.058706\n", "dtype: float64" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12.apply(f, axis=\"columns\")" ] }, { "cell_type": "markdown", "id": "1dfebb36", "metadata": {}, "source": [ "Viele der gebräuchlichsten Array-Statistiken (wie `sum` und `mean`) sind DataFrame-Methoden, so dass die Verwendung von `apply` nicht notwendig ist." ] }, { "cell_type": "markdown", "id": "429fb65c", "metadata": {}, "source": [ "Die an apply übergebene Funktion muss keinen Einzelwert zurückgeben; sie kann auch eine Reihe mit mehreren Werten zurückgeben:" ] }, { "cell_type": "code", "execution_count": 14, "id": "758ab932", "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", "
012
min-1.267942-1.919667-2.501289
max0.2331211.4144710.577517
\n", "
" ], "text/plain": [ " 0 1 2\n", "min -1.267942 -1.919667 -2.501289\n", "max 0.233121 1.414471 0.577517" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x):\n", " return pd.Series([x.min(), x.max()], index=[\"min\", \"max\"])\n", "\n", "\n", "df12.apply(f)" ] }, { "cell_type": "markdown", "id": "5b6f33bc", "metadata": {}, "source": [ "Es können auch elementweise Python-Funktionen verwendet werden. Angenommen, ihr möchtet aus jedem Fließkommawert in `df12` eine formatierte Zeichenkette berechnen. Dies könnt ihr mit [pandas.DataFrame.applymap](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.applymap.html) erreichen:" ] }, { "cell_type": "code", "execution_count": 15, "id": "ba3f9ec6", "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", " \n", " \n", " \n", "
012
0-1.251.41-0.93
1-1.15-0.53-1.33
2-0.56-1.92-2.50
3-0.82-1.210.58
4-1.270.890.11
50.230.400.00
6-0.85-1.060.00
\n", "
" ], "text/plain": [ " 0 1 2\n", "0 -1.25 1.41 -0.93\n", "1 -1.15 -0.53 -1.33\n", "2 -0.56 -1.92 -2.50\n", "3 -0.82 -1.21 0.58\n", "4 -1.27 0.89 0.11\n", "5 0.23 0.40 0.00\n", "6 -0.85 -1.06 0.00" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f = lambda x: round(x, 2)\n", "\n", "df12.map(f)" ] }, { "cell_type": "markdown", "id": "cda74d52", "metadata": {}, "source": [ "Der Grund für den Namen `applymap` ist, dass Series über eine `map`-Methode zur Anwendung einer elementweisen Funktion verfügt:" ] }, { "cell_type": "code", "execution_count": 16, "id": "df04727d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 -0.93\n", "1 -1.33\n", "2 -2.50\n", "3 0.58\n", "4 0.11\n", "5 0.00\n", "6 0.00\n", "Name: 2, dtype: float64" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df12[2].map(f)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.13 Kernel", "language": "python", "name": "python313" }, "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.13.0" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }