Arithmetik

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 auf den Indexbezeichnungen. Schauen wir uns ein Beispiel an:

[1]:
import numpy as np
import pandas as pd


rng = np.random.default_rng()
s1 = pd.Series(rng.normal(size=5))
s2 = pd.Series(rng.normal(size=7))

Addiert man diese Werte, erhält man:

[2]:
s1 + s2
[2]:
0    0.722551
1   -2.406030
2   -0.890058
3    0.567861
4   -0.436258
5         NaN
6         NaN
dtype: float64

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.

Bei DataFrames wird die Ausrichtung sowohl für die Zeilen als auch für die Spalten durchgeführt:

[3]:
df1 = pd.DataFrame(rng.normal(size=(5, 3)))
df2 = pd.DataFrame(rng.normal(size=(7, 2)))

Wenn die beiden DataFrames addiert werden, ergibt sich ein DataFrame, dessen Index und Spalten die Vereinigungen derjenigen in jedem der obigen DataFrames sind:

[4]:
df1 + df2
[4]:
0 1 2
0 0.273899 -0.568106 NaN
1 -0.337689 -1.422105 NaN
2 1.379209 0.996964 NaN
3 -0.398303 -0.325736 NaN
4 0.143051 1.320471 NaN
5 NaN NaN NaN
6 NaN NaN NaN

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.

Arithmetische Methoden mit Füllwerten

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:

[5]:
df12 = df1.add(df2, fill_value=0)

df12
[5]:
0 1 2
0 0.273899 -0.568106 0.650960
1 -0.337689 -1.422105 0.973974
2 1.379209 0.996964 0.556086
3 -0.398303 -0.325736 -0.283546
4 0.143051 1.320471 1.307053
5 0.201034 -1.587605 NaN
6 0.747488 -0.389178 NaN

Im folgenden Beispiel setzen wir die beiden verbleibenden NaN-Werte auf 0:

[6]:
df12.iloc[[5, 6], [2]] = 0
[7]:
df12
[7]:
0 1 2
0 0.273899 -0.568106 0.650960
1 -0.337689 -1.422105 0.973974
2 1.379209 0.996964 0.556086
3 -0.398303 -0.325736 -0.283546
4 0.143051 1.320471 1.307053
5 0.201034 -1.587605 0.000000
6 0.747488 -0.389178 0.000000

Arithmetische Methoden

Methode

Beschreibung

add, radd

Methoden für Addition (+)

sub, rsub

Methoden für die Subtraktion (-)

div, rdiv

Methoden für die Division (/)

floordiv, rfloordiv

Methoden für die Abrundungsfunktion (engl.: floor divison) (//)

mul, rmul

Methoden für die Multiplikation (*)

pow, rpow

Methoden zur Potenzierung (**)

r (engl.: reverse) kehrt die Methode um.

Operationen zwischen DataFrame und Series

Wie bei NumPy-Arrays verschiedener Dimensionen ist auch die Arithmetik zwischen DataFrame und Series definiert.

[8]:
s1 + df12
[8]:
0 1 2 3 4
0 0.028700 -1.108155 0.334155 NaN NaN
1 -0.582888 -1.962154 0.657169 NaN NaN
2 1.134010 0.456916 0.239281 NaN NaN
3 -0.643501 -0.865785 -0.600351 NaN NaN
4 -0.102147 0.780423 0.990248 NaN NaN
5 -0.044165 -2.127653 -0.316805 NaN NaN
6 0.502290 -0.929227 -0.316805 NaN NaN

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.

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:

Wenn ihr stattdessen die Spalten übertragen und die Zeilen abgleichen wollt, müsst ihr eine der arithmetischen Methoden verwenden, z.B.:

[9]:
df12.add(s2, axis="index")
[9]:
0 1 2
0 1.241648 0.399643 1.618709
1 -2.203672 -3.288087 -0.892008
2 0.805956 0.423711 -0.017167
3 -0.886309 -0.813743 -0.771553
4 0.004428 1.181848 1.168430
5 0.183869 -1.604769 -0.017164
6 0.322119 -0.814548 -0.425370

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.

Funktionsanwendung und Mapping

numpy.ufunc (elementweise Array-Methoden) funktionieren auch mit Pandas-Objekten:

[10]:
np.abs(df12)
[10]:
0 1 2
0 0.273899 0.568106 0.650960
1 0.337689 1.422105 0.973974
2 1.379209 0.996964 0.556086
3 0.398303 0.325736 0.283546
4 0.143051 1.320471 1.307053
5 0.201034 1.587605 0.000000
6 0.747488 0.389178 0.000000

Eine weitere häufige Operation ist die Anwendung einer Funktion auf eindimensionale Arrays auf jede Spalte oder Zeile. Die pandas.DataFrame.apply-Methode tut genau dies:

[11]:
df12
[11]:
0 1 2
0 0.273899 -0.568106 0.650960
1 -0.337689 -1.422105 0.973974
2 1.379209 0.996964 0.556086
3 -0.398303 -0.325736 -0.283546
4 0.143051 1.320471 1.307053
5 0.201034 -1.587605 0.000000
6 0.747488 -0.389178 0.000000
[12]:
def minmaxrange(x):
    return x.max() - x.min()


df12.apply(minmaxrange)
[12]:
0    1.777512
1    2.908076
2    1.590599
dtype: float64

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.

Wenn ihr axis='columns' an apply übergebt, wird die Funktion stattdessen einmal pro Zeile aufgerufen:

[13]:
df12.apply(minmaxrange, axis="columns")
[13]:
0    1.219066
1    2.396079
2    0.823123
3    0.114757
4    1.177420
5    1.788638
6    1.136666
dtype: float64

Viele der gebräuchlichsten Array-Statistiken (wie sum und mean) sind DataFrame-Methoden, so dass die Verwendung von apply nicht notwendig ist.

Die an apply übergebene Funktion muss keinen Einzelwert zurückgeben; sie kann auch eine Reihe mit mehreren Werten zurückgeben:

[14]:
def f(x):
    return pd.Series([x.min(), x.max()], index=["min", "max"])


df12.apply(f)
[14]:
0 1 2
min -0.398303 -1.587605 -0.283546
max 1.379209 1.320471 1.307053

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 erreichen:

[15]:
def round_two(x):
    return round(x, 2)


df12.map(round_two)
[15]:
0 1 2
0 0.27 -0.57 0.65
1 -0.34 -1.42 0.97
2 1.38 1.00 0.56
3 -0.40 -0.33 -0.28
4 0.14 1.32 1.31
5 0.20 -1.59 0.00
6 0.75 -0.39 0.00

Der Grund für den Namen applymap ist, dass Series über eine map-Methode zur Anwendung einer elementweisen Funktion verfügt:

[16]:
df12[2].map(round_two)
[16]:
0    0.65
1    0.97
2    0.56
3   -0.28
4    1.31
5    0.00
6    0.00
Name: 2, dtype: float64