Datumsinformationen verarbeiten mit datetime und Timestamp

Dieser Beitrag befasst sich mit dem Thema Datumsvariablen und den in Python implementierten Klassen für deren Bearbeitung. Mit den Bibliotheken datetime und pandas stehe 2 zentrale Pakete/Klassen zur Verfügung, über die Kalenderinformationen bearbeitet bzw. extrahiert werden können. Was die elementaren Anwendungen für Datumsvariablen sind, wie sich diese in Python umsetzen lassen und welche Vor- und Nachteile die unterschiedlichen Klassen haben, erfahren Sie hier.

Inhalt

Elementare Arbeiten an Datums- und Zeitinformationen

Haben wir chronologisch sortierbare Daten vorliegen, handelt es sich dabei grundsätzlich um eine Zeitreihe. Ist die Zeitreihe ein Datum oder eine Uhrzeit, lassen sich 2 zentrale Anwendungen damit durchführen:

  1. Kalenderinformationen wie Jahr, Monat oder Wochentag können extrahiert werden (beispielsweise um sie als weitere Features in ein Machine Learning Model eingehen zu lassen)
  2. Es können Berechnungen vorgenommen werden (Beispielsweise um Zeitdifferenzen zu ermitteln)

Anmerkungen:

  • Wenn im Folgenden von einer Zeitreihe die Rede ist, gehen wir davon aus, dass es sich (zumindest) um ein Datum handelt.
  • Lassen Sie sich im Fleißtext nicht von der Schreibweise der Klassenbezeichner irritieren. Der Klassenbezeichner datetime wird kleingeschrieben, Timestamp hingegen, der Bezeichner aus pandas, wird groß geschrieben.

In Python existieren im 2 zentrale Klassen, die für den Umgang mit Zeitreihen herangezogen werden. Dies ist zum einen die Klasse datetime aus dem gleichnamigen Paket datetime und Timestamp aus dem Paket pandas. Um Missverständnissen vorzubeugen: Beide Klassen sind grundsätzlich sehr ähnlich und verfügen über eine große Schnittmenge gleicher Methoden und Anwendungen. Welche Klasse zu bevorzugen ist, hängt im Wesentlichen davon ab, wie die Datenhaltung der Anwendung erfolgt. Werden relationale Daten in Tabellenform verarbeitet? Dann empfiehlt sich der Einsatz von pandas und damit die Klasse Timestamp.

Zu den elementarsten Aufgaben im Umgang mit Zeitreihen gehört das Umwandeln eines Strings in ein Datumsformat (wie eben datetime und Timestamp). Werden Daten importiert, ganz gleich aus welchem Datenbanksystem oder Dateiformat, liegen die Daten (in nahezu allen Fällen) zunächst als String vor. Umgekehrt besteht häufig die Aufgabe das Datumsformat für den Export oder für ein Reporting in einen String umzuwandeln. Auch wenn Datenbanksysteme in der Regel ISO-Formate unterstützen bzw. erwarten – der Export von Datumsinformationen in eine Datenbank erfolgt in der Regel als String (und weist häufig so seine Tücken auf).

Schauen wir uns im Folgenden zunächst die Klasse datetime an. Diese verfügt für die beiden oben genannten Aufgaben 2 zentrale Methoden:

  1. strptime –> Wandelt String in ein Datetimeobjekt um
  2. strftime –> Wandelt Datetimeobjekt in einen String um

Schauen wir uns deren Funktionsweise direkt im Code an. Beginnen wir mit strptime (Anmerkung: Wir können den Funktionsnamen übersetzen in ‚String-Parse-Time‘ – lese einen String und erstelle ein Datumsformat). Erstellen wir uns dazu zunächst einige Strings mit Datumsangaben (nehmen wir an, diese haben wir uns aus unterschiedlichen Datenbanken importiert).

the_time_1 = "2019-12-24 18:00:00"
the_time_2 = "24/Dec/2019 18-00-00"
the_time_3 = "19 12 24"

the_time_1 = datetime.datetime.strptime(the_time_1, '%Y-%m-%d %H:%M:%S')
the_time_2 = datetime.datetime.strptime(the_time_2, '%d/%b/%Y %H-%M-%S')
the_time_3 = datetime.datetime.strptime(the_time_3, '%y %m %d')

print(the_time_1)
# >>> 2019-12-24 18:00:00

print(the_time_1)
# >>> <class 'datetime.datetime'>

Wie wir am Code sehen können, handelt es sich bei strptime um einen Klassenmethode – d.h. sie ist über die Klasse selbst abrufbar, und benötigt keine daher Instanz, aus welcher sie aufgerufen wird. Die Methode verfüt über 2 Argumente: Zum einen den String, welcher in ein datetime-Objekt umgewandelt werden soll, sowie das sog. Format, in dem sich der String befindet. Mit dem Format wird eine allg. Beschreibung der im String vorhandenen Datumsinformationen verlangt. Für die Beschreibung werden sog. Codes (oder auch Tags genannt) verwendet, die mit einem %-Zeichen beginnen. Der Code %Y bedeutet bspw. Jahresangabe mit 4 Ziffern. Eine Übersicht der Codes findet sich auf: https://docs.python.org/3/library/datetime.html#datetime.datetime.strptime

Wenden wir uns nun dem anderen Szenario zu: Wie wollen aus einem datetime-Objekt einen String erstellen (um bspw. einen Datenbankexport zu erledigen). Dazu erstellen wir uns zunächst eine datetime-Objekt. Wie wir unten sehen, kann dies mit der Funktion datetime geschehen – die Argumente sind selbsterklärend. Für einen aktuellen Zeitstempel können wir die Funktion now verwenden. Der Folgende Codeblock zeigt die Verwendung von strftime. Wie wir sehen, handelt es sich bei strftime um eine Instanzmethode. Sie benötigt lediglich die Angabe über das gewünschte Format des returnierenden Strings – mit dem Argument format geben wir also an, wie das Datum bzw. die Uhrzeit als String repräsentiert sein soll.

# --- Erstellen eines datetime-Objekts
the_time = datetime.datetime(year=2019, month=10, day=23, hour=6, minute=40, second=15)
the_time = datetime.datetime.now()

print(the_time)
# >>> 2019-10-23 06:41:30.658683

print(datetime.datetime)
# >>> <class 'datetime.datetime'>

# --- Umwandeln eines datetime-Objekts in einen String. Formatieren des resultierenden Strings
the_time.strftime(format = "%d-%m-%Y") 
the_time.strftime(format = "%Y-%m-%d %H:%M:%S")
the_time.strftime(format = "%d %b %y")

Anfügen von Zeitzonen

Soll die Information der Zeitzone an das datetime-Objekt angefügt werden, benötigt dies die Bibliothek pytz. Zeitzonen werden in 2 Schritten an das datetime-Objekt gehangen:

  1. Erstellen eines timezone-Objekts mit gewünschter Zeitzone
  2. Anwenden der Instanzmethode localize auf das datetime-Objekts
# --- Erstellen eine datetime-Objekts
the_time = datetime.datetime.now()

pytz.all_timezones[0:5] # Liste von Zeitzonen die als String in die Funktion timezone übergeben werden können
# >>> ['Africa/Abidjan',
#      'Africa/Accra',
#      'Africa/Addis_Ababa',
#      'Africa/Algiers',
#      'Africa/Asmara']

# --- Schritt 1: Erstellen eines Zeitzonenobjekts
timezone = pytz.timezone("Europe/Berlin")

# --- Schritt 2: Anwenden der Zeitzone auf das Datetime-Objekt
the_time = timezone.localize(the_time)

print(the_time)
# >>> datetime.datetime(2019, 10, 23, 9, 7, 7, 776044, tzinfo=<DstTzInfo 'Europe/Berlin' CEST+2:00:00 DST>)

Kalenderinformationen extrahieren

Liegt ein datetime-Objekt vor, kann man sich nun an diesem bedienen, sprich: Kalenderinformationen können daraus extrahiert werden. Diese liegen (zumeist) als Attribut am Objekt und können wie im Codeblock dargestellt, abgerufen werden. Um den Wochentag eines datetime-Objekts zu ermitteln muss hingegen gerechnet werden. Hierfür existiert die Methode weekday.

# --- Erstellen eine datetime-Objekts
the_time = datetime.datetime.now()

the_time.year
the_time.month
the_time.day
the_time.hour
the_time.minute
the_time.second

the_time.weekday()

Mit Datum und Zeit rechnen

Eine häufige Verwendung von Datums- bzw. Zeitobjekten ist es, mit diesem zu rechnen – d.h. Zeitdifferenzen zu ermitteln. Wir könnten uns an dieser Stelle bspw. fragen: Wie lange dauert es noch, bis endlich Weihnachten ist? Führen Sie den unteren Codeblock aus, und Sie erhalten die Antwort (es steht Ihnen natürlich frei, den Code ihren Kindern zur Verfügung zu stellen – so können diese sich die Frage zukünftig selbst beantworten). Python antwortet auf die Frage mit einem Objekt der Klasse timedelta. Mit diesem können Sie sogar auf die Sekunde genau ermitteln, wie lange noch zu warten ist – oh du Fröhliche!

# --- Berechnungen mit Datetime
weihnachten = datetime.datetime(year=2019,
                                month=12,
                                day=24,
                                minute=0,
                                second=0)

diff = weihnachten - datetime.datetime.now()

print(diff)
# >>> 61 days, 14:25:17.817276

print(diff.__class__) 
# >>> <class 'datetime.timedelta'>

print(diff.days)
# >>> 61 

print(diff.seconds)
# >>> 51274

Timestamp aus pandas

Wie bereits Einleitend erwähnt, sind sich die Klassen datetime und Timestamp sehr ähnlich. Dennoch kann Timestamp als etwas komfortablere Klasse im Umgang mit Zeitreihen verstanden werden. Bspw. können Zeitzonen „einfacher“ an einen Timestamp angehangen werden, als dies bei datetime der Fall war, oder es können weitere Kalenderinformationen aus dem Objekt extrahiert werden, wie etwa der Tag im Jahr und die Woche im Jahr.

Wie wir am Codeblock weiter unten sehen, unterscheiden sich datetime und Timestamp hinsichtlich ihrer Methoden und deren Bezeichner nicht (sofern die Funktionalität in beiden Klassen implementiert ist). Alle Anwendungen die weiter oben für datetime-Objekte beschrieben wurden, finden sich im unteren Codeblock zusammengefasst.

# ----- timestamp
import pandas as pd

# --- Erstellen eines timestamps (hier kann die Zeitzone direkt angegeben werden)
the_time = pd.Timestamp(year=2018, month=11, day=28, hour=15, minute=30, second=15, tz="Europe/Berlin")
the_time = pd.Timestamp.now()

print(the_time)
print(the_time.__class__)

# --- Einschub: Wie wir hier sehen, kann aus einem datetime-Objekt ohne weiteres ein Timestamp erstellt werden
the_time = datetime.datetime.now()
the_time = pd.Timestamp(the_time)

# strptime --> Umwandeln von String in Timestamp
the_time = "24/Dec/2018 18:00:00"
the_time = pd.Timestamp.strptime(the_time, '%d/%b/%Y %H:%M:%S')

# strftime --> Umwandeln von Timestamp in String
the_time.strftime(format = "%d-%m-%Y")
the_time.strftime(format = "%Y-%m-%d %H:%M:%S")
the_time.strftime(format = "%d %b %y")

# Kalenderinformationen/Zeitzone extrahieren 
the_time.month
the_time.day
the_time.weekofyear
the_time.dayofyear
the_time.dayofweek
the_time.tzinfo 
the_time.day_name()
the_time.month_name()

# --- Berechnungen
now = pd.Timestamp.now()
weihnachten = pd.Timestamp(year=2018, month=12, day=24)

diff = weihnachten - now # timedelta
dir(diffBisWeihnachten)

Relationale Daten und Timestamp

Die Klasse Timestamp hat insbesondere Vorteile, wenn Zeitreihen in Tabellen gehalten werden – bspw. als Index oder als Spalte. Operationen wie das Extrahieren von Kalenderinformationen können dann vektorwertig erfolgen. In Folgenden Codeblock wird zunächst ein DataFrame mit einer Variable Date über die Utility-Funktion date_range erstellt. date_range erstellt eine Folge von Timestamp-Objekten. Das Ergebnis ist ein Objekt der Klasse DatetimeIndex, welches als Spalte in einen DataFrame eingefügt werden kann. Die Series ‚Date‘ verfügt über ein Attribut dt der Klasse DatetimeProperties. Über dieses Attribut kann vektorwertig auf der Zeitreihe gearbeitet werden. Im Code findet sich zur Illustration ein Vergleich von nicht-vektorwertiger und vektorwertiger Vorgehensweise.

import pandas as pd
import datetime
import random

myDateRange = pd.date_range('1/1/2019', periods=12, freq='d',closed="right")
df = pd.DataFrame({'Date': myDateRange,
              'Measurement': [random.random() for i in range(0,11)]})

# Nicht vektorwertiges Vorgehen
df['day_of_week'] = [item.dayofweek for index, item in df['Date'].iteritems()]
In [113]:
import pandas as pd
import datetime
import random

myDateRange = pd.date_range('1/1/2019', periods=12, freq='d',closed="right")
print(myDateRange.__class__)
# >>> <class 'pandas.core.indexes.datetimes.DatetimeIndex'>

df = pd.DataFrame({'Date': myDateRange,
              'Measurement': [random.random() for i in range(0,11)]})

# Nicht vektorwertiges Vorgehen
df['day_of_week'] = [item.dayofweek for index, item in df['Date'].iteritems()]

# Vektorwertiges Vorgehen
df['day_of_week'] = df['Date'].dt.dayofweek

print(df['Date'].dt.__class__)
# >>> <class 'pandas.core.indexes.accessors.DatetimeProperties'>

print(df)
<class 'pandas.core.indexes.datetimes.DatetimeIndex'>
<class 'pandas.core.indexes.accessors.DatetimeProperties'>
         Date  Measurement  day_of_week
0  2019-01-02     0.520440            2
1  2019-01-03     0.777314            3
2  2019-01-04     0.017218            4
3  2019-01-05     0.577009            5
4  2019-01-06     0.055419            6
5  2019-01-07     0.167268            0
6  2019-01-08     0.368003            1
7  2019-01-09     0.427284            2
8  2019-01-10     0.309788            3
9  2019-01-11     0.117242            4
10 2019-01-12     0.467119            5

Fazit

Der Umgang mit Datums- und Zeitinformationen erfolgt in Python über die Klassen datetime und Timestamp aus den Paketen datetime und pandas. Beide Klassen unterscheiden sich in ihrer Anwendung kaum voneinander. Timestamp erweitert jedoch die Funktionalität von datetime. Außerdem können Timestamps in DataFrames eingesetzt und vektorwertig darauf gearbeitet werden.