dtype konvertieren

Manchmal passen die pandas-Datentypen nicht wirklich gut. Dies kann z.B. auf Serialisierungsformate zurückzuführen sein, die keine Typinformationen enthalten. Manchmal solltet ihr jedoch den Typ auch ändern, um eine bessere Performance zu erzielen – entweder mehr Manipulationsmöglichkeiten oder weniger Speicherbedarf. In den folgenden Beispielen werden wir verschiedene Konvertierungen einer Series vornehmen:

[1]:
import numpy as np
import pandas as pd
[2]:
s = pd.Series(np.random.randn(7))
[3]:
s
[3]:
0   -0.540728
1   -1.803588
2    2.709375
3    0.020922
4   -1.233492
5    1.809115
6   -1.310579
dtype: float64

Automatische Konvertierung

pandas.Series.convert_dtypes versucht, eine Series in einen Typ zu konvertieren, der NA unterstützt. Im Fall unserer Series wird der Typ von float64 in Float64 geändert:

[4]:
s.convert_dtypes()
[4]:
0   -0.540728
1   -1.803588
2    2.709375
3    0.020922
4   -1.233492
5    1.809115
6   -1.310579
dtype: Float64

Bedauerlicherweise habe ich jedoch mit convert_dtypes kaum Kontrolle darüber, in welchen Datentyp konvertiert wird. Daher bevorzuge ich pandas.Series.astype:

[5]:
s.astype("Float32")
[5]:
0   -0.540728
1   -1.803589
2    2.709375
3    0.020922
4   -1.233492
5    1.809115
6   -1.310579
dtype: Float32

Die Verwendung des richtigen Typs kann Speicherplatz einsparen. Üblich ist ein 8 Byte breiter Datentyp, also int64 oder float64. Wenn ihr einen schmaleren Typ verwenden könnt, reduziert dies den Speicherverbrauch deutlich, sodass ihr mehr Daten verarbeiten könnt. Ihr könnt NumPy verwenden, um die Grenzen von Integer- und Float-Typen zu überprüfen:

[6]:
np.iinfo("int64")
[6]:
iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)
[7]:
np.finfo("float32")
[7]:
finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32)
[8]:
np.finfo('float64')
[8]:
finfo(resolution=1e-15, min=-1.7976931348623157e+308, max=1.7976931348623157e+308, dtype=float64)

Speicherverbrauch

Um den Speicherverbrauch der Series zu berechnen, könnt ihr pandas.Series.nbytes verwenden um den Speicher, der von den Daten verwendet wird, zu ermitteln. pandas.Series.memory_usage erfasst darüberhinaus auch den Indexspeicher und den Datentyp. Mit deep=True lässt sich auch der Speicherverbrauch auf Systemebene ermitteln.

[9]:
s.nbytes
[9]:
56
[10]:
s.astype("Float32").nbytes
[10]:
35
[11]:
s.memory_usage()
[11]:
188
[12]:
s.astype("Float32").memory_usage()
[12]:
167
[13]:
s.memory_usage(deep=True)
[13]:
188

String- und Kategorietypen

Die Methode pandas.Series.astype kann auch numerische Reihen in Zeichenketten umwandeln, wenn ihr str übergebt. Beachtet den dtype im folgenden Beispiel:

[14]:
s.astype(str)
[14]:
0     -0.5407280521417943
1     -1.8035884918817433
2       2.709375360462432
3    0.020922480171874997
4     -1.2334923058043816
5       1.809115347105466
6     -1.3105793583289318
dtype: object
[15]:
s.astype(str).memory_usage()
[15]:
188
[16]:
s.astype(str).memory_usage(deep=True)
[16]:
661

Zur Konvertierung in einen kategorialen Typ könnt ihr 'category' als Typ übergeben:

[17]:
s.astype(str).astype("category")
[17]:
0     -0.5407280521417943
1     -1.8035884918817433
2       2.709375360462432
3    0.020922480171874997
4     -1.2334923058043816
5       1.809115347105466
6     -1.3105793583289318
dtype: category
Categories (7, object): ['-0.5407280521417943', '-1.2334923058043816', '-1.3105793583289318', '-1.8035884918817433', '0.020922480171874997', '1.809115347105466', '2.709375360462432']

Eine kategoriale Series ist nützlich für String-Daten und kann zu großen Speichereinsparungen führen. Das liegt daran, dass pandas bei der Konvertierung in kategoriale Daten nicht länger Python-Strings für jeden Wert verwendet, sondern sich wiederholende Werte nicht dupliziert werden. Ihr habt immer noch alle Funktionen des str-Attributs, aber ihr spart viel Speicherplatz wenn ihr viele doppelte Werte habt und steigert die Leistung, da ihr nicht so viele String-Operationen durchführen müsst.

[18]:
s.astype("category").memory_usage(deep=True)
[18]:
495

Geordnete Kategorien

Um geordnete Kategorien zu erstellen, müsst ihr einen eigenen pandas.CategoricalDtype definieren:

[19]:
from pandas.api.types import CategoricalDtype


sorted = pd.Series(sorted(set(s)))
cat_dtype = CategoricalDtype(categories=sorted, ordered=True)

s.astype(cat_dtype)
[19]:
0   -0.540728
1   -1.803588
2    2.709375
3    0.020922
4   -1.233492
5    1.809115
6   -1.310579
dtype: category
Categories (7, float64): [-1.803588 < -1.310579 < -1.233492 < -0.540728 < 0.020922 < 1.809115 < 2.709375]
[20]:
s.astype(cat_dtype).memory_usage(deep=True)
[20]:
495

In der folgenden Tabelle sind die Typen aufgeführt, die ihr an astype übergeben könnt.

Datentyp

Beschreibung

str, 'str'

in Python-String konvertieren

'string'

in Pandas-String konvertieren mit pandas.NA

int, 'int', 'int64'

in NumPy int64 konvertieren

'int32', 'uint32'

in NumPy int32 konvertieren

'Int64'

in pandas Int64 konvertieren mit pandas.NA

float, 'float', 'float64'

in Floats konvertieren

'category'

in CategoricalDtype konvertieren mit pandas.NA

Umwandlung in andere Datentypen

Die Methode pandas.Series.to_numpy oder die Eigenschaft pandas.Series.values liefert uns ein NumPy-Array mit Werten, und pandas.Series.to_list gibt eine Python-Liste mit Werten zurück. Warum solltet ihr das wollen? pandas-Objekte sind meist viel benutzerfreundlicher und der Code lässt sich leichter lesen. Zudem werden Python-Listen sehr viel langsamer verarbeitet werden können. Mit pandas.Series.to_frame könnt ihr ggf. einen DataFrame mit einer einzigen Spalte erzeugen:

[21]:
s.to_frame()
[21]:
0
0 -0.540728
1 -1.803588
2 2.709375
3 0.020922
4 -1.233492
5 1.809115
6 -1.310579

Auch die Funktion pandas.to_datetime kann hilfreich sein um in pandas um Werte in Datum und Uhrzeit zu konvertieren.