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.245242
1 -3.844435
2 -0.467513
3 2.430268
4 1.948743
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.254390 | -0.082757 | NaN |
1 | 1.030985 | 2.309641 | NaN |
2 | 0.277460 | -0.577714 | NaN |
3 | -0.923041 | 0.047021 | NaN |
4 | 0.255349 | -1.191603 | 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.254390 | -0.082757 | 0.776434 |
1 | 1.030985 | 2.309641 | 1.700989 |
2 | 0.277460 | -0.577714 | -0.950954 |
3 | -0.923041 | 0.047021 | -0.360664 |
4 | 0.255349 | -1.191603 | -0.711503 |
5 | 0.060263 | 0.393012 | NaN |
6 | 0.006922 | 1.300511 | 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.254390 | -0.082757 | 0.776434 |
1 | 1.030985 | 2.309641 | 1.700989 |
2 | 0.277460 | -0.577714 | -0.950954 |
3 | -0.923041 | 0.047021 | -0.360664 |
4 | 0.255349 | -1.191603 | -0.711503 |
5 | 0.060263 | 0.393012 | 0.000000 |
6 | 0.006922 | 1.300511 | 0.000000 |
Arithmetische Methoden#
Methode |
Beschreibung |
---|---|
|
Methoden für Addition ( |
|
Methoden für die Subtraktion ( |
|
Methoden für die Division ( |
|
Methoden für die Abrundungsfunktion (engl.: floor divison) ( |
|
Methoden für die Multiplikation ( |
|
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.884340 | -2.598045 | 0.216800 | NaN | NaN |
1 | 0.401035 | -0.205646 | 1.141356 | NaN | NaN |
2 | -0.352489 | -3.093002 | -1.510588 | NaN | NaN |
3 | -1.552991 | -2.468266 | -0.920298 | NaN | NaN |
4 | -0.374600 | -3.706890 | -1.271137 | NaN | NaN |
5 | -0.569687 | -2.122275 | -0.559633 | NaN | NaN |
6 | -0.623028 | -1.214776 | -0.559633 | 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 | 0.130318 | 0.301951 | 1.161141 |
1 | -0.298163 | 0.980494 | 0.371842 |
2 | 0.369581 | -0.485593 | -0.858833 |
3 | 0.643568 | 1.613630 | 1.205945 |
4 | 2.346520 | 0.899568 | 1.379667 |
5 | 0.006770 | 0.339519 | -0.053493 |
6 | 0.679892 | 1.973482 | 0.672971 |
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.254390 | 0.082757 | 0.776434 |
1 | 1.030985 | 2.309641 | 1.700989 |
2 | 0.277460 | 0.577714 | 0.950954 |
3 | 0.923041 | 0.047021 | 0.360664 |
4 | 0.255349 | 1.191603 | 0.711503 |
5 | 0.060263 | 0.393012 | 0.000000 |
6 | 0.006922 | 1.300511 | 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.254390 | -0.082757 | 0.776434 |
1 | 1.030985 | 2.309641 | 1.700989 |
2 | 0.277460 | -0.577714 | -0.950954 |
3 | -0.923041 | 0.047021 | -0.360664 |
4 | 0.255349 | -1.191603 | -0.711503 |
5 | 0.060263 | 0.393012 | 0.000000 |
6 | 0.006922 | 1.300511 | 0.000000 |
[12]:
f = lambda x: x.max() - x.min()
df12.apply(f)
[12]:
0 1.954026
1 3.501244
2 2.651944
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 1.030823
1 1.278656
2 1.228415
3 0.970062
4 1.446952
5 0.393012
6 1.300511
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.923041 | -1.191603 | -0.950954 |
max | 1.030985 | 2.309641 | 1.700989 |
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.25 | -0.08 | 0.78 |
1 | 1.03 | 2.31 | 1.70 |
2 | 0.28 | -0.58 | -0.95 |
3 | -0.92 | 0.05 | -0.36 |
4 | 0.26 | -1.19 | -0.71 |
5 | 0.06 | 0.39 | 0.00 |
6 | 0.01 | 1.30 | 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 0.78
1 1.70
2 -0.95
3 -0.36
4 -0.71
5 0.00
6 0.00
Name: 2, dtype: float64