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


s1 = pd.Series(np.random.randn(5))
s2 = pd.Series(np.random.randn(7))

Addiert man diese Werte, erhält man:

[2]:
s1 + s2
[2]:
0   -0.862485
1    0.474474
2   -0.357133
3   -0.625274
4    0.787645
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(np.random.randn(5,3))
df2 = pd.DataFrame(np.random.randn(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.228215 -0.922674 NaN
1 -1.515175 3.044930 NaN
2 0.501039 2.414039 NaN
3 -0.495267 -0.952028 NaN
4 0.630200 1.885596 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.228215 -0.922674 -1.091759
1 -1.515175 3.044930 0.018406
2 0.501039 2.414039 -0.252414
3 -0.495267 -0.952028 2.494162
4 0.630200 1.885596 -0.292126
5 1.651636 -0.611053 NaN
6 -0.320220 0.199775 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.228215 -0.922674 -1.091759
1 -1.515175 3.044930 0.018406
2 0.501039 2.414039 -0.252414
3 -0.495267 -0.952028 2.494162
4 0.630200 1.885596 -0.292126
5 1.651636 -0.611053 0.000000
6 -0.320220 0.199775 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 -2.324582 -1.279211 -1.284821 NaN NaN
1 -3.611541 2.688394 -0.174656 NaN NaN
2 -1.595327 2.057503 -0.445476 NaN NaN
3 -2.591633 -1.308564 2.301100 NaN NaN
4 -1.466166 1.529060 -0.485188 NaN NaN
5 -0.444730 -0.967589 -0.193062 NaN NaN
6 -2.416587 -0.156761 -0.193062 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.005665 0.311206 0.142122
1 -0.684165 3.875940 0.849415
2 0.336968 2.249968 -0.416485
3 -0.307428 -0.764190 2.682000
4 1.990071 3.245467 1.067746
5 0.686948 -1.575741 -0.964688
6 -1.575925 -1.055929 -1.255704

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.228215 0.922674 1.091759
1 1.515175 3.044930 0.018406
2 0.501039 2.414039 0.252414
3 0.495267 0.952028 2.494162
4 0.630200 1.885596 0.292126
5 1.651636 0.611053 0.000000
6 0.320220 0.199775 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.228215 -0.922674 -1.091759
1 -1.515175 3.044930 0.018406
2 0.501039 2.414039 -0.252414
3 -0.495267 -0.952028 2.494162
4 0.630200 1.885596 -0.292126
5 1.651636 -0.611053 0.000000
6 -0.320220 0.199775 0.000000
[12]:
f = lambda x: x.max() - x.min()

df12.apply(f)
[12]:
0    3.166811
1    3.996958
2    3.585921
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(f, axis="columns")
[13]:
0    0.863544
1    4.560105
2    2.666453
3    3.446189
4    2.177721
5    2.262689
6    0.519995
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 -1.515175 -0.952028 -1.091759
max 1.651636 3.044930 2.494162

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]:
f = lambda x: round(x, 2)

df12.applymap(f)
[15]:
0 1 2
0 -0.23 -0.92 -1.09
1 -1.52 3.04 0.02
2 0.50 2.41 -0.25
3 -0.50 -0.95 2.49
4 0.63 1.89 -0.29
5 1.65 -0.61 0.00
6 -0.32 0.20 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(f)
[16]:
0   -1.09
1    0.02
2   -0.25
3    2.49
4   -0.29
5    0.00
6    0.00
Name: 2, dtype: float64